1 // Copyright 2020 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 "chrome/browser/ui/views/extensions/settings_overridden_dialog_view.h"
6
7 #include "base/path_service.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/test/scoped_feature_list.h"
10 #include "base/time/time.h"
11 #include "build/build_config.h"
12 #include "chrome/app/vector_icons/vector_icons.h"
13 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/search_engines/template_url_service_factory.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_window.h"
18 #include "chrome/browser/ui/extensions/settings_api_bubble_helpers.h"
19 #include "chrome/browser/ui/extensions/settings_overridden_dialog_controller.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
21 #include "chrome/browser/ui/test/test_browser_dialog.h"
22 #include "chrome/browser/ui/ui_features.h"
23 #include "chrome/common/chrome_paths.h"
24 #include "chrome/common/webui_url_constants.h"
25 #include "chrome/test/base/search_test_utils.h"
26 #include "chrome/test/base/ui_test_utils.h"
27 #include "components/search_engines/search_engines_test_util.h"
28 #include "components/search_engines/template_url.h"
29 #include "components/search_engines/template_url_service.h"
30 #include "content/public/test/browser_test.h"
31 #include "content/public/test/browser_test_utils.h"
32 #include "ui/views/test/widget_test.h"
33
34 namespace {
35
36 // A stub dialog controller that displays the dialog with the supplied params.
37 class TestDialogController : public SettingsOverriddenDialogController {
38 public:
TestDialogController(ShowParams show_params,base::Optional<DialogResult> * dialog_result_out)39 TestDialogController(ShowParams show_params,
40 base::Optional<DialogResult>* dialog_result_out)
41 : show_params_(std::move(show_params)),
42 dialog_result_out_(dialog_result_out) {
43 DCHECK(dialog_result_out_);
44 }
45 TestDialogController(const TestDialogController&) = delete;
46 TestDialogController& operator=(const TestDialogController&) = delete;
47 ~TestDialogController() override = default;
48
49 private:
ShouldShow()50 bool ShouldShow() override { return true; }
GetShowParams()51 ShowParams GetShowParams() override { return show_params_; }
OnDialogShown()52 void OnDialogShown() override {}
HandleDialogResult(DialogResult result)53 void HandleDialogResult(DialogResult result) override {
54 ASSERT_FALSE(dialog_result_out_->has_value());
55 *dialog_result_out_ = result;
56 }
57
58 const ShowParams show_params_;
59
60 // The result to populate. Must outlive this object.
61 base::Optional<DialogResult>* const dialog_result_out_;
62 };
63
64 } // namespace
65
66 class SettingsOverriddenDialogViewBrowserTest : public DialogBrowserTest {
67 public:
68 enum class DefaultSearch {
69 kUseDefault,
70 kUseNonGoogleFromDefaultList,
71 kUseNewSearch,
72 };
73
SettingsOverriddenDialogViewBrowserTest()74 SettingsOverriddenDialogViewBrowserTest() {
75 scoped_feature_list_.InitAndEnableFeature(
76 features::kExtensionSettingsOverriddenDialogs);
77 }
78 ~SettingsOverriddenDialogViewBrowserTest() override = default;
79
SetUpOnMainThread()80 void SetUpOnMainThread() override {
81 DialogBrowserTest::SetUpOnMainThread();
82 search_test_utils::WaitForTemplateURLServiceToLoad(
83 TemplateURLServiceFactory::GetForProfile(browser()->profile()));
84 }
85
ShowUi(const std::string & name)86 void ShowUi(const std::string& name) override {
87 test_name_ = name;
88 if (name == "SimpleDialog") {
89 ShowSimpleDialog(false, browser());
90 } else if (name == "SimpleDialogWithIcon") {
91 ShowSimpleDialog(true, browser());
92 } else if (name == "NtpOverriddenDialog_BackToDefault") {
93 ShowNtpOverriddenDefaultDialog();
94 } else if (name == "NtpOverriddenDialog_Generic") {
95 ShowNtpOverriddenGenericDialog();
96 } else if (name == "SearchOverriddenDialog_BackToGoogle") {
97 ShowSearchOverriddenDialog(DefaultSearch::kUseDefault);
98 } else if (name == "SearchOverriddenDialog_BackToOther") {
99 ShowSearchOverriddenDialog(DefaultSearch::kUseNonGoogleFromDefaultList);
100 } else if (name == "SearchOverriddenDialog_Generic") {
101 ShowSearchOverriddenDialog(DefaultSearch::kUseNewSearch);
102 } else {
103 NOTREACHED() << name;
104 }
105 }
106
107 // Creates, shows, and returns a dialog anchored to the given |browser|. The
108 // dialog is owned by the views framework.
ShowSimpleDialog(bool show_icon,Browser * browser)109 SettingsOverriddenDialogView* ShowSimpleDialog(bool show_icon,
110 Browser* browser) {
111 SettingsOverriddenDialogController::ShowParams params{
112 base::ASCIIToUTF16("Settings overridden dialog title"),
113 base::ASCIIToUTF16(
114 "Settings overriden dialog body, which is quite a bit "
115 "longer than the title alone")};
116 if (show_icon)
117 params.icon = &kProductIcon;
118 auto* dialog =
119 new SettingsOverriddenDialogView(std::make_unique<TestDialogController>(
120 std::move(params), &dialog_result_));
121 dialog->Show(browser->window()->GetNativeWindow());
122
123 return dialog;
124 }
125
ShowNtpOverriddenDefaultDialog()126 void ShowNtpOverriddenDefaultDialog() {
127 // Load an extension overriding the NTP and open a new tab to trigger the
128 // dialog.
129 LoadExtensionOverridingNewTab();
130 NavigateToNewTab();
131 }
132
ShowNtpOverriddenGenericDialog()133 void ShowNtpOverriddenGenericDialog() {
134 SetNewSearchProvider(DefaultSearch::kUseNonGoogleFromDefaultList);
135 LoadExtensionOverridingNewTab();
136 NavigateToNewTab();
137 }
138
ShowSearchOverriddenDialog(DefaultSearch search)139 void ShowSearchOverriddenDialog(DefaultSearch search) {
140 SetNewSearchProvider(search);
141 LoadExtensionOverridingSearch();
142 PerformSearchFromOmnibox();
143 }
144
VerifyUi()145 bool VerifyUi() override {
146 if (!DialogBrowserTest::VerifyUi())
147 return false;
148
149 if (base::StartsWith(test_name_, "SearchOverriddenDialog",
150 base::CompareCase::SENSITIVE)) {
151 // Note: Because this is a test, we don't actually expect this navigation
152 // to succeed. But we can still check that the user was sent to
153 // example.com (the new search engine).
154 EXPECT_EQ("www.example.com", browser()
155 ->tab_strip_model()
156 ->GetActiveWebContents()
157 ->GetLastCommittedURL()
158 .host_piece());
159 }
160
161 return true;
162 }
163
164 base::Optional<SettingsOverriddenDialogController::DialogResult>
dialog_result() const165 dialog_result() const {
166 return dialog_result_;
167 }
168
169 private:
LoadExtensionOverridingNewTab()170 void LoadExtensionOverridingNewTab() {
171 base::FilePath test_root_path;
172 ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_root_path));
173
174 Profile* const profile = browser()->profile();
175 scoped_refptr<const extensions::Extension> extension =
176 extensions::ChromeTestExtensionLoader(profile).LoadExtension(
177 test_root_path.AppendASCII("extensions/api_test/override/newtab"));
178 ASSERT_TRUE(extension);
179 }
180
LoadExtensionOverridingSearch()181 void LoadExtensionOverridingSearch() {
182 base::FilePath test_root_path;
183 ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_root_path));
184
185 Profile* const profile = browser()->profile();
186 scoped_refptr<const extensions::Extension> extension =
187 extensions::ChromeTestExtensionLoader(profile).LoadExtension(
188 test_root_path.AppendASCII("extensions/search_provider_override"));
189 ASSERT_TRUE(extension);
190 }
191
NavigateToNewTab()192 void NavigateToNewTab() {
193 ui_test_utils::NavigateToURLWithDisposition(
194 browser(), GURL(chrome::kChromeUINewTabURL),
195 WindowOpenDisposition::NEW_FOREGROUND_TAB,
196 ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
197 }
198
SetNewSearchProvider(DefaultSearch search)199 void SetNewSearchProvider(DefaultSearch search) {
200 if (search == DefaultSearch::kUseDefault)
201 return;
202
203 TemplateURLService* const template_url_service =
204 TemplateURLServiceFactory::GetForProfile(browser()->profile());
205
206 bool new_search_shows_in_default_list = true;
207 // If the test requires a search engine that doesn't show in the default
208 // list, we need to add one.
209 if (search == DefaultSearch::kUseNewSearch) {
210 new_search_shows_in_default_list = false;
211 template_url_service->Add(
212 std::make_unique<TemplateURL>(*GenerateDummyTemplateURLData("test")));
213 }
214
215 TemplateURLService::TemplateURLVector template_urls =
216 template_url_service->GetTemplateURLs();
217 auto iter =
218 std::find_if(template_urls.begin(), template_urls.end(),
219 [template_url_service, new_search_shows_in_default_list](
220 const TemplateURL* turl) {
221 return !turl->HasGoogleBaseURLs(
222 template_url_service->search_terms_data()) &&
223 template_url_service->ShowInDefaultList(turl) ==
224 new_search_shows_in_default_list;
225 });
226 ASSERT_TRUE(iter != template_urls.end());
227
228 template_url_service->SetUserSelectedDefaultSearchProvider(*iter);
229 }
230
PerformSearchFromOmnibox()231 void PerformSearchFromOmnibox() {
232 ui_test_utils::SendToOmniboxAndSubmit(browser(), "Penguin",
233 base::TimeTicks::Now());
234 content::WaitForLoadStop(
235 browser()->tab_strip_model()->GetActiveWebContents());
236 }
237
238 std::string test_name_;
239
240 base::Optional<SettingsOverriddenDialogController::DialogResult>
241 dialog_result_;
242
243 base::test::ScopedFeatureList scoped_feature_list_;
244 };
245
246 ////////////////////////////////////////////////////////////////////////////////
247 // UI Browser Tests
248
IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,InvokeUi_SimpleDialog)249 IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
250 InvokeUi_SimpleDialog) {
251 ShowAndVerifyUi();
252 }
253
IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,InvokeUi_SimpleDialogWithIcon)254 IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
255 InvokeUi_SimpleDialogWithIcon) {
256 ShowAndVerifyUi();
257 }
258
IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,InvokeUi_NtpOverriddenDialog_BackToDefault)259 IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
260 InvokeUi_NtpOverriddenDialog_BackToDefault) {
261 // Force the post-install NTP UI to be enabled, so that we can test on all
262 // platforms.
263 extensions::SetNtpPostInstallUiEnabledForTesting(true);
264 ShowAndVerifyUi();
265 extensions::SetNtpPostInstallUiEnabledForTesting(false);
266 }
267
IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,InvokeUi_NtpOverriddenDialog_Generic)268 IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
269 InvokeUi_NtpOverriddenDialog_Generic) {
270 // Force the post-install NTP UI to be enabled, so that we can test on all
271 // platforms.
272 extensions::SetNtpPostInstallUiEnabledForTesting(true);
273 ShowAndVerifyUi();
274 extensions::SetNtpPostInstallUiEnabledForTesting(false);
275 }
276
277 // The chrome_settings_overrides API that allows extensions to override the
278 // default search provider is only available on Windows and Mac.
279 #if defined(OS_WIN) || defined(OS_MAC)
IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,InvokeUi_SearchOverriddenDialog_BackToGoogle)280 IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
281 InvokeUi_SearchOverriddenDialog_BackToGoogle) {
282 ShowAndVerifyUi();
283 }
284
IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,InvokeUi_SearchOverriddenDialog_BackToOther)285 IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
286 InvokeUi_SearchOverriddenDialog_BackToOther) {
287 ShowAndVerifyUi();
288 }
289
IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,InvokeUi_SearchOverriddenDialog_Generic)290 IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
291 InvokeUi_SearchOverriddenDialog_Generic) {
292 ShowAndVerifyUi();
293 }
294 #endif // defined(OS_WIN) || defined(OS_MAC)
295
296 ////////////////////////////////////////////////////////////////////////////////
297 // Functional Browser Tests
298
299 // Verify that if the parent window is closed, the dialog notifies the
300 // controller that it was closed without any user action.
IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,DialogWindowClosed)301 IN_PROC_BROWSER_TEST_F(SettingsOverriddenDialogViewBrowserTest,
302 DialogWindowClosed) {
303 Browser* second_browser = CreateBrowser(browser()->profile());
304 ASSERT_TRUE(second_browser);
305
306 SettingsOverriddenDialogView* dialog =
307 ShowSimpleDialog(false, second_browser);
308
309 views::test::WidgetDestroyedWaiter widget_destroyed_waiter(
310 dialog->GetWidget());
311 CloseBrowserSynchronously(second_browser);
312 widget_destroyed_waiter.Wait();
313 ASSERT_TRUE(dialog_result());
314 EXPECT_EQ(SettingsOverriddenDialogController::DialogResult::
315 kDialogClosedWithoutUserAction,
316 *dialog_result());
317 }
318