File: fedcm_account_selection_view_desktop_browsertest.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (481 lines) | stat: -rw-r--r-- 17,825 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h"

#include "base/test/scoped_feature_list.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_occlusion_tracker.h"
#include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/test/popup_test_base.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/browser/ui/views/webid/account_selection_view_test_base.h"
#include "chrome/browser/ui/views/webid/fake_delegate.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"

namespace webid {

// TODO(https://crbug.com/378939142): This is a misuse of DialogBrowserTest.
// DialogBrowserTest exists to test the visual properties of dialogs. Instead
// use FedCmBrowserTest below.
class FedCmAccountSelectionViewBrowserTest : public DialogBrowserTest {
 public:
  FedCmAccountSelectionViewBrowserTest() {
    feature_list_.InitAndEnableFeature(features::kFedCm);
  }

  void PreShow() override {
    delegate_ = std::make_unique<FakeDelegate>(
        browser()->tab_strip_model()->GetActiveWebContents());
    account_selection_view_ = std::make_unique<FedCmAccountSelectionView>(
        delegate(), browser()->GetActiveTabInterface());
  }

  void ShowUi(const std::string& name) override { ShowAccounts(); }

  void Initialize() {
    idps_ = {base::MakeRefCounted<content::IdentityProviderData>(
        "idp-example.com", content::IdentityProviderMetadata(),
        content::ClientMetadata(GURL(), GURL(), GURL(), gfx::Image()),
        blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
        kDefaultDisclosureFields,
        /*has_login_status_mismatch=*/false)};
    accounts_ = {base::MakeRefCounted<Account>(
        "id", "display_identifier", "display_name", "email", "name",
        "given_name", GURL(), "tel", "username",
        /*login_hints=*/std::vector<std::string>(),
        /*domain_hints=*/std::vector<std::string>(),
        /*labels=*/std::vector<std::string>())};
    accounts_[0]->identity_provider = idps_[0];
  }

  void ShowAccounts() {
    Initialize();
    account_selection_view()->Show(
        content::RelyingPartyData(u"rp-example.com",
                                  /*iframe_for_display=*/u""),
        idps_, accounts_, blink::mojom::RpMode::kPassive,
        /*new_accounts=*/std::vector<IdentityRequestAccountPtr>());
  }

  void ShowVerifyingDialog(Account::SignInMode sign_in_mode) {
    Initialize();
    account_selection_view()->ShowVerifyingDialog(
        content::RelyingPartyData(u"rp-example.com",
                                  /*iframe_for_display=*/u""),
        idps_[0], accounts_[0], sign_in_mode, blink::mojom::RpMode::kPassive);
  }

  void Show() {
    PreShow();
    ShowUi("");
  }

  views::Widget* GetDialog() {
    return account_selection_view_->GetDialogWidget();
  }

  FakeDelegate* delegate() { return delegate_.get(); }

  FedCmAccountSelectionView* account_selection_view() {
    return account_selection_view_.get();
  }

  void ResetAccountSelectionView() { account_selection_view_ = nullptr; }

