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 "chrome/browser/chromeos/extensions/echo_private_api.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/threading/thread_task_runner_handle.h"
11 #include "chrome/browser/chromeos/settings/cros_settings.h"
12 #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
13 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
14 #include "chrome/browser/chromeos/ui/echo_dialog_view.h"
15 #include "chrome/browser/extensions/extension_apitest.h"
16 #include "chrome/browser/extensions/extension_function_test_utils.h"
17 #include "chrome/browser/extensions/extension_tab_util.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "chromeos/constants/chromeos_switches.h"
21 #include "content/public/test/browser_test.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 
24 namespace utils = extension_function_test_utils;
25 
26 namespace chromeos {
27 
28 class ExtensionEchoPrivateApiTest : public extensions::ExtensionApiTest {
29  public:
30   enum DialogTestAction {
31     DIALOG_TEST_ACTION_NONE,
32     DIALOG_TEST_ACTION_ACCEPT,
33     DIALOG_TEST_ACTION_CANCEL,
34   };
35 
ExtensionEchoPrivateApiTest()36   ExtensionEchoPrivateApiTest()
37       : expected_dialog_buttons_(ui::DIALOG_BUTTON_NONE),
38         dialog_action_(DIALOG_TEST_ACTION_NONE),
39         dialog_invocation_count_(0) {
40   }
41 
~ExtensionEchoPrivateApiTest()42   ~ExtensionEchoPrivateApiTest() override {}
43 
RunDefaultGetUserFunctionAndExpectResultEquals(int tab_id,bool expected_result)44   void RunDefaultGetUserFunctionAndExpectResultEquals(int tab_id,
45                                                       bool expected_result) {
46     scoped_refptr<EchoPrivateGetUserConsentFunction> function(
47         EchoPrivateGetUserConsentFunction::CreateForTest(
48             base::BindRepeating(&ExtensionEchoPrivateApiTest::OnDialogShown,
49                                 base::Unretained(this))));
50     function->set_has_callback(true);
51 
52     const std::string arguments = base::StringPrintf(
53         R"([{"serviceName": "name", "origin": "https://test.com", "tabId": %d}])",
54         tab_id);
55     std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
56         function.get(), arguments, browser()));
57 
58     ASSERT_TRUE(result.get());
59     ASSERT_EQ(base::Value::Type::BOOLEAN, result->type());
60 
61     bool result_as_boolean = false;
62     ASSERT_TRUE(result->GetAsBoolean(&result_as_boolean));
63 
64     EXPECT_EQ(expected_result, result_as_boolean);
65   }
66 
OnDialogShown(chromeos::EchoDialogView * dialog)67   void OnDialogShown(chromeos::EchoDialogView* dialog) {
68     dialog_invocation_count_++;
69     ASSERT_LE(dialog_invocation_count_, 1);
70 
71     EXPECT_EQ(expected_dialog_buttons_, dialog->GetDialogButtons());
72 
73     // Don't accept the dialog if the dialog buttons don't match expectation.
74     // Accepting a dialog which should not have accept option may crash the
75     // test. The test already failed, so it's ok to cancel the dialog.
76     DialogTestAction dialog_action = dialog_action_;
77     if (dialog_action == DIALOG_TEST_ACTION_ACCEPT &&
78         expected_dialog_buttons_ != dialog->GetDialogButtons()) {
79       dialog_action = DIALOG_TEST_ACTION_CANCEL;
80     }
81 
82     // Perform test action on the dialog.
83     // The dialog should stay around until AcceptWindow or CancelWindow is
84     // called, so base::Unretained is safe.
85     if (dialog_action == DIALOG_TEST_ACTION_ACCEPT) {
86       base::ThreadTaskRunnerHandle::Get()->PostTask(
87           FROM_HERE,
88           base::BindOnce(base::IgnoreResult(&chromeos::EchoDialogView::Accept),
89                          base::Unretained(dialog)));
90     } else if (dialog_action == DIALOG_TEST_ACTION_CANCEL) {
91       base::ThreadTaskRunnerHandle::Get()->PostTask(
92           FROM_HERE,
93           base::BindOnce(base::IgnoreResult(&chromeos::EchoDialogView::Cancel),
94                          base::Unretained(dialog)));
95     }
96   }
97 
dialog_invocation_count() const98   int dialog_invocation_count() const {
99     return dialog_invocation_count_;
100   }
101 
102   // Open and activates tab in the test browser. Returns the ID of the opened
103   // tab.
OpenAndActivateTab()104   int OpenAndActivateTab() {
105     AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_LINK);
106     browser()->tab_strip_model()->ActivateTabAt(
107         0, {TabStripModel::GestureType::kOther});
108     return extensions::ExtensionTabUtil::GetTabId(
109         browser()->tab_strip_model()->GetActiveWebContents());
110   }
111 
CloseTabWithId(int tab_id)112   bool CloseTabWithId(int tab_id) {
113     TabStripModel* tab_strip = nullptr;
114     int tab_index = -1;
115     if (!extensions::ExtensionTabUtil::GetTabById(tab_id, profile(), false,
116                                                   nullptr, &tab_strip, nullptr,
117                                                   &tab_index)) {
118       ADD_FAILURE() << "Tab not found " << tab_id;
119       return false;
120     }
121 
122     return tab_strip->CloseWebContentsAt(tab_index, 0);
123   }
124 
125  protected:
126   int expected_dialog_buttons_;
127   DialogTestAction dialog_action_;
128   ScopedTestingCrosSettings scoped_testing_cros_settings_;
129 
130  private:
131   int dialog_invocation_count_;
132 };
133 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,EchoTest)134 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest, EchoTest) {
135   EXPECT_TRUE(RunComponentExtensionTest("echo/component_extension"))
136       << message_;
137 }
138 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,GetUserConsent_InvalidOrigin)139 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
140                        GetUserConsent_InvalidOrigin) {
141   const int tab_id = OpenAndActivateTab();
142 
143   expected_dialog_buttons_ =  ui::DIALOG_BUTTON_NONE;
144   dialog_action_ = DIALOG_TEST_ACTION_NONE;
145 
146   scoped_refptr<EchoPrivateGetUserConsentFunction> function(
147       EchoPrivateGetUserConsentFunction::CreateForTest(
148           base::BindRepeating(&ExtensionEchoPrivateApiTest::OnDialogShown,
149                               base::Unretained(this))));
150 
151   std::string error = utils::RunFunctionAndReturnError(
152       function.get(),
153       base::StringPrintf(
154           R"([{"serviceName": "name", "origin": "invalid", "tabId": %d}])",
155           tab_id),
156       browser());
157 
158   EXPECT_EQ("Invalid origin.", error);
159   EXPECT_EQ(0, dialog_invocation_count());
160 }
161 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,GetUserConsent_NoTabIdSet)162 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest, GetUserConsent_NoTabIdSet) {
163   expected_dialog_buttons_ = ui::DIALOG_BUTTON_NONE;
164   dialog_action_ = DIALOG_TEST_ACTION_NONE;
165 
166   scoped_refptr<EchoPrivateGetUserConsentFunction> function(
167       EchoPrivateGetUserConsentFunction::CreateForTest(
168           base::BindRepeating(&ExtensionEchoPrivateApiTest::OnDialogShown,
169                               base::Unretained(this))));
170 
171   std::string error = utils::RunFunctionAndReturnError(
172       function.get(),
173       R"([{"serviceName": "name", "origin": "https://test.com"}])", browser());
174 
175   EXPECT_EQ("Not called from an app window - the tabId is required.", error);
176   EXPECT_EQ(0, dialog_invocation_count());
177 }
178 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,GetUserConsent_InactiveTab)179 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
180                        GetUserConsent_InactiveTab) {
181   const int tab_id = OpenAndActivateTab();
182   // Open and activate another tab.
183   OpenAndActivateTab();
184 
185   expected_dialog_buttons_ = ui::DIALOG_BUTTON_NONE;
186   dialog_action_ = DIALOG_TEST_ACTION_NONE;
187 
188   scoped_refptr<EchoPrivateGetUserConsentFunction> function(
189       EchoPrivateGetUserConsentFunction::CreateForTest(
190           base::BindRepeating(&ExtensionEchoPrivateApiTest::OnDialogShown,
191                               base::Unretained(this))));
192 
193   const std::string arguments = base::StringPrintf(
194       R"([{"serviceName": "name", "origin": "https://test.com", "tabId": %d}])",
195       tab_id);
196   std::string error =
197       utils::RunFunctionAndReturnError(function.get(), arguments, browser());
198 
199   EXPECT_EQ("Consent requested from an inactive tab.", error);
200   EXPECT_EQ(0, dialog_invocation_count());
201 }
202 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,GetUserConsent_ClosedTab)203 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest, GetUserConsent_ClosedTab) {
204   const int tab_id = OpenAndActivateTab();
205   ASSERT_TRUE(CloseTabWithId(tab_id));
206 
207   expected_dialog_buttons_ = ui::DIALOG_BUTTON_NONE;
208   dialog_action_ = DIALOG_TEST_ACTION_NONE;
209 
210   scoped_refptr<EchoPrivateGetUserConsentFunction> function(
211       EchoPrivateGetUserConsentFunction::CreateForTest(
212           base::BindRepeating(&ExtensionEchoPrivateApiTest::OnDialogShown,
213                               base::Unretained(this))));
214 
215   const std::string arguments = base::StringPrintf(
216       R"([{"serviceName": "name", "origin": "https://test.com", "tabId": %d}])",
217       tab_id);
218   std::string error =
219       utils::RunFunctionAndReturnError(function.get(), arguments, browser());
220 
221   EXPECT_EQ("Tab not found.", error);
222   EXPECT_EQ(0, dialog_invocation_count());
223 }
224 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,GetUserConsent_AllowRedeemPrefNotSet)225 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
226                        GetUserConsent_AllowRedeemPrefNotSet) {
227   const int tab_id = OpenAndActivateTab();
228   expected_dialog_buttons_ = ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK;
229   dialog_action_ = DIALOG_TEST_ACTION_ACCEPT;
230 
231   RunDefaultGetUserFunctionAndExpectResultEquals(tab_id, true);
232 
233   EXPECT_EQ(1, dialog_invocation_count());
234 }
235 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,GetUserConsent_AllowRedeemPrefTrue)236 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
237                        GetUserConsent_AllowRedeemPrefTrue) {
238   const int tab_id = OpenAndActivateTab();
239   scoped_testing_cros_settings_.device_settings()->Set(
240       kAllowRedeemChromeOsRegistrationOffers, base::Value(true));
241 
242   expected_dialog_buttons_ = ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK;
243   dialog_action_ = DIALOG_TEST_ACTION_ACCEPT;
244 
245   RunDefaultGetUserFunctionAndExpectResultEquals(tab_id, true);
246 
247   EXPECT_EQ(1, dialog_invocation_count());
248 }
249 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,GetUserConsent_ConsentDenied)250 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
251                        GetUserConsent_ConsentDenied) {
252   const int tab_id = OpenAndActivateTab();
253   scoped_testing_cros_settings_.device_settings()->Set(
254       kAllowRedeemChromeOsRegistrationOffers, base::Value(true));
255 
256   expected_dialog_buttons_ = ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK;
257   dialog_action_ = DIALOG_TEST_ACTION_CANCEL;
258 
259   RunDefaultGetUserFunctionAndExpectResultEquals(tab_id, false);
260 
261   EXPECT_EQ(1, dialog_invocation_count());
262 }
263 
IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,GetUserConsent_AllowRedeemPrefFalse)264 IN_PROC_BROWSER_TEST_F(ExtensionEchoPrivateApiTest,
265                        GetUserConsent_AllowRedeemPrefFalse) {
266   const int tab_id = OpenAndActivateTab();
267   scoped_testing_cros_settings_.device_settings()->Set(
268       kAllowRedeemChromeOsRegistrationOffers, base::Value(false));
269 
270   expected_dialog_buttons_ = ui::DIALOG_BUTTON_CANCEL;
271   dialog_action_ = DIALOG_TEST_ACTION_CANCEL;
272 
273   RunDefaultGetUserFunctionAndExpectResultEquals(tab_id, false);
274 
275   EXPECT_EQ(1, dialog_invocation_count());
276 }
277 
278 }  // namespace chromeos
279