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 "base/files/file_path.h"
6#include "base/files/scoped_temp_dir.h"
7#include "base/strings/string_util.h"
8#include "base/strings/stringprintf.h"
9#include "chrome/browser/extensions/api/management/management_api.h"
10#include "chrome/browser/extensions/api/management/management_api_constants.h"
11#include "chrome/browser/extensions/extension_browsertest.h"
12#include "chrome/browser/extensions/extension_function_test_utils.h"
13#include "chrome/browser/extensions/extension_service.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/ui/browser.h"
16#include "content/public/browser/notification_service.h"
17#include "content/public/common/url_constants.h"
18#include "content/public/test/browser_test_utils.h"
19#include "content/public/test/test_utils.h"
20#include "extensions/browser/extension_host.h"
21#include "extensions/browser/extension_prefs.h"
22#include "extensions/browser/extension_system.h"
23#include "extensions/browser/notification_types.h"
24#include "extensions/common/test_util.h"
25#include "extensions/test/extension_test_message_listener.h"
26
27namespace keys = extension_management_api_constants;
28namespace util = extension_function_test_utils;
29
30namespace extensions {
31
32class ExtensionManagementApiBrowserTest : public ExtensionBrowserTest {
33 protected:
34  bool CrashEnabledExtension(const std::string& extension_id) {
35    ExtensionHost* background_host =
36        ExtensionSystem::Get(browser()->profile())->
37            process_manager()->GetBackgroundHostForExtension(extension_id);
38    if (!background_host)
39      return false;
40    content::CrashTab(background_host->host_contents());
41    return true;
42  }
43};
44
45// We test this here instead of in an ExtensionApiTest because normal extensions
46// are not allowed to call the install function.
47IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, InstallEvent) {
48  ExtensionTestMessageListener listener1("ready", false);
49  ASSERT_TRUE(LoadExtension(
50      test_data_dir_.AppendASCII("management/install_event")));
51  ASSERT_TRUE(listener1.WaitUntilSatisfied());
52
53  ExtensionTestMessageListener listener2("got_event", false);
54  ASSERT_TRUE(LoadExtension(
55      test_data_dir_.AppendASCII("api_test/management/enabled_extension")));
56  ASSERT_TRUE(listener2.WaitUntilSatisfied());
57}
58
59IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, LaunchApp) {
60  ExtensionTestMessageListener listener1("app_launched", false);
61  ExtensionTestMessageListener listener2("got_expected_error", false);
62  ASSERT_TRUE(LoadExtension(
63      test_data_dir_.AppendASCII("management/simple_extension")));
64  ASSERT_TRUE(LoadExtension(
65      test_data_dir_.AppendASCII("management/packaged_app")));
66  ASSERT_TRUE(LoadExtension(
67      test_data_dir_.AppendASCII("management/launch_app")));
68  ASSERT_TRUE(listener1.WaitUntilSatisfied());
69  ASSERT_TRUE(listener2.WaitUntilSatisfied());
70}
71
72IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
73                       LaunchAppFromBackground) {
74  ExtensionTestMessageListener listener1("success", false);
75  ASSERT_TRUE(LoadExtension(
76      test_data_dir_.AppendASCII("management/packaged_app")));
77  ASSERT_TRUE(LoadExtension(
78      test_data_dir_.AppendASCII("management/launch_app_from_background")));
79  ASSERT_TRUE(listener1.WaitUntilSatisfied());
80}
81
82IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
83                       SelfUninstall) {
84  ExtensionTestMessageListener listener1("success", false);
85  ASSERT_TRUE(LoadExtension(
86      test_data_dir_.AppendASCII("management/self_uninstall_helper")));
87  ASSERT_TRUE(LoadExtension(
88      test_data_dir_.AppendASCII("management/self_uninstall")));
89  ASSERT_TRUE(listener1.WaitUntilSatisfied());
90}
91
92IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
93                       SelfUninstallNoPermissions) {
94  ExtensionTestMessageListener listener1("success", false);
95  ASSERT_TRUE(LoadExtension(
96      test_data_dir_.AppendASCII("management/self_uninstall_helper")));
97  ASSERT_TRUE(LoadExtension(
98      test_data_dir_.AppendASCII("management/self_uninstall_noperm")));
99  ASSERT_TRUE(listener1.WaitUntilSatisfied());
100}
101
102IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
103                       GetSelfNoPermissions) {
104  ExtensionTestMessageListener listener1("success", false);
105  ASSERT_TRUE(LoadExtension(
106      test_data_dir_.AppendASCII("management/get_self")));
107  ASSERT_TRUE(listener1.WaitUntilSatisfied());
108}
109
110IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
111                       UninstallWithConfirmDialog) {
112  ExtensionService* service = ExtensionSystem::Get(browser()->profile())->
113      extension_service();
114
115  // Install an extension.
116  const Extension* extension = InstallExtension(
117      test_data_dir_.AppendASCII("api_test/management/enabled_extension"), 1);
118  ASSERT_TRUE(extension);
119
120  const std::string id = extension->id();
121
122  scoped_refptr<Extension> empty_extension(test_util::CreateEmptyExtension());
123  // Uninstall, then cancel via the confirm dialog.
124  scoped_refptr<ManagementUninstallFunction> uninstall_function(
125      new ManagementUninstallFunction());
126  uninstall_function->set_extension(empty_extension.get());
127  uninstall_function->set_user_gesture(true);
128  ManagementUninstallFunction::SetAutoConfirmForTest(false);
129
130  EXPECT_TRUE(MatchPattern(
131      util::RunFunctionAndReturnError(
132          uninstall_function.get(),
133          base::StringPrintf("[\"%s\", {\"showConfirmDialog\": true}]",
134                             id.c_str()),
135          browser()),
136      keys::kUninstallCanceledError));
137
138  // Make sure the extension wasn't uninstalled.
139  EXPECT_TRUE(service->GetExtensionById(id, false) != NULL);
140
141  // Uninstall, then accept via the confirm dialog.
142  uninstall_function = new ManagementUninstallFunction();
143  uninstall_function->set_extension(empty_extension.get());
144  ManagementUninstallFunction::SetAutoConfirmForTest(true);
145  uninstall_function->set_user_gesture(true);
146  util::RunFunctionAndReturnSingleResult(
147      uninstall_function.get(),
148      base::StringPrintf("[\"%s\", {\"showConfirmDialog\": true}]", id.c_str()),
149      browser());
150
151  // Make sure the extension was uninstalled.
152  EXPECT_TRUE(service->GetExtensionById(id, false) == NULL);
153}
154
155IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
156                       CreateAppShortcutConfirmDialog) {
157  const Extension* app = InstallExtension(
158      test_data_dir_.AppendASCII("api_test/management/packaged_app"), 1);
159  ASSERT_TRUE(app);
160
161  const std::string app_id = app->id();
162
163  scoped_refptr<ManagementCreateAppShortcutFunction> create_shortcut_function(
164      new ManagementCreateAppShortcutFunction());
165  create_shortcut_function->set_user_gesture(true);
166  ManagementCreateAppShortcutFunction::SetAutoConfirmForTest(true);
167  util::RunFunctionAndReturnSingleResult(
168      create_shortcut_function.get(),
169      base::StringPrintf("[\"%s\"]", app_id.c_str()),
170      browser());
171
172  create_shortcut_function = new ManagementCreateAppShortcutFunction();
173  create_shortcut_function->set_user_gesture(true);
174  ManagementCreateAppShortcutFunction::SetAutoConfirmForTest(false);
175  EXPECT_TRUE(MatchPattern(
176      util::RunFunctionAndReturnError(
177          create_shortcut_function.get(),
178          base::StringPrintf("[\"%s\"]", app_id.c_str()),
179          browser()),
180      keys::kCreateShortcutCanceledError));
181}
182
183IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest,
184                       GetAllIncludesTerminated) {
185  // Load an extension with a background page, so that we know it has a process
186  // running.
187  ExtensionTestMessageListener listener("ready", false);
188  const Extension* extension = LoadExtension(
189      test_data_dir_.AppendASCII("management/install_event"));
190  ASSERT_TRUE(extension);
191  ASSERT_TRUE(listener.WaitUntilSatisfied());
192
193  // The management API should list this extension.
194  scoped_refptr<ManagementGetAllFunction> function =
195      new ManagementGetAllFunction();
196  scoped_ptr<base::Value> result(util::RunFunctionAndReturnSingleResult(
197      function.get(), "[]", browser()));
198  base::ListValue* list;
199  ASSERT_TRUE(result->GetAsList(&list));
200  EXPECT_EQ(1U, list->GetSize());
201
202  // And it should continue to do so even after it crashes.
203  ASSERT_TRUE(CrashEnabledExtension(extension->id()));
204
205  function = new ManagementGetAllFunction();
206  result.reset(util::RunFunctionAndReturnSingleResult(
207      function.get(), "[]", browser()));
208  ASSERT_TRUE(result->GetAsList(&list));
209  EXPECT_EQ(1U, list->GetSize());
210}
211
212class ExtensionManagementApiEscalationTest :
213    public ExtensionManagementApiBrowserTest {
214 protected:
215  // The id of the permissions escalation test extension we use.
216  static const char kId[];
217
218  virtual void SetUpOnMainThread() OVERRIDE {
219    EXPECT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
220    base::FilePath pem_path = test_data_dir_.
221        AppendASCII("permissions_increase").AppendASCII("permissions.pem");
222    base::FilePath path_v1 = PackExtensionWithOptions(
223        test_data_dir_.AppendASCII("permissions_increase").AppendASCII("v1"),
224        scoped_temp_dir_.path().AppendASCII("permissions1.crx"),
225        pem_path,
226        base::FilePath());
227    base::FilePath path_v2 = PackExtensionWithOptions(
228        test_data_dir_.AppendASCII("permissions_increase").AppendASCII("v2"),
229        scoped_temp_dir_.path().AppendASCII("permissions2.crx"),
230        pem_path,
231        base::FilePath());
232
233    ExtensionService* service = ExtensionSystem::Get(browser()->profile())->
234        extension_service();
235
236    // Install low-permission version of the extension.
237    ASSERT_TRUE(InstallExtension(path_v1, 1));
238    EXPECT_TRUE(service->GetExtensionById(kId, false) != NULL);
239
240    // Update to a high-permission version - it should get disabled.
241    EXPECT_FALSE(UpdateExtension(kId, path_v2, -1));
242    EXPECT_TRUE(service->GetExtensionById(kId, false) == NULL);
243    EXPECT_TRUE(service->GetExtensionById(kId, true) != NULL);
244    EXPECT_TRUE(ExtensionPrefs::Get(browser()->profile())
245                    ->DidExtensionEscalatePermissions(kId));
246  }
247
248  void SetEnabled(bool enabled, bool user_gesture,
249                  const std::string& expected_error) {
250    scoped_refptr<ManagementSetEnabledFunction> function(
251        new ManagementSetEnabledFunction);
252    const char* enabled_string = enabled ? "true" : "false";
253    if (user_gesture)
254      function->set_user_gesture(true);
255    bool response = util::RunFunction(
256        function.get(),
257        base::StringPrintf("[\"%s\", %s]", kId, enabled_string),
258        browser(),
259        util::NONE);
260    if (expected_error.empty()) {
261      EXPECT_EQ(true, response);
262    } else {
263      EXPECT_TRUE(response == false);
264      EXPECT_EQ(expected_error, function->GetError());
265    }
266  }
267
268
269 private:
270  base::ScopedTempDir scoped_temp_dir_;
271};
272
273const char ExtensionManagementApiEscalationTest::kId[] =
274    "pgdpcfcocojkjfbgpiianjngphoopgmo";
275
276IN_PROC_BROWSER_TEST_F(ExtensionManagementApiEscalationTest,
277                       DisabledReason) {
278  scoped_refptr<ManagementGetFunction> function =
279      new ManagementGetFunction();
280  scoped_ptr<base::Value> result(util::RunFunctionAndReturnSingleResult(
281      function.get(),
282      base::StringPrintf("[\"%s\"]", kId),
283      browser()));
284  ASSERT_TRUE(result.get() != NULL);
285  ASSERT_TRUE(result->IsType(base::Value::TYPE_DICTIONARY));
286  base::DictionaryValue* dict =
287      static_cast<base::DictionaryValue*>(result.get());
288  std::string reason;
289  EXPECT_TRUE(dict->GetStringASCII(keys::kDisabledReasonKey, &reason));
290  EXPECT_EQ(reason, std::string(keys::kDisabledReasonPermissionsIncrease));
291}
292
293IN_PROC_BROWSER_TEST_F(ExtensionManagementApiEscalationTest,
294                       SetEnabled) {
295  // Expect an error about no gesture.
296  SetEnabled(true, false, keys::kGestureNeededForEscalationError);
297
298  // Expect an error that user cancelled the dialog.
299  ExtensionInstallPrompt::g_auto_confirm_for_tests =
300      ExtensionInstallPrompt::CANCEL;
301  SetEnabled(true, true, keys::kUserDidNotReEnableError);
302
303  // This should succeed when user accepts dialog.  We must wait for the process
304  // to connect *and* for the channel to finish initializing before trying to
305  // crash it.  (NOTIFICATION_RENDERER_PROCESS_CREATED does not wait for the
306  // latter and can cause KillProcess to fail on Windows.)
307  content::WindowedNotificationObserver observer(
308      extensions::NOTIFICATION_EXTENSION_HOST_CREATED,
309      content::NotificationService::AllSources());
310  ExtensionInstallPrompt::g_auto_confirm_for_tests =
311      ExtensionInstallPrompt::ACCEPT;
312  SetEnabled(true, true, std::string());
313  observer.Wait();
314
315  // Crash the extension. Mock a reload by disabling and then enabling. The
316  // extension should be reloaded and enabled.
317  ASSERT_TRUE(CrashEnabledExtension(kId));
318  SetEnabled(false, true, std::string());
319  SetEnabled(true, true, std::string());
320  const Extension* extension = ExtensionSystem::Get(browser()->profile())
321      ->extension_service()->GetExtensionById(kId, false);
322  EXPECT_TRUE(extension);
323}
324
325}  // namespace extensions
326