 protected:
  base::test::ScopedFeatureList feature_list_;
  std::unique_ptr<FakeDelegate> delegate_;
  std::vector<IdentityProviderDataPtr> idps_;
  std::vector<IdentityRequestAccountPtr> accounts_;
  std::unique_ptr<FedCmAccountSelectionView> account_selection_view_;
};

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest, ShowAndVerifyUi) {
  ShowAndVerifyUi();
}

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest, CloseAllTabs) {
  Show();
  ASSERT_TRUE(GetDialog());
  EXPECT_TRUE(GetDialog()->IsVisible());

  browser()->tab_strip_model()->CloseAllTabs();

  // The dialog should be closed after the WebContents is Hidden.
  ASSERT_FALSE(GetDialog());
}

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest, NavigateAway) {
  Show();
  ASSERT_TRUE(GetDialog());
  EXPECT_TRUE(GetDialog()->IsVisible());
  // Navigate away to a real URL, otherwise it does not seem to work.
  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(browser(), GURL("https://www.google.com")));
  // The dialog should be closed after the browser navigates away from the page.
  EXPECT_FALSE(GetDialog());
}

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest, ReShow) {
  Show();
  EXPECT_TRUE(AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));

  // The tab is currently hidden.
  ASSERT_TRUE(GetDialog());
  EXPECT_FALSE(GetDialog()->IsVisible());

  browser()->tab_strip_model()->ActivateTabAt(0);

  // The dialog should be reshown after the WebContents is Visible.
  ASSERT_TRUE(GetDialog());
  EXPECT_TRUE(GetDialog()->IsVisible());
}

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest, ShowWhileHidden) {
  // Run preshow to ensure the dialog will be created in the initial tab.
  PreShow();
  // Add a new tab.
  EXPECT_TRUE(AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));
  ShowUi("");

  // Since Show() was called while hidden, the dialog should have been created,
  // but should not be visible.
  ASSERT_TRUE(GetDialog());
  EXPECT_FALSE(GetDialog()->IsVisible());

  browser()->tab_strip_model()->ActivateTabAt(0);
  ASSERT_TRUE(GetDialog());
  EXPECT_TRUE(GetDialog()->IsVisible());
}

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest,
                       ShowWhileCannotFitInWebContents) {
  browser()->tab_strip_model()->GetActiveWebContents()->Resize(
      gfx::Rect(/*x=*/0, /*y=*/0, /*width=*/10, /*height=*/10));

  Show();
  // Since Show() was called while the web contents is too small, the dialog
  // should have been created, but should not be visible.
  ASSERT_TRUE(GetDialog());
  EXPECT_FALSE(GetDialog()->IsVisible());
}

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest,
                       ModalDialogThenShowThenCloseModalDialog) {
  PreShow();
  delegate_->SetAccountSelectedCallback(base::BindOnce(
      &FedCmAccountSelectionViewBrowserTest::ResetAccountSelectionView,
      base::Unretained(this)));
  account_selection_view_->ShowModalDialog(GURL("https://rp-example.com"),
                                           blink::mojom::RpMode::kPassive);
  // Because a modal dialog is up, this should save the accounts for later.
  ShowVerifyingDialog(Account::SignInMode::kAuto);
  // This should trigger auto re-authn without crashing or UAF.
  account_selection_view_->CloseModalDialog();
  EXPECT_EQ(account_selection_view_->state_,
            FedCmAccountSelectionView::State::AUTO_REAUTHN);
}

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest, DetachAndDelete) {
  Show();
  browser()->tab_strip_model()->DetachAndDeleteWebContentsAt(0);
  EXPECT_FALSE(GetDialog());
}

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest,
                       DetachForInsertion) {
  Show();
  browser()->tab_strip_model()->DetachAndDeleteWebContentsAt(0);
  // TODO(npm): it would be better if the dialog actually moves with the
  // corresponding tab, instead of being altogether deleted.
  EXPECT_FALSE(GetDialog());
}

// Tests crash scenario from crbug.com/1473691.
IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest, ClosedBrowser) {
  PreShow();
  browser()->window()->Close();
  ui_test_utils::WaitForBrowserToClose(browser());

  // Invoking this after browser is closed should not cause a crash.
  ShowUi("");
  EXPECT_FALSE(GetDialog());
}

// Tests that adding a new tab hides the FedCM UI, and closing tabs until the
// original tab is shown causes the UI to be reshown.
IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest, AddTabHidesUI) {
  Show();
  ASSERT_TRUE(GetDialog());
  EXPECT_TRUE(GetDialog()->IsVisible());

  ASSERT_TRUE(AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));

  // The dialog should be hidden since the new tab is appended foregrounded.
  ASSERT_TRUE(GetDialog());
  EXPECT_FALSE(GetDialog()->IsVisible());

  ASSERT_TRUE(AddTabAtIndex(2, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));
  ASSERT_TRUE(GetDialog());
  EXPECT_FALSE(GetDialog()->IsVisible());

  browser()->tab_strip_model()->CloseWebContentsAt(
      2, TabCloseTypes::CLOSE_USER_GESTURE);
  ASSERT_TRUE(GetDialog());
  EXPECT_FALSE(GetDialog()->IsVisible());

  browser()->tab_strip_model()->CloseWebContentsAt(
      1, TabCloseTypes::CLOSE_USER_GESTURE);

  // FedCM UI becomes visible again.
  ASSERT_TRUE(GetDialog());
  EXPECT_TRUE(GetDialog()->IsVisible());
}

// Tests that detaching a tab with FedCM UI does not trigger a crash.
IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest,
                       DetachTabForInsertion) {
  Show();
  ASSERT_TRUE(GetDialog());
  EXPECT_TRUE(GetDialog()->IsVisible());

  // Add a new tab and detach the FedCM tab without closing it.
  ASSERT_TRUE(AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED));
  std::unique_ptr<tabs::TabModel> tab =
      browser()->tab_strip_model()->DetachTabAtForInsertion(0);

  ASSERT_FALSE(GetDialog());

  // Add the the FedCM tab back in to the tabstrip to complete the transfer so
  // we can tear down cleanly.
  browser()->tab_strip_model()->AppendTab(std::move(tab), true);
}

