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 <memory>
6 
7 #include "base/bind.h"
8 #include "base/run_loop.h"
9 #include "base/test/metrics/histogram_tester.h"
10 #include "base/test/scoped_feature_list.h"
11 #include "chrome/browser/browser_features.h"
12 #include "chrome/browser/command_updater_impl.h"
13 #include "chrome/browser/promo_browser_command/promo_browser_command.mojom.h"
14 #include "chrome/browser/ui/chrome_pages.h"
15 #include "chrome/browser/ui/webui/new_tab_page/promo_browser_command/promo_browser_command_handler.h"
16 #include "chrome/common/webui_url_constants.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "components/content_settings/core/common/pref_names.h"
19 #include "components/password_manager/core/common/password_manager_pref_names.h"
20 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
21 #include "components/sync_preferences/testing_pref_service_syncable.h"
22 #include "content/public/test/browser_task_environment.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "ui/base/window_open_disposition.h"
26 
27 using promo_browser_command::mojom::ClickInfo;
28 using promo_browser_command::mojom::ClickInfoPtr;
29 using promo_browser_command::mojom::Command;
30 using promo_browser_command::mojom::CommandHandler;
31 
32 namespace {
33 
34 class TestCommandHandler : public PromoBrowserCommandHandler {
35  public:
TestCommandHandler(Profile * profile)36   explicit TestCommandHandler(Profile* profile)
37       : PromoBrowserCommandHandler(mojo::PendingReceiver<CommandHandler>(),
38                                    profile) {}
39   ~TestCommandHandler() override = default;
40 
NavigateToURL(const GURL &,WindowOpenDisposition)41   void NavigateToURL(const GURL&, WindowOpenDisposition) override {
42     // The functionality of opening a URL is removed, as it cannot be executed
43     // in a unittest.
44   }
45 
GetCommandUpdater()46   CommandUpdater* GetCommandUpdater() override {
47     if (command_updater_)
48       return command_updater_.get();
49     return PromoBrowserCommandHandler::GetCommandUpdater();
50   }
51 
SetCommandUpdater(std::unique_ptr<CommandUpdater> command_updater)52   void SetCommandUpdater(std::unique_ptr<CommandUpdater> command_updater) {
53     command_updater_ = std::move(command_updater);
54     // Ensure that all commands are also updated in the new |command_updater|.
55     EnableCommands();
56   }
57 
58   std::unique_ptr<CommandUpdater> command_updater_;
59 };
60 
61 class MockCommandHandler : public TestCommandHandler {
62  public:
MockCommandHandler(Profile * profile)63   explicit MockCommandHandler(Profile* profile) : TestCommandHandler(profile) {}
64   ~MockCommandHandler() override = default;
65 
66   MOCK_METHOD2(NavigateToURL, void(const GURL&, WindowOpenDisposition));
67 };
68 
69 class MockCommandUpdater : public CommandUpdaterImpl {
70  public:
MockCommandUpdater(CommandUpdaterDelegate * delegate)71   explicit MockCommandUpdater(CommandUpdaterDelegate* delegate)
72       : CommandUpdaterImpl(delegate) {}
73   ~MockCommandUpdater() override = default;
74 
75   MOCK_CONST_METHOD1(IsCommandEnabled, bool(int id));
76   MOCK_CONST_METHOD1(SupportsCommand, bool(int id));
77 };
78 
79 // Callback used for testing
80 // PromoBrowserCommandHandler::CanShowPromoWithCommand().
CanShowPromoWithCommandCallback(base::OnceClosure quit_closure,bool * expected_can_show,bool can_show)81 void CanShowPromoWithCommandCallback(base::OnceClosure quit_closure,
82                                      bool* expected_can_show,
83                                      bool can_show) {
84   *expected_can_show = can_show;
85   std::move(quit_closure).Run();
86 }
87 
88 // Callback used for testing PromoBrowserCommandHandler::ExecuteCommand().
ExecuteCommandCallback(base::OnceClosure quit_closure,bool * expected_command_executed,bool command_executed)89 void ExecuteCommandCallback(base::OnceClosure quit_closure,
90                             bool* expected_command_executed,
91                             bool command_executed) {
92   *expected_command_executed = command_executed;
93   std::move(quit_closure).Run();
94 }
95 
96 // A shorthand for conversion between ClickInfo and WindowOpenDisposition.
DispositionFromClick(const ClickInfo & info)97 WindowOpenDisposition DispositionFromClick(const ClickInfo& info) {
98   return ui::DispositionFromClick(info.middle_button, info.alt_key,
99                                   info.ctrl_key, info.meta_key, info.shift_key);
100 }
101 
102 }  // namespace
103 
104 class PromoBrowserCommandHandlerTest : public testing::Test {
105  public:
106   PromoBrowserCommandHandlerTest() = default;
107   ~PromoBrowserCommandHandlerTest() override = default;
108 
SetUp()109   void SetUp() override {
110     command_handler_ = std::make_unique<MockCommandHandler>(&profile_);
111   }
112 
TearDown()113   void TearDown() override { testing::Test::TearDown(); }
114 
mock_command_updater()115   MockCommandUpdater* mock_command_updater() {
116     return static_cast<MockCommandUpdater*>(
117         command_handler_->GetCommandUpdater());
118   }
119 
CanShowPromoWithCommand(Command command_id)120   bool CanShowPromoWithCommand(Command command_id) {
121     base::RunLoop run_loop;
122     bool can_show = false;
123     command_handler_->CanShowPromoWithCommand(
124         command_id, base::BindOnce(&CanShowPromoWithCommandCallback,
125                                    run_loop.QuitClosure(), &can_show));
126     run_loop.Run();
127     return can_show;
128   }
129 
ExecuteCommand(Command command_id,ClickInfoPtr click_info)130   bool ExecuteCommand(Command command_id, ClickInfoPtr click_info) {
131     base::RunLoop run_loop;
132     bool command_executed = false;
133     command_handler_->ExecuteCommand(
134         command_id, std::move(click_info),
135         base::BindOnce(&ExecuteCommandCallback, run_loop.QuitClosure(),
136                        &command_executed));
137     run_loop.Run();
138     return command_executed;
139   }
140 
141  protected:
142   content::BrowserTaskEnvironment task_environment_;
143   TestingProfile profile_;
144   std::unique_ptr<MockCommandHandler> command_handler_;
145 };
146 
TEST_F(PromoBrowserCommandHandlerTest,SupportedCommands)147 TEST_F(PromoBrowserCommandHandlerTest, SupportedCommands) {
148   base::HistogramTester histogram_tester;
149 
150   // Mock out the command updater to test enabling and disabling commands.
151   command_handler_->SetCommandUpdater(
152       std::make_unique<MockCommandUpdater>(command_handler_.get()));
153 
154   // Unsupported commands do not get executed and no histogram is logged.
155   EXPECT_CALL(*mock_command_updater(),
156               SupportsCommand(static_cast<int>(Command::kUnknownCommand)))
157       .WillOnce(testing::Return(false));
158 
159   EXPECT_FALSE(ExecuteCommand(Command::kUnknownCommand, ClickInfo::New()));
160   histogram_tester.ExpectTotalCount(
161       PromoBrowserCommandHandler::kPromoBrowserCommandHistogramName, 0);
162 
163   // Disabled commands do not get executed and no histogram is logged.
164   EXPECT_CALL(*mock_command_updater(),
165               SupportsCommand(static_cast<int>(Command::kUnknownCommand)))
166       .WillOnce(testing::Return(true));
167   EXPECT_CALL(*mock_command_updater(),
168               IsCommandEnabled(static_cast<int>(Command::kUnknownCommand)))
169       .WillOnce(testing::Return(false));
170 
171   EXPECT_FALSE(ExecuteCommand(Command::kUnknownCommand, ClickInfo::New()));
172   histogram_tester.ExpectTotalCount(
173       PromoBrowserCommandHandler::kPromoBrowserCommandHistogramName, 0);
174 
175   // Only supported and enabled commands get executed for which a histogram is
176   // logged.
177   EXPECT_CALL(*mock_command_updater(),
178               SupportsCommand(static_cast<int>(Command::kUnknownCommand)))
179       .WillOnce(testing::Return(true));
180   EXPECT_CALL(*mock_command_updater(),
181               IsCommandEnabled(static_cast<int>(Command::kUnknownCommand)))
182       .WillOnce(testing::Return(true));
183 
184   EXPECT_TRUE(ExecuteCommand(Command::kUnknownCommand, ClickInfo::New()));
185   histogram_tester.ExpectBucketCount(
186       PromoBrowserCommandHandler::kPromoBrowserCommandHistogramName, 0, 1);
187 }
188 
TEST_F(PromoBrowserCommandHandlerTest,DisableHandlingCommands)189 TEST_F(PromoBrowserCommandHandlerTest, DisableHandlingCommands) {
190   base::HistogramTester histogram_tester;
191 
192   // Disabling features::kPromoBrowserCommands prevents the commands from being
193   // executed.
194   base::test::ScopedFeatureList feature_list;
195   feature_list.InitAndDisableFeature(features::kPromoBrowserCommands);
196 
197   // The PromoBrowserCommandHandler instance needs to be recreated for the
198   // feature to take effect.
199   command_handler_ = std::make_unique<MockCommandHandler>(&profile_);
200 
201   EXPECT_FALSE(ExecuteCommand(Command::kUnknownCommand, ClickInfo::New()));
202   histogram_tester.ExpectTotalCount(
203       PromoBrowserCommandHandler::kPromoBrowserCommandHistogramName, 0);
204 }
205 
TEST_F(PromoBrowserCommandHandlerTest,CanShowPromoWithCommand_OpenSafetyCheck)206 TEST_F(PromoBrowserCommandHandlerTest,
207        CanShowPromoWithCommand_OpenSafetyCheck) {
208   // By default, showing the Safety Check promo is allowed.
209   EXPECT_TRUE(CanShowPromoWithCommand(
210       Command::kOpenSafeBrowsingEnhancedProtectionSettings));
211 
212   // In enterprise environments, i.e. if any browser policy is applied, showing
213   // the Safety Check promo is not allowed.
214   TestingProfile::Builder builder;
215   std::unique_ptr<TestingProfile> profile = builder.Build();
216   profile->GetTestingPrefService()->SetManagedPref(
217       prefs::kSafeBrowsingEnabled, std::make_unique<base::Value>(true));
218   EXPECT_FALSE(CanShowPromoWithCommand(Command::kOpenSafetyCheck));
219 
220   profile->GetTestingPrefService()->RemoveManagedPref(
221       prefs::kSafeBrowsingEnabled);
222   profile->GetTestingPrefService()->SetManagedPref(
223       password_manager::prefs::kCredentialsEnableService,
224       std::make_unique<base::Value>(true));
225   EXPECT_FALSE(CanShowPromoWithCommand(Command::kOpenSafetyCheck));
226 
227   // That's true even if the policy in question is not related to the entries
228   // shown in the Safety Check.
229   profile->GetTestingPrefService()->RemoveManagedPref(
230       password_manager::prefs::kCredentialsEnableService);
231   profile->GetTestingPrefService()->SetManagedPref(
232       prefs::kManagedCookiesAllowedForUrls,
233       std::make_unique<base::Value>(true));
234   EXPECT_FALSE(CanShowPromoWithCommand(Command::kOpenSafetyCheck));
235 
236   // Once policies are removed, the command works again.
237   profile->GetTestingPrefService()->RemoveManagedPref(
238       prefs::kManagedCookiesAllowedForUrls);
239   EXPECT_TRUE(CanShowPromoWithCommand(
240       Command::kOpenSafeBrowsingEnhancedProtectionSettings));
241 }
242 
TEST_F(PromoBrowserCommandHandlerTest,OpenSafetyCheckCommand)243 TEST_F(PromoBrowserCommandHandlerTest, OpenSafetyCheckCommand) {
244   // The OpenSafetyCheck command opens a new settings window with the Safety
245   // Check, and the correct disposition.
246   ClickInfoPtr info = ClickInfo::New();
247   info->middle_button = true;
248   info->meta_key = true;
249   EXPECT_CALL(
250       *command_handler_,
251       NavigateToURL(GURL(chrome::GetSettingsUrl(chrome::kSafetyCheckSubPage)),
252                     DispositionFromClick(*info)));
253   EXPECT_TRUE(ExecuteCommand(Command::kOpenSafetyCheck, std::move(info)));
254 }
255 
TEST_F(PromoBrowserCommandHandlerTest,CanShowSafeBrowsingEnhancedProtectionCommandPromo_NoPolicies)256 TEST_F(PromoBrowserCommandHandlerTest,
257        CanShowSafeBrowsingEnhancedProtectionCommandPromo_NoPolicies) {
258   EXPECT_TRUE(CanShowPromoWithCommand(
259       Command::kOpenSafeBrowsingEnhancedProtectionSettings));
260 }
261 
TEST_F(PromoBrowserCommandHandlerTest,CanShowSafeBrowsingEnhancedProtectionCommandPromo_EnhancedProtectionEnabled)262 TEST_F(
263     PromoBrowserCommandHandlerTest,
264     CanShowSafeBrowsingEnhancedProtectionCommandPromo_EnhancedProtectionEnabled) {
265   TestingProfile::Builder builder;
266   std::unique_ptr<TestingProfile> profile = builder.Build();
267   profile->GetTestingPrefService()->SetUserPref(
268       prefs::kSafeBrowsingEnhanced, std::make_unique<base::Value>(true));
269   command_handler_ = std::make_unique<MockCommandHandler>(profile.get());
270 
271   EXPECT_FALSE(CanShowPromoWithCommand(
272       Command::kOpenSafeBrowsingEnhancedProtectionSettings));
273 }
274 
TEST_F(PromoBrowserCommandHandlerTest,CanShowSafeBrowsingEnhancedProtectionCommandPromo_HasSafeBrowsingManaged_NoProtection)275 TEST_F(
276     PromoBrowserCommandHandlerTest,
277     CanShowSafeBrowsingEnhancedProtectionCommandPromo_HasSafeBrowsingManaged_NoProtection) {
278   TestingProfile::Builder builder;
279   std::unique_ptr<TestingProfile> profile = builder.Build();
280   profile->GetTestingPrefService()->SetManagedPref(
281       prefs::kSafeBrowsingEnabled, std::make_unique<base::Value>(false));
282   profile->GetTestingPrefService()->SetManagedPref(
283       prefs::kSafeBrowsingEnhanced, std::make_unique<base::Value>(false));
284   command_handler_ = std::make_unique<MockCommandHandler>(profile.get());
285 
286   EXPECT_FALSE(CanShowPromoWithCommand(
287       Command::kOpenSafeBrowsingEnhancedProtectionSettings));
288 }
289 
TEST_F(PromoBrowserCommandHandlerTest,CanShowSafeBrowsingEnhancedProtectionCommandPromo_HasSafeBrowsingManaged_StandardProtection)290 TEST_F(
291     PromoBrowserCommandHandlerTest,
292     CanShowSafeBrowsingEnhancedProtectionCommandPromo_HasSafeBrowsingManaged_StandardProtection) {
293   TestingProfile::Builder builder;
294   std::unique_ptr<TestingProfile> profile = builder.Build();
295   profile->GetTestingPrefService()->SetManagedPref(
296       prefs::kSafeBrowsingEnabled, std::make_unique<base::Value>(true));
297   profile->GetTestingPrefService()->SetManagedPref(
298       prefs::kSafeBrowsingEnhanced, std::make_unique<base::Value>(false));
299   command_handler_ = std::make_unique<MockCommandHandler>(profile.get());
300 
301   EXPECT_FALSE(CanShowPromoWithCommand(
302       Command::kOpenSafeBrowsingEnhancedProtectionSettings));
303 }
304 
TEST_F(PromoBrowserCommandHandlerTest,CanShowSafeBrowsingEnhancedProtectionCommandPromo_HasSafeBrowsingManaged_EnhancedProtection)305 TEST_F(
306     PromoBrowserCommandHandlerTest,
307     CanShowSafeBrowsingEnhancedProtectionCommandPromo_HasSafeBrowsingManaged_EnhancedProtection) {
308   TestingProfile::Builder builder;
309   std::unique_ptr<TestingProfile> profile = builder.Build();
310   profile->GetTestingPrefService()->SetManagedPref(
311       prefs::kSafeBrowsingEnabled, std::make_unique<base::Value>(true));
312   profile->GetTestingPrefService()->SetManagedPref(
313       prefs::kSafeBrowsingEnhanced, std::make_unique<base::Value>(true));
314   command_handler_ = std::make_unique<MockCommandHandler>(profile.get());
315 
316   EXPECT_FALSE(CanShowPromoWithCommand(
317       Command::kOpenSafeBrowsingEnhancedProtectionSettings));
318 }
319 
TEST_F(PromoBrowserCommandHandlerTest,OpenSafeBrowsingEnhancedProtectionCommand)320 TEST_F(PromoBrowserCommandHandlerTest,
321        OpenSafeBrowsingEnhancedProtectionCommand) {
322   // The kOpenSafeBrowsingEnhancedProtectionSettings command opens a new
323   // settings window with the Safe Browsing settings with the Enhanced
324   // Protection section expanded, and the correct disposition.
325   ClickInfoPtr info = ClickInfo::New();
326   info->middle_button = true;
327   info->meta_key = true;
328   EXPECT_CALL(
329       *command_handler_,
330       NavigateToURL(GURL(chrome::GetSettingsUrl(
331                         chrome::kSafeBrowsingEnhancedProtectionSubPage)),
332                     DispositionFromClick(*info)));
333   EXPECT_TRUE(ExecuteCommand(
334       Command::kOpenSafeBrowsingEnhancedProtectionSettings, std::move(info)));
335 }
336