File: extension_install_friction_dialog_view_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 (170 lines) | stat: -rw-r--r-- 6,193 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
// Copyright 2021 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/extensions/extension_install_friction_dialog_view.h"

#include <string>

#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/extensions_dialogs.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "ui/views/test/widget_test.h"

namespace {

void CloseAndWait(views::Widget* widget) {
  views::test::WidgetDestroyedWaiter waiter(widget);
  widget->Close();
  waiter.Wait();
}

}  // namespace

// Helper class to display the ExtensionInstallFrictionDialogView dialog for
// testing.
class ExtensionInstallFrictionDialogTest : public DialogBrowserTest {
 public:
  ExtensionInstallFrictionDialogTest() = default;

  void ShowUi(const std::string& name) override {
    extensions::ShowExtensionInstallFrictionDialog(
        browser()->tab_strip_model()->GetActiveWebContents(),
        base::DoNothing());
  }
};

IN_PROC_BROWSER_TEST_F(ExtensionInstallFrictionDialogTest, InvokeUi_default) {
  ShowAndVerifyUi();
}

class ExtensionInstallFrictionDialogViewTest
    : public extensions::ExtensionBrowserTest {
 public:
  ExtensionInstallFrictionDialogViewTest() = default;
  ExtensionInstallFrictionDialogViewTest(
      const ExtensionInstallFrictionDialogViewTest&) = delete;
  ExtensionInstallFrictionDialogViewTest& operator=(
      const ExtensionInstallFrictionDialogViewTest&) = delete;

  void SetUpOnMainThread() override {
    extensions::ExtensionBrowserTest::SetUpOnMainThread();

    // Note: Any extension will do.
    extension_ = LoadExtension(test_data_dir_.AppendASCII("install/install"));
    web_contents_ = browser()->tab_strip_model()->GetWebContentsAt(0);
  }

  ExtensionInstallFrictionDialogView* CreateAndShowFrictionDialogView() {
    auto dialog = std::make_unique<ExtensionInstallFrictionDialogView>(
        web_contents(), base::BindOnce([](bool) {}));
    ExtensionInstallFrictionDialogView* delegate_view = dialog.get();

    views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget(
        dialog.release(), gfx::NativeWindow(),
        platform_util::GetViewForWindow(
            browser()->window()->GetNativeWindow()));
    modal_dialog->Show();

    return delegate_view;
  }

 protected:
  content::WebContents* web_contents() { return web_contents_; }

 private:
  raw_ptr<const extensions::Extension, DanglingUntriaged> extension_ = nullptr;
  raw_ptr<content::WebContents, DanglingUntriaged> web_contents_ = nullptr;
};

// Regression test for https://crbug.com/1201031: Ensures that while an
// ExtensionInstallFrictionDialogView is visible, it does not (and cannot) refer
// to its originator tab/WebContents after the tab's closure.
IN_PROC_BROWSER_TEST_F(ExtensionInstallFrictionDialogViewTest,
                       TabClosureClearsWebContentsFromDialogView) {
  ExtensionInstallFrictionDialogView* delegate_view =
      CreateAndShowFrictionDialogView();
  TabStripModel* tab_strip_model = browser()->tab_strip_model();
  content::WebContents* originator_contents =
      tab_strip_model->GetActiveWebContents();
  EXPECT_EQ(originator_contents, delegate_view->parent_web_contents());

  // Add a second tab.
  chrome::AddTabAt(browser(), GURL(url::kAboutBlankURL), -1, true);
  {
    // Close the first tab that results in install dialog moving to the second
    // tab.
    int tab1_idx = tab_strip_model->GetIndexOfWebContents(originator_contents);
    content::WebContentsDestroyedWatcher tab_destroyed_watcher(
        tab_strip_model->GetWebContentsAt(tab1_idx));
    int previous_tab_count = tab_strip_model->count();
    tab_strip_model->CloseWebContentsAt(tab1_idx, TabCloseTypes::CLOSE_NONE);
    EXPECT_EQ(previous_tab_count - 1, tab_strip_model->count());
    tab_destroyed_watcher.Wait();
  }

  // The dialog remains visible even though |originator_contents| is gone. Note
  // that this doesn't seem quite intuitive, but this is how things are at the
  // moment. See crbug.com/1201031 for details.
  EXPECT_TRUE(delegate_view->GetVisible());

  // After WebContents is destroyed, ensure |delegate_view| sees it as
  // nullptr.
  EXPECT_EQ(nullptr, delegate_view->parent_web_contents());

  // TODO(lazyboy): This is similar to TabAddedObserver in
  // extension_install_view_dialog_browsertest.cc, consider putting it in a
  // common place.
  class TabAddedObserver : public TabStripModelObserver {
   public:
    explicit TabAddedObserver(TabStripModel* tab_strip_model) {
      tab_strip_model->AddObserver(this);
    }

    void WaitForWebstoreTabAdded() { run_loop_.Run(); }

    // TabStripModelObserver:
    void OnTabStripModelChanged(
        TabStripModel* tab_strip_model,
        const TabStripModelChange& change,
        const TabStripSelectionChange& selection) override {
      if (change.type() != TabStripModelChange::kInserted) {
        return;
      }

      GURL learn_more_url(chrome::kCwsEnhancedSafeBrowsingLearnMoreURL);
      for (const auto& contents : change.GetInsert()->contents) {
        // Note: GetVisibleURL() is used instead of GetLastCommittedURL() for
        // simplicity's sake as this test doesn't serve webstore url and
        // the url doesn't commit.
        const GURL& url = contents.contents->GetVisibleURL();
        if (url == learn_more_url) {
          run_loop_.Quit();
          return;
        }
      }
    }

   private:
    base::RunLoop run_loop_;
  };

  // Click "learn more" link.
  {
    TabAddedObserver observer(tab_strip_model);
    delegate_view->ClickLearnMoreLinkForTesting();
    observer.WaitForWebstoreTabAdded();
  }

  CloseAndWait(delegate_view->GetWidget());
}