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.
5 #include "base/memory/ref_counted.h"
6 #include "chrome/browser/download/download_crx_util.h"
7 #include "chrome/browser/extensions/browser_action_test_util.h"
8 #include "chrome/browser/extensions/crx_installer.h"
9 #include "chrome/browser/extensions/extension_browsertest.h"
10 #include "chrome/browser/extensions/extension_install_prompt.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_system.h"
13 #include "chrome/browser/extensions/fake_safe_browsing_database_manager.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/common/extensions/extension.h"
19 #include "chrome/common/extensions/extension_file_util.h"
20 #include "chrome/common/extensions/feature_switch.h"
21 #include "chrome/test/base/ui_test_utils.h"
22 #include "content/public/browser/download_manager.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/test/download_test_observer.h"
25 #include "extensions/common/permissions/permission_set.h"
26 #include "extensions/common/switches.h"
27 #include "grit/generated_resources.h"
28 #include "ui/base/l10n/l10n_util.h"
30 #if defined(OS_CHROMEOS)
31 #include "chrome/browser/chromeos/login/fake_user_manager.h"
32 #include "chrome/browser/chromeos/login/user_manager.h"
37 namespace extensions {
41 class MockInstallPrompt;
43 // This class holds information about things that happen with a
44 // MockInstallPrompt. We create the MockInstallPrompt but need to pass
45 // ownership of it to CrxInstaller, so it isn't safe to hang this data on
46 // MockInstallPrompt itself becuase we can't guarantee it's lifetime.
47 class MockPromptProxy :
48 public base::RefCountedThreadSafe<MockPromptProxy> {
50 explicit MockPromptProxy(content::WebContents* web_contents);
52 bool did_succeed() const { return !extension_id_.empty(); }
53 const std::string& extension_id() { return extension_id_; }
54 bool confirmation_requested() const { return confirmation_requested_; }
55 const string16& error() const { return error_; }
57 // To have any effect, this should be called before CreatePrompt.
58 void set_record_oauth2_grant(bool record_oauth2_grant) {
59 record_oauth2_grant_.reset(new bool(record_oauth2_grant));
62 void set_extension_id(const std::string& id) { extension_id_ = id; }
63 void set_confirmation_requested() { confirmation_requested_ = true; }
64 void set_error(const string16& error) { error_ = error; }
66 scoped_ptr<ExtensionInstallPrompt> CreatePrompt();
69 friend class base::RefCountedThreadSafe<MockPromptProxy>;
70 virtual ~MockPromptProxy();
72 // Data used to create a prompt.
73 content::WebContents* web_contents_;
74 scoped_ptr<bool> record_oauth2_grant_;
76 // Data reported back to us by the prompt we created.
77 bool confirmation_requested_;
78 std::string extension_id_;
82 class MockInstallPrompt : public ExtensionInstallPrompt {
84 MockInstallPrompt(content::WebContents* web_contents,
85 MockPromptProxy* proxy) :
86 ExtensionInstallPrompt(web_contents),
89 void set_record_oauth2_grant(bool record) { record_oauth2_grant_ = record; }
91 // Overriding some of the ExtensionInstallUI API.
92 virtual void ConfirmInstall(
94 const Extension* extension,
95 const ShowDialogCallback& show_dialog_callback) OVERRIDE {
96 proxy_->set_confirmation_requested();
97 delegate->InstallUIProceed();
99 virtual void OnInstallSuccess(const Extension* extension,
100 SkBitmap* icon) OVERRIDE {
101 proxy_->set_extension_id(extension->id());
102 base::MessageLoopForUI::current()->Quit();
104 virtual void OnInstallFailure(const CrxInstallerError& error) OVERRIDE {
105 proxy_->set_error(error.message());
106 base::MessageLoopForUI::current()->Quit();
110 scoped_refptr<MockPromptProxy> proxy_;
114 MockPromptProxy::MockPromptProxy(content::WebContents* web_contents) :
115 web_contents_(web_contents),
116 confirmation_requested_(false) {
119 MockPromptProxy::~MockPromptProxy() {}
121 scoped_ptr<ExtensionInstallPrompt> MockPromptProxy::CreatePrompt() {
122 scoped_ptr<MockInstallPrompt> prompt(
123 new MockInstallPrompt(web_contents_, this));
124 if (record_oauth2_grant_.get())
125 prompt->set_record_oauth2_grant(*record_oauth2_grant_.get());
126 return prompt.PassAs<ExtensionInstallPrompt>();
130 scoped_refptr<MockPromptProxy> CreateMockPromptProxyForBrowser(
132 return new MockPromptProxy(
133 browser->tab_strip_model()->GetActiveWebContents());
138 class ExtensionCrxInstallerTest : public ExtensionBrowserTest {
140 scoped_ptr<WebstoreInstaller::Approval> GetApproval(
141 const char* manifest_dir,
142 const std::string& id,
143 bool strict_manifest_checks) {
144 scoped_ptr<WebstoreInstaller::Approval> result;
146 base::FilePath ext_path = test_data_dir_.AppendASCII(manifest_dir);
148 scoped_ptr<base::DictionaryValue> parsed_manifest(
149 extension_file_util::LoadManifest(ext_path, &error));
150 if (!parsed_manifest.get() || !error.empty())
151 return result.Pass();
153 return WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
154 browser()->profile(),
156 parsed_manifest.Pass(),
157 strict_manifest_checks);
160 void RunCrxInstaller(const WebstoreInstaller::Approval* approval,
161 scoped_ptr<ExtensionInstallPrompt> prompt,
162 const base::FilePath& crx_path) {
163 ExtensionService* service = extensions::ExtensionSystem::Get(
164 browser()->profile())->extension_service();
165 scoped_refptr<CrxInstaller> installer(
166 CrxInstaller::Create(service, prompt.Pass(), approval));
167 installer->set_allow_silent_install(true);
168 installer->set_is_gallery_install(true);
169 installer->InstallCrx(crx_path);
170 content::RunMessageLoop();
173 // Installs a crx from |crx_relpath| (a path relative to the extension test
174 // data dir) with expected id |id|.
175 void InstallWithPrompt(const char* ext_relpath,
176 const std::string& id,
177 scoped_refptr<MockPromptProxy> mock_install_prompt) {
178 base::FilePath ext_path = test_data_dir_.AppendASCII(ext_relpath);
180 scoped_ptr<WebstoreInstaller::Approval> approval;
182 approval = GetApproval(ext_relpath, id, true);
184 base::FilePath crx_path = PackExtension(ext_path);
185 EXPECT_FALSE(crx_path.empty());
186 RunCrxInstaller(approval.get(), mock_install_prompt->CreatePrompt(),
189 EXPECT_TRUE(mock_install_prompt->did_succeed());
192 // Installs an extension and checks that it has scopes granted IFF
193 // |record_oauth2_grant| is true.
194 void CheckHasEmptyScopesAfterInstall(const std::string& ext_relpath,
195 bool record_oauth2_grant) {
196 CommandLine::ForCurrentProcess()->AppendSwitch(
197 switches::kEnableExperimentalExtensionApis);
199 ExtensionService* service = extensions::ExtensionSystem::Get(
200 browser()->profile())->extension_service();
202 scoped_refptr<MockPromptProxy> mock_prompt =
203 CreateMockPromptProxyForBrowser(browser());
205 mock_prompt->set_record_oauth2_grant(record_oauth2_grant);
206 InstallWithPrompt("browsertest/scopes", std::string(), mock_prompt);
208 scoped_refptr<PermissionSet> permissions =
209 service->extension_prefs()->GetGrantedPermissions(
210 mock_prompt->extension_id());
211 ASSERT_TRUE(permissions.get());
215 #if defined(OS_CHROMEOS)
216 #define MAYBE_Whitelisting DISABLED_Whitelisting
218 #define MAYBE_Whitelisting Whitelisting
220 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, MAYBE_Whitelisting) {
221 std::string id = "hdgllgikmikobbofgnabhfimcfoopgnd";
222 ExtensionService* service = extensions::ExtensionSystem::Get(
223 browser()->profile())->extension_service();
225 // Even whitelisted extensions with NPAPI should not prompt.
226 scoped_refptr<MockPromptProxy> mock_prompt =
227 CreateMockPromptProxyForBrowser(browser());
228 InstallWithPrompt("uitest/plugins", id, mock_prompt);
229 EXPECT_FALSE(mock_prompt->confirmation_requested());
230 EXPECT_TRUE(service->GetExtensionById(id, false));
233 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest,
234 GalleryInstallGetsExperimental) {
235 // We must modify the command line temporarily in order to pack an extension
236 // that requests the experimental permission.
237 CommandLine* command_line = CommandLine::ForCurrentProcess();
238 CommandLine old_command_line = *command_line;
239 command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
240 base::FilePath crx_path = PackExtension(
241 test_data_dir_.AppendASCII("experimental"));
242 ASSERT_FALSE(crx_path.empty());
244 // Now reset the command line so that we are testing specifically whether
245 // installing from webstore enables experimental permissions.
246 *(CommandLine::ForCurrentProcess()) = old_command_line;
248 EXPECT_FALSE(InstallExtension(crx_path, 0));
249 EXPECT_TRUE(InstallExtensionFromWebstore(crx_path, 1));
252 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, PlatformAppCrx) {
253 CommandLine::ForCurrentProcess()->AppendSwitch(
254 switches::kEnableExperimentalExtensionApis);
255 EXPECT_TRUE(InstallExtension(
256 test_data_dir_.AppendASCII("minimal_platform_app.crx"), 1));
259 // http://crbug.com/136397
260 #if defined(OS_CHROMEOS)
261 #define MAYBE_PackAndInstallExtension DISABLED_PackAndInstallExtension
263 #define MAYBE_PackAndInstallExtension PackAndInstallExtension
265 IN_PROC_BROWSER_TEST_F(
266 ExtensionCrxInstallerTest, MAYBE_PackAndInstallExtension) {
267 if (!FeatureSwitch::easy_off_store_install()->IsEnabled())
270 const int kNumDownloadsExpected = 1;
272 LOG(ERROR) << "PackAndInstallExtension: Packing extension";
273 base::FilePath crx_path = PackExtension(
274 test_data_dir_.AppendASCII("common/background_page"));
275 ASSERT_FALSE(crx_path.empty());
276 std::string crx_path_string(crx_path.value().begin(), crx_path.value().end());
277 GURL url = GURL(std::string("file:///").append(crx_path_string));
279 scoped_refptr<MockPromptProxy> mock_prompt =
280 CreateMockPromptProxyForBrowser(browser());
281 download_crx_util::SetMockInstallPromptForTesting(
282 mock_prompt->CreatePrompt());
284 LOG(ERROR) << "PackAndInstallExtension: Getting download manager";
285 content::DownloadManager* download_manager =
286 content::BrowserContext::GetDownloadManager(browser()->profile());
288 LOG(ERROR) << "PackAndInstallExtension: Setting observer";
289 scoped_ptr<content::DownloadTestObserver> observer(
290 new content::DownloadTestObserverTerminal(
291 download_manager, kNumDownloadsExpected,
292 content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT));
293 LOG(ERROR) << "PackAndInstallExtension: Navigating to URL";
294 ui_test_utils::NavigateToURLWithDisposition(browser(), url, CURRENT_TAB,
295 ui_test_utils::BROWSER_TEST_NONE);
297 EXPECT_TRUE(WaitForCrxInstallerDone());
298 LOG(ERROR) << "PackAndInstallExtension: Extension install";
299 EXPECT_TRUE(mock_prompt->confirmation_requested());
300 LOG(ERROR) << "PackAndInstallExtension: Extension install confirmed";
303 // Tests that scopes are only granted if |record_oauth2_grant_| on the prompt is
306 #define MAYBE_GrantScopes DISABLED_GrantScopes
308 #define MAYBE_GrantScopes GrantScopes
310 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, MAYBE_GrantScopes) {
311 EXPECT_NO_FATAL_FAILURE(CheckHasEmptyScopesAfterInstall("browsertest/scopes",
315 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, DoNotGrantScopes) {
316 EXPECT_NO_FATAL_FAILURE(CheckHasEmptyScopesAfterInstall("browsertest/scopes",
320 // Off-store install cannot yet be disabled on Aura.
321 #if defined(USE_AURA)
322 #define MAYBE_AllowOffStore DISABLED_AllowOffStore
324 #define MAYBE_AllowOffStore AllowOffStore
326 // Crashy: http://crbug.com/140893
327 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, DISABLED_AllowOffStore) {
328 ExtensionService* service = extensions::ExtensionSystem::Get(
329 browser()->profile())->extension_service();
330 const bool kTestData[] = {false, true};
332 for (size_t i = 0; i < arraysize(kTestData); ++i) {
333 scoped_refptr<MockPromptProxy> mock_prompt =
334 CreateMockPromptProxyForBrowser(browser());
336 scoped_refptr<CrxInstaller> crx_installer(
337 CrxInstaller::Create(service, mock_prompt->CreatePrompt()));
338 crx_installer->set_install_cause(
339 extension_misc::INSTALL_CAUSE_USER_DOWNLOAD);
342 crx_installer->set_off_store_install_allow_reason(
343 CrxInstaller::OffStoreInstallAllowedInTest);
346 crx_installer->InstallCrx(test_data_dir_.AppendASCII("good.crx"));
347 EXPECT_EQ(kTestData[i],
348 WaitForExtensionInstall()) << kTestData[i];
349 EXPECT_EQ(kTestData[i], mock_prompt->did_succeed());
350 EXPECT_EQ(kTestData[i], mock_prompt->confirmation_requested()) <<
353 EXPECT_EQ(string16(), mock_prompt->error()) << kTestData[i];
355 EXPECT_EQ(l10n_util::GetStringUTF16(
356 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE),
357 mock_prompt->error()) << kTestData[i];
362 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, HiDpiThemeTest) {
363 base::FilePath crx_path = test_data_dir_.AppendASCII("theme_hidpi_crx");
364 crx_path = crx_path.AppendASCII("theme_hidpi.crx");
366 ASSERT_TRUE(InstallExtension(crx_path,1));
368 const std::string extension_id("gllekhaobjnhgeagipipnkpmmmpchacm");
369 ExtensionService* service = extensions::ExtensionSystem::Get(
370 browser()->profile())->extension_service();
371 ASSERT_TRUE(service);
372 const extensions::Extension* extension =
373 service->GetExtensionById(extension_id, false);
374 ASSERT_TRUE(extension);
375 EXPECT_EQ(extension_id, extension->id());
377 UninstallExtension(extension_id);
378 EXPECT_FALSE(service->GetExtensionById(extension_id, false));
381 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest,
382 InstallDelayedUntilNextUpdate) {
383 const std::string extension_id("ldnnhddmnhbkjipkidpdiheffobcpfmf");
384 base::FilePath crx_path = test_data_dir_.AppendASCII("delayed_install");
385 ExtensionSystem* extension_system = extensions::ExtensionSystem::Get(
386 browser()->profile());
387 ExtensionService* service = extension_system->extension_service();
388 ASSERT_TRUE(service);
390 // Install version 1 of the test extension. This extension does not have
391 // a background page but does have a browser action.
392 ASSERT_TRUE(InstallExtension(crx_path.AppendASCII("v1.crx"), 1));
393 const extensions::Extension* extension =
394 service->GetExtensionById(extension_id, false);
395 ASSERT_TRUE(extension);
396 ASSERT_EQ(extension_id, extension->id());
397 ASSERT_EQ("1.0", extension->version()->GetString());
399 // Make test extension non-idle by opening the extension's browser action
400 // popup. This should cause the installation to be delayed.
401 content::WindowedNotificationObserver loading_observer(
402 chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
403 content::Source<Profile>(profile()));
404 BrowserActionTestUtil util(browser());
405 // There is only one extension, so just click the first browser action.
406 ASSERT_EQ(1, util.NumberOfBrowserActions());
408 loading_observer.Wait();
409 ExtensionHost* extension_host =
410 content::Details<ExtensionHost>(loading_observer.details()).ptr();
412 // Install version 2 of the extension and check that it is indeed delayed.
413 ASSERT_TRUE(UpdateExtensionWaitForIdle(
414 extension_id, crx_path.AppendASCII("v2.crx"), 0));
416 ASSERT_EQ(1u, service->delayed_installs()->size());
417 extension = service->GetExtensionById(extension_id, false);
418 ASSERT_EQ("1.0", extension->version()->GetString());
420 // Make the extension idle again by closing the popup. This should not trigger
421 //the delayed install.
422 content::WindowedNotificationObserver terminated_observer(
423 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
424 content::Source<content::RenderProcessHost>(
425 extension_host->render_process_host()));
426 extension_host->render_view_host()->ClosePage();
427 terminated_observer.Wait();
428 ASSERT_EQ(1u, service->delayed_installs()->size());
430 // Install version 3 of the extension. Because the extension is idle,
431 // this install should succeed.
432 ASSERT_TRUE(UpdateExtensionWaitForIdle(
433 extension_id, crx_path.AppendASCII("v3.crx"), 0));
434 extension = service->GetExtensionById(extension_id, false);
435 ASSERT_EQ("3.0", extension->version()->GetString());
437 // The version 2 delayed install should be cleaned up, and finishing
438 // delayed extension installation shouldn't break anything.
439 ASSERT_EQ(0u, service->delayed_installs()->size());
440 service->MaybeFinishDelayedInstallations();
441 extension = service->GetExtensionById(extension_id, false);
442 ASSERT_EQ("3.0", extension->version()->GetString());
445 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, Blacklist) {
446 scoped_refptr<FakeSafeBrowsingDatabaseManager> blacklist_db(
447 new FakeSafeBrowsingDatabaseManager(true));
448 Blacklist::ScopedDatabaseManagerForTest scoped_blacklist_db(blacklist_db);
450 blacklist_db->SetUnsafe("gllekhaobjnhgeagipipnkpmmmpchacm");
452 base::FilePath crx_path = test_data_dir_.AppendASCII("theme_hidpi_crx")
453 .AppendASCII("theme_hidpi.crx");
454 EXPECT_FALSE(InstallExtension(crx_path, 0));
457 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, NonStrictManifestCheck) {
458 scoped_refptr<MockPromptProxy> mock_prompt =
459 CreateMockPromptProxyForBrowser(browser());
461 // We want to simulate the case where the webstore sends a more recent
462 // version of the manifest, but the downloaded .crx file is old since
463 // the newly published version hasn't fully propagated to all the download
464 // servers yet. So load the v2 manifest, but then install the v1 crx file.
465 std::string id = "lhnaeclnpobnlbjbgogdanmhadigfnjp";
466 scoped_ptr<WebstoreInstaller::Approval> approval =
467 GetApproval("crx_installer/v2_no_permission_change/", id, false);
469 RunCrxInstaller(approval.get(), mock_prompt->CreatePrompt(),
470 test_data_dir_.AppendASCII("crx_installer/v1.crx"));
472 EXPECT_TRUE(mock_prompt->did_succeed());
475 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest, KioskOnlyTest) {
476 base::FilePath crx_path =
477 test_data_dir_.AppendASCII("kiosk/kiosk_only.crx");
478 EXPECT_FALSE(InstallExtension(crx_path, 0));
479 #if defined(OS_CHROMEOS)
480 // Simulate ChromeOS kiosk mode. |scoped_user_manager| will take over
481 // lifetime of |user_manager|.
482 chromeos::FakeUserManager* fake_user_manager =
483 new chromeos::FakeUserManager();
484 fake_user_manager->AddKioskAppUser("example@example.com");
485 fake_user_manager->LoginUser("example@example.com");
486 chromeos::ScopedUserManagerEnabler scoped_user_manager(fake_user_manager);
487 EXPECT_TRUE(InstallExtension(crx_path, 1));
491 } // namespace extensions