// Test that the dialog is disabled when occluded by a PiP window.
IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewBrowserTest,
                       DisabledWhenOccluded) {
  Show();
  ASSERT_TRUE(GetDialog());
  EXPECT_TRUE(GetDialog()->IsVisible());

  views::View* dialog_view = GetDialog()->GetContentsView();
  ASSERT_NE(dialog_view, nullptr);

  // Create a picture-in-picture widget that does not occlude the prompt.
  gfx::Rect prompt_widget_bounds =
      dialog_view->GetWidget()->GetWindowBoundsInScreen();
  gfx::Rect non_occluding_bounds =
      gfx::Rect(prompt_widget_bounds.right() + 1, 0, 100, 100);
  views::Widget::InitParams init_params(
      views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET,
      views::Widget::InitParams::TYPE_WINDOW);
  init_params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  init_params.bounds = non_occluding_bounds;
  auto pip_widget = std::make_unique<views::Widget>(std::move(init_params));
  pip_widget->Show();
  PictureInPictureWindowManager::GetInstance()
      ->GetOcclusionTracker()
      ->OnPictureInPictureWidgetOpened(pip_widget.get());

  // The prompt should be enabled, as it's not occluded.
  EXPECT_TRUE(dialog_view->GetEnabled());

  // Move the picture-in-picture window to occlude the prompt.
  pip_widget->SetBounds(prompt_widget_bounds);

  // The prompt should be disabled. We may need to wait for that to happen.
  if (dialog_view->GetEnabled()) {
    base::RunLoop wait_loop;
    auto subscription =
        dialog_view->AddEnabledChangedCallback(wait_loop.QuitClosure());
    wait_loop.Run();
  }
  EXPECT_FALSE(dialog_view->GetEnabled());

  // Move the picture-in-picture window to no longer occlude the prompt.
  pip_widget->SetBounds(non_occluding_bounds);

  // The prompt should be enabled again. We may need to wait for that to happen.
  if (!dialog_view->GetEnabled()) {
    base::RunLoop wait_loop;
    auto subscription =
        dialog_view->AddEnabledChangedCallback(wait_loop.QuitClosure());
    wait_loop.Run();
  }
  EXPECT_TRUE(dialog_view->GetEnabled());
}

// Helper methods shared by FedCmBrowserTest and
// FedCmAccountSelectionViewPopupTest.
class FedCmMixin {
 public:
  // In a bubble view.
  void ShowAccounts(Browser* browser) {
    delegate_ = std::make_unique<FakeDelegate>(
        browser->GetActiveTabInterface()->GetContents());
    account_selection_view_ = std::make_unique<FedCmAccountSelectionView>(
        delegate_.get(), browser->GetActiveTabInterface());

    idps_ = {base::MakeRefCounted<content::IdentityProviderData>(
        "idp-example.com", content::IdentityProviderMetadata(),
        content::ClientMetadata(GURL(), GURL(), GURL(), gfx::Image()),
        blink::mojom::RpContext::kSignIn, /*format=*/std::nullopt,
        kDefaultDisclosureFields,
        /*has_login_status_mismatch=*/false)};
    accounts_ = {base::MakeRefCounted<Account>(
        "id", "display_identifier", "display_name", "email", "name",
        "given_name", GURL(), "phone", "username",
        /*login_hints=*/std::vector<std::string>(),
        /*domain_hints=*/std::vector<std::string>(),
        /*labels=*/std::vector<std::string>())};
    accounts_[0]->identity_provider = idps_[0];
    account_selection_view_->Show(
        content::RelyingPartyData(u"rp-example.com",
                                  /*iframe_for_display=*/u""),
        idps_, accounts_, blink::mojom::RpMode::kPassive,
        /*new_accounts=*/std::vector<IdentityRequestAccountPtr>());
  }

  void Reset() {
    account_selection_view_.reset();
    delegate_.reset();
  }

  std::unique_ptr<FakeDelegate> delegate_;
  std::vector<IdentityProviderDataPtr> idps_;
  std::vector<IdentityRequestAccountPtr> accounts_;
  std::unique_ptr<FedCmAccountSelectionView> account_selection_view_;
};

// These are normal browser tests. Add more of these.
class FedCmBrowserTest : public InProcessBrowserTest, public FedCmMixin {
 public:
  void ShowModalLoadingDialog() {
    delegate_ = std::make_unique<FakeDelegate>(
        browser()->GetActiveTabInterface()->GetContents());
    account_selection_view_ = std::make_unique<FedCmAccountSelectionView>(
        delegate_.get(), browser()->GetActiveTabInterface());
    account_selection_view_->ShowLoadingDialog(
        content::RelyingPartyData(u"rp-example.com",
                                  /*iframe_for_display=*/u""),
        "idp_etld_plus_one.com", blink::mojom::RpContext::kSignIn,
        blink::mojom::RpMode::kActive);
  }
};

IN_PROC_BROWSER_TEST_F(FedCmBrowserTest, InputDisabledForModalDialog) {
  // Check that input is enabled by default.
  EXPECT_FALSE(browser()
                   ->GetActiveTabInterface()
                   ->GetContents()
                   ->ShouldIgnoreInputEventsForTesting());

  // Show a modal dialog.
  ShowModalLoadingDialog();

  // Now input should be disabled.
  EXPECT_TRUE(browser()
                  ->GetActiveTabInterface()
                  ->GetContents()
                  ->ShouldIgnoreInputEventsForTesting());

  // If we make a new tab input should be enabled.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
  EXPECT_FALSE(browser()
                   ->GetActiveTabInterface()
                   ->GetContents()
                   ->ShouldIgnoreInputEventsForTesting());

  // If we switch to original tab input should be disabled.
  browser()->tab_strip_model()->ActivateTabAt(/*index=*/0);
  EXPECT_TRUE(browser()
                  ->GetActiveTabInterface()
                  ->GetContents()
                  ->ShouldIgnoreInputEventsForTesting());

  // If we close the dialog input should be enabled.
  account_selection_view_.reset();
  EXPECT_FALSE(browser()
                   ->GetActiveTabInterface()
                   ->GetContents()
                   ->ShouldIgnoreInputEventsForTesting());
}

IN_PROC_BROWSER_TEST_F(FedCmBrowserTest, InputEnabledForBubbleDialog) {
  // Check that input is enabled by default.
  EXPECT_FALSE(browser()
                   ->GetActiveTabInterface()
                   ->GetContents()
                   ->ShouldIgnoreInputEventsForTesting());

  // Show a bubble dialog.
  ShowAccounts(browser());

  // Check that input is still enabled.
  EXPECT_FALSE(browser()
                   ->GetActiveTabInterface()
                   ->GetContents()
                   ->ShouldIgnoreInputEventsForTesting());
}

class FedCmAccountSelectionViewPopupTest : public PopupTestBase,
                                           public FedCmMixin {};

IN_PROC_BROWSER_TEST_F(FedCmAccountSelectionViewPopupTest,
                       CanFitInWebContents) {
  // Normal size popup should work fine.
  {
    Browser* popup = OpenPopup(
        browser(), "open('.', '', 'left=0,top=0,width=1000,height=1000')");
    ShowAccounts(popup);
    EXPECT_TRUE(account_selection_view_->CanFitInWebContents());

    auto* bubble = static_cast<AccountSelectionBubbleView*>(
        account_selection_view_->account_selection_view());
    EXPECT_TRUE(
        popup->GetActiveTabInterface()->GetContents()->GetViewBounds().Contains(
            bubble->GetBubbleBounds()));
    Reset();
  }

  // Too small vertically. Popups have a minimum vertical height so the actual
  // height will be larger, but that's still too small.
  {
    Browser* popup = OpenPopup(
        browser(), "open('.', '', 'left=0,top=0,width=1000,height=10')");
    ShowAccounts(popup);
    EXPECT_FALSE(account_selection_view_->CanFitInWebContents());
    Reset();
  }

  // Too small horizontally.
  {
    Browser* popup = OpenPopup(
        browser(), "open('.', '', 'left=0,top=0,width=10,height=1000')");
    ShowAccounts(popup);
    EXPECT_FALSE(account_selection_view_->CanFitInWebContents());
    Reset();
  }

  // Too small in both directions.
  {
    Browser* popup = OpenPopup(
        browser(), "open('.', '', 'left=0,top=0,width=10,height=10')");
    ShowAccounts(popup);
    EXPECT_FALSE(account_selection_view_->CanFitInWebContents());
    Reset();
  }
}

}  // namespace webid