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_file_util.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "content/public/browser/download_manager.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/test/browser_test_utils.h"
23 #include "content/public/test/download_test_observer.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/feature_switch.h"
26 #include "extensions/common/permissions/permission_set.h"
27 #include "extensions/common/switches.h"
28 #include "grit/generated_resources.h"
29 #include "ui/base/l10n/l10n_util.h"
31 #if defined(OS_CHROMEOS)
32 #include "chrome/browser/chromeos/login/fake_user_manager.h"
33 #include "chrome/browser/chromeos/login/user_manager.h"
38 namespace extensions
{
42 class MockInstallPrompt
;
44 // This class holds information about things that happen with a
45 // MockInstallPrompt. We create the MockInstallPrompt but need to pass
46 // ownership of it to CrxInstaller, so it isn't safe to hang this data on
47 // MockInstallPrompt itself becuase we can't guarantee it's lifetime.
48 class MockPromptProxy
:
49 public base::RefCountedThreadSafe
<MockPromptProxy
> {
51 explicit MockPromptProxy(content::WebContents
* web_contents
);
53 bool did_succeed() const { return !extension_id_
.empty(); }
54 const std::string
& extension_id() { return extension_id_
; }
55 bool confirmation_requested() const { return confirmation_requested_
; }
56 const base::string16
& error() const { return error_
; }
58 // To have any effect, this should be called before CreatePrompt.
59 void set_record_oauth2_grant(bool record_oauth2_grant
) {
60 record_oauth2_grant_
.reset(new bool(record_oauth2_grant
));
63 void set_extension_id(const std::string
& id
) { extension_id_
= id
; }
64 void set_confirmation_requested() { confirmation_requested_
= true; }
65 void set_error(const base::string16
& error
) { error_
= error
; }
67 scoped_ptr
<ExtensionInstallPrompt
> CreatePrompt();
70 friend class base::RefCountedThreadSafe
<MockPromptProxy
>;
71 virtual ~MockPromptProxy();
73 // Data used to create a prompt.
74 content::WebContents
* web_contents_
;
75 scoped_ptr
<bool> record_oauth2_grant_
;
77 // Data reported back to us by the prompt we created.
78 bool confirmation_requested_
;
79 std::string extension_id_
;
80 base::string16 error_
;
83 class MockInstallPrompt
: public ExtensionInstallPrompt
{
85 MockInstallPrompt(content::WebContents
* web_contents
,
86 MockPromptProxy
* proxy
) :
87 ExtensionInstallPrompt(web_contents
),
90 void set_record_oauth2_grant(bool record
) { record_oauth2_grant_
= record
; }
92 // Overriding some of the ExtensionInstallUI API.
93 virtual void ConfirmInstall(
95 const Extension
* extension
,
96 const ShowDialogCallback
& show_dialog_callback
) OVERRIDE
{
97 proxy_
->set_confirmation_requested();
98 delegate
->InstallUIProceed();
100 virtual void OnInstallSuccess(const Extension
* extension
,
101 SkBitmap
* icon
) OVERRIDE
{
102 proxy_
->set_extension_id(extension
->id());
103 base::MessageLoopForUI::current()->Quit();
105 virtual void OnInstallFailure(const CrxInstallerError
& error
) OVERRIDE
{
106 proxy_
->set_error(error
.message());
107 base::MessageLoopForUI::current()->Quit();
111 scoped_refptr
<MockPromptProxy
> proxy_
;
115 MockPromptProxy::MockPromptProxy(content::WebContents
* web_contents
) :
116 web_contents_(web_contents
),
117 confirmation_requested_(false) {
120 MockPromptProxy::~MockPromptProxy() {}
122 scoped_ptr
<ExtensionInstallPrompt
> MockPromptProxy::CreatePrompt() {
123 scoped_ptr
<MockInstallPrompt
> prompt(
124 new MockInstallPrompt(web_contents_
, this));
125 if (record_oauth2_grant_
.get())
126 prompt
->set_record_oauth2_grant(*record_oauth2_grant_
.get());
127 return prompt
.PassAs
<ExtensionInstallPrompt
>();
131 scoped_refptr
<MockPromptProxy
> CreateMockPromptProxyForBrowser(
133 return new MockPromptProxy(
134 browser
->tab_strip_model()->GetActiveWebContents());
139 class ExtensionCrxInstallerTest
: public ExtensionBrowserTest
{
141 scoped_ptr
<WebstoreInstaller::Approval
> GetApproval(
142 const char* manifest_dir
,
143 const std::string
& id
,
144 bool strict_manifest_checks
) {
145 scoped_ptr
<WebstoreInstaller::Approval
> result
;
147 base::FilePath ext_path
= test_data_dir_
.AppendASCII(manifest_dir
);
149 scoped_ptr
<base::DictionaryValue
> parsed_manifest(
150 extension_file_util::LoadManifest(ext_path
, &error
));
151 if (!parsed_manifest
.get() || !error
.empty())
152 return result
.Pass();
154 return WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
155 browser()->profile(),
157 parsed_manifest
.Pass(),
158 strict_manifest_checks
);
161 void RunCrxInstaller(const WebstoreInstaller::Approval
* approval
,
162 scoped_ptr
<ExtensionInstallPrompt
> prompt
,
163 const base::FilePath
& crx_path
) {
164 ExtensionService
* service
= extensions::ExtensionSystem::Get(
165 browser()->profile())->extension_service();
166 scoped_refptr
<CrxInstaller
> installer(
167 CrxInstaller::Create(service
, prompt
.Pass(), approval
));
168 installer
->set_allow_silent_install(true);
169 installer
->set_is_gallery_install(true);
170 installer
->InstallCrx(crx_path
);
171 content::RunMessageLoop();
174 // Installs a crx from |crx_relpath| (a path relative to the extension test
175 // data dir) with expected id |id|.
176 void InstallWithPrompt(const char* ext_relpath
,
177 const std::string
& id
,
178 scoped_refptr
<MockPromptProxy
> mock_install_prompt
) {
179 base::FilePath ext_path
= test_data_dir_
.AppendASCII(ext_relpath
);
181 scoped_ptr
<WebstoreInstaller::Approval
> approval
;
183 approval
= GetApproval(ext_relpath
, id
, true);
185 base::FilePath crx_path
= PackExtension(ext_path
);
186 EXPECT_FALSE(crx_path
.empty());
187 RunCrxInstaller(approval
.get(), mock_install_prompt
->CreatePrompt(),
190 EXPECT_TRUE(mock_install_prompt
->did_succeed());
193 // Installs an extension and checks that it has scopes granted IFF
194 // |record_oauth2_grant| is true.
195 void CheckHasEmptyScopesAfterInstall(const std::string
& ext_relpath
,
196 bool record_oauth2_grant
) {
197 CommandLine::ForCurrentProcess()->AppendSwitch(
198 switches::kEnableExperimentalExtensionApis
);
200 ExtensionService
* service
= extensions::ExtensionSystem::Get(
201 browser()->profile())->extension_service();
203 scoped_refptr
<MockPromptProxy
> mock_prompt
=
204 CreateMockPromptProxyForBrowser(browser());
206 mock_prompt
->set_record_oauth2_grant(record_oauth2_grant
);
207 InstallWithPrompt("browsertest/scopes", std::string(), mock_prompt
);
209 scoped_refptr
<PermissionSet
> permissions
=
210 service
->extension_prefs()->GetGrantedPermissions(
211 mock_prompt
->extension_id());
212 ASSERT_TRUE(permissions
.get());
216 #if defined(OS_CHROMEOS)
217 #define MAYBE_Whitelisting DISABLED_Whitelisting
219 #define MAYBE_Whitelisting Whitelisting
221 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, MAYBE_Whitelisting
) {
222 std::string id
= "hdgllgikmikobbofgnabhfimcfoopgnd";
223 ExtensionService
* service
= extensions::ExtensionSystem::Get(
224 browser()->profile())->extension_service();
226 // Even whitelisted extensions with NPAPI should not prompt.
227 scoped_refptr
<MockPromptProxy
> mock_prompt
=
228 CreateMockPromptProxyForBrowser(browser());
229 InstallWithPrompt("uitest/plugins", id
, mock_prompt
);
230 EXPECT_FALSE(mock_prompt
->confirmation_requested());
231 EXPECT_TRUE(service
->GetExtensionById(id
, false));
234 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
,
235 GalleryInstallGetsExperimental
) {
236 // We must modify the command line temporarily in order to pack an extension
237 // that requests the experimental permission.
238 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
239 CommandLine old_command_line
= *command_line
;
240 command_line
->AppendSwitch(switches::kEnableExperimentalExtensionApis
);
241 base::FilePath crx_path
= PackExtension(
242 test_data_dir_
.AppendASCII("experimental"));
243 ASSERT_FALSE(crx_path
.empty());
245 // Now reset the command line so that we are testing specifically whether
246 // installing from webstore enables experimental permissions.
247 *(CommandLine::ForCurrentProcess()) = old_command_line
;
249 EXPECT_FALSE(InstallExtension(crx_path
, 0));
250 EXPECT_TRUE(InstallExtensionFromWebstore(crx_path
, 1));
253 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, PlatformAppCrx
) {
254 CommandLine::ForCurrentProcess()->AppendSwitch(
255 switches::kEnableExperimentalExtensionApis
);
256 EXPECT_TRUE(InstallExtension(
257 test_data_dir_
.AppendASCII("minimal_platform_app.crx"), 1));
260 // http://crbug.com/136397
261 #if defined(OS_CHROMEOS)
262 #define MAYBE_PackAndInstallExtension DISABLED_PackAndInstallExtension
264 #define MAYBE_PackAndInstallExtension PackAndInstallExtension
266 IN_PROC_BROWSER_TEST_F(
267 ExtensionCrxInstallerTest
, MAYBE_PackAndInstallExtension
) {
268 if (!FeatureSwitch::easy_off_store_install()->IsEnabled())
271 const int kNumDownloadsExpected
= 1;
273 LOG(ERROR
) << "PackAndInstallExtension: Packing extension";
274 base::FilePath crx_path
= PackExtension(
275 test_data_dir_
.AppendASCII("common/background_page"));
276 ASSERT_FALSE(crx_path
.empty());
277 std::string
crx_path_string(crx_path
.value().begin(), crx_path
.value().end());
278 GURL url
= GURL(std::string("file:///").append(crx_path_string
));
280 scoped_refptr
<MockPromptProxy
> mock_prompt
=
281 CreateMockPromptProxyForBrowser(browser());
282 download_crx_util::SetMockInstallPromptForTesting(
283 mock_prompt
->CreatePrompt());
285 LOG(ERROR
) << "PackAndInstallExtension: Getting download manager";
286 content::DownloadManager
* download_manager
=
287 content::BrowserContext::GetDownloadManager(browser()->profile());
289 LOG(ERROR
) << "PackAndInstallExtension: Setting observer";
290 scoped_ptr
<content::DownloadTestObserver
> observer(
291 new content::DownloadTestObserverTerminal(
292 download_manager
, kNumDownloadsExpected
,
293 content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT
));
294 LOG(ERROR
) << "PackAndInstallExtension: Navigating to URL";
295 ui_test_utils::NavigateToURLWithDisposition(browser(), url
, CURRENT_TAB
,
296 ui_test_utils::BROWSER_TEST_NONE
);
298 EXPECT_TRUE(WaitForCrxInstallerDone());
299 LOG(ERROR
) << "PackAndInstallExtension: Extension install";
300 EXPECT_TRUE(mock_prompt
->confirmation_requested());
301 LOG(ERROR
) << "PackAndInstallExtension: Extension install confirmed";
304 // Tests that scopes are only granted if |record_oauth2_grant_| on the prompt is
307 #define MAYBE_GrantScopes DISABLED_GrantScopes
309 #define MAYBE_GrantScopes GrantScopes
311 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, MAYBE_GrantScopes
) {
312 EXPECT_NO_FATAL_FAILURE(CheckHasEmptyScopesAfterInstall("browsertest/scopes",
316 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, DoNotGrantScopes
) {
317 EXPECT_NO_FATAL_FAILURE(CheckHasEmptyScopesAfterInstall("browsertest/scopes",
321 // Off-store install cannot yet be disabled on Aura.
322 #if defined(USE_AURA)
323 #define MAYBE_AllowOffStore DISABLED_AllowOffStore
325 #define MAYBE_AllowOffStore AllowOffStore
327 // Crashy: http://crbug.com/140893
328 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, DISABLED_AllowOffStore
) {
329 ExtensionService
* service
= extensions::ExtensionSystem::Get(
330 browser()->profile())->extension_service();
331 const bool kTestData
[] = {false, true};
333 for (size_t i
= 0; i
< arraysize(kTestData
); ++i
) {
334 scoped_refptr
<MockPromptProxy
> mock_prompt
=
335 CreateMockPromptProxyForBrowser(browser());
337 scoped_refptr
<CrxInstaller
> crx_installer(
338 CrxInstaller::Create(service
, mock_prompt
->CreatePrompt()));
339 crx_installer
->set_install_cause(
340 extension_misc::INSTALL_CAUSE_USER_DOWNLOAD
);
343 crx_installer
->set_off_store_install_allow_reason(
344 CrxInstaller::OffStoreInstallAllowedInTest
);
347 crx_installer
->InstallCrx(test_data_dir_
.AppendASCII("good.crx"));
348 EXPECT_EQ(kTestData
[i
],
349 WaitForExtensionInstall()) << kTestData
[i
];
350 EXPECT_EQ(kTestData
[i
], mock_prompt
->did_succeed());
351 EXPECT_EQ(kTestData
[i
], mock_prompt
->confirmation_requested()) <<
354 EXPECT_EQ(base::string16(), mock_prompt
->error()) << kTestData
[i
];
356 EXPECT_EQ(l10n_util::GetStringUTF16(
357 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE
),
358 mock_prompt
->error()) << kTestData
[i
];
363 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, HiDpiThemeTest
) {
364 base::FilePath crx_path
= test_data_dir_
.AppendASCII("theme_hidpi_crx");
365 crx_path
= crx_path
.AppendASCII("theme_hidpi.crx");
367 ASSERT_TRUE(InstallExtension(crx_path
,1));
369 const std::string
extension_id("gllekhaobjnhgeagipipnkpmmmpchacm");
370 ExtensionService
* service
= extensions::ExtensionSystem::Get(
371 browser()->profile())->extension_service();
372 ASSERT_TRUE(service
);
373 const extensions::Extension
* extension
=
374 service
->GetExtensionById(extension_id
, false);
375 ASSERT_TRUE(extension
);
376 EXPECT_EQ(extension_id
, extension
->id());
378 UninstallExtension(extension_id
);
379 EXPECT_FALSE(service
->GetExtensionById(extension_id
, false));
382 // See http://crbug.com/315299.
384 #define MAYBE_InstallDelayedUntilNextUpdate \
385 DISABLED_InstallDelayedUntilNextUpdate
387 #define MAYBE_InstallDelayedUntilNextUpdate InstallDelayedUntilNextUpdate
388 #endif // defined(OS_WIN)
389 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
,
390 MAYBE_InstallDelayedUntilNextUpdate
) {
391 const std::string
extension_id("ldnnhddmnhbkjipkidpdiheffobcpfmf");
392 base::FilePath crx_path
= test_data_dir_
.AppendASCII("delayed_install");
393 ExtensionSystem
* extension_system
= extensions::ExtensionSystem::Get(
394 browser()->profile());
395 ExtensionService
* service
= extension_system
->extension_service();
396 ASSERT_TRUE(service
);
398 // Install version 1 of the test extension. This extension does not have
399 // a background page but does have a browser action.
400 ASSERT_TRUE(InstallExtension(crx_path
.AppendASCII("v1.crx"), 1));
401 const extensions::Extension
* extension
=
402 service
->GetExtensionById(extension_id
, false);
403 ASSERT_TRUE(extension
);
404 ASSERT_EQ(extension_id
, extension
->id());
405 ASSERT_EQ("1.0", extension
->version()->GetString());
407 // Make test extension non-idle by opening the extension's browser action
408 // popup. This should cause the installation to be delayed.
409 content::WindowedNotificationObserver
loading_observer(
410 chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING
,
411 content::Source
<Profile
>(profile()));
412 BrowserActionTestUtil
util(browser());
413 // There is only one extension, so just click the first browser action.
414 ASSERT_EQ(1, util
.NumberOfBrowserActions());
416 loading_observer
.Wait();
417 ExtensionHost
* extension_host
=
418 content::Details
<ExtensionHost
>(loading_observer
.details()).ptr();
420 // Install version 2 of the extension and check that it is indeed delayed.
421 ASSERT_TRUE(UpdateExtensionWaitForIdle(
422 extension_id
, crx_path
.AppendASCII("v2.crx"), 0));
424 ASSERT_EQ(1u, service
->delayed_installs()->size());
425 extension
= service
->GetExtensionById(extension_id
, false);
426 ASSERT_EQ("1.0", extension
->version()->GetString());
428 // Make the extension idle again by closing the popup. This should not trigger
429 //the delayed install.
430 content::RenderProcessHostWatcher
terminated_observer(
431 extension_host
->render_process_host(),
432 content::RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION
);
433 extension_host
->render_view_host()->ClosePage();
434 terminated_observer
.Wait();
435 ASSERT_EQ(1u, service
->delayed_installs()->size());
437 // Install version 3 of the extension. Because the extension is idle,
438 // this install should succeed.
439 ASSERT_TRUE(UpdateExtensionWaitForIdle(
440 extension_id
, crx_path
.AppendASCII("v3.crx"), 0));
441 extension
= service
->GetExtensionById(extension_id
, false);
442 ASSERT_EQ("3.0", extension
->version()->GetString());
444 // The version 2 delayed install should be cleaned up, and finishing
445 // delayed extension installation shouldn't break anything.
446 ASSERT_EQ(0u, service
->delayed_installs()->size());
447 service
->MaybeFinishDelayedInstallations();
448 extension
= service
->GetExtensionById(extension_id
, false);
449 ASSERT_EQ("3.0", extension
->version()->GetString());
452 #if defined(FULL_SAFE_BROWSING)
453 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, Blacklist
) {
454 scoped_refptr
<FakeSafeBrowsingDatabaseManager
> blacklist_db(
455 new FakeSafeBrowsingDatabaseManager(true));
456 Blacklist::ScopedDatabaseManagerForTest
scoped_blacklist_db(blacklist_db
);
458 blacklist_db
->SetUnsafe("gllekhaobjnhgeagipipnkpmmmpchacm");
460 base::FilePath crx_path
= test_data_dir_
.AppendASCII("theme_hidpi_crx")
461 .AppendASCII("theme_hidpi.crx");
462 EXPECT_FALSE(InstallExtension(crx_path
, 0));
466 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, NonStrictManifestCheck
) {
467 scoped_refptr
<MockPromptProxy
> mock_prompt
=
468 CreateMockPromptProxyForBrowser(browser());
470 // We want to simulate the case where the webstore sends a more recent
471 // version of the manifest, but the downloaded .crx file is old since
472 // the newly published version hasn't fully propagated to all the download
473 // servers yet. So load the v2 manifest, but then install the v1 crx file.
474 std::string id
= "lhnaeclnpobnlbjbgogdanmhadigfnjp";
475 scoped_ptr
<WebstoreInstaller::Approval
> approval
=
476 GetApproval("crx_installer/v2_no_permission_change/", id
, false);
478 RunCrxInstaller(approval
.get(), mock_prompt
->CreatePrompt(),
479 test_data_dir_
.AppendASCII("crx_installer/v1.crx"));
481 EXPECT_TRUE(mock_prompt
->did_succeed());
484 IN_PROC_BROWSER_TEST_F(ExtensionCrxInstallerTest
, KioskOnlyTest
) {
485 base::FilePath crx_path
=
486 test_data_dir_
.AppendASCII("kiosk/kiosk_only.crx");
487 EXPECT_FALSE(InstallExtension(crx_path
, 0));
488 #if defined(OS_CHROMEOS)
489 // Simulate ChromeOS kiosk mode. |scoped_user_manager| will take over
490 // lifetime of |user_manager|.
491 chromeos::FakeUserManager
* fake_user_manager
=
492 new chromeos::FakeUserManager();
493 fake_user_manager
->AddKioskAppUser("example@example.com");
494 fake_user_manager
->LoginUser("example@example.com");
495 chromeos::ScopedUserManagerEnabler
scoped_user_manager(fake_user_manager
);
496 EXPECT_TRUE(InstallExtension(crx_path
, 1));
500 } // namespace extensions