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 // Download code which handles CRX files (extensions, themes, apps, ...).
6
7 #include "chrome/browser/download/download_crx_util.h"
8
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/crx_installer.h"
11 #include "chrome/browser/extensions/extension_install_prompt.h"
12 #include "chrome/browser/extensions/extension_management.h"
13 #include "chrome/browser/extensions/webstore_installer.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser_finder.h"
16 #include "chrome/browser/ui/host_desktop.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "content/public/browser/download_item.h"
19 #include "content/public/browser/notification_service.h"
20 #include "extensions/browser/extension_system.h"
21 #include "extensions/common/user_script.h"
22
23 using content::BrowserThread;
24 using content::DownloadItem;
25 using extensions::WebstoreInstaller;
26
27 namespace download_crx_util {
28
29 namespace {
30
31 // Hold a mock ExtensionInstallPrompt object that will be used when the
32 // download system opens a CRX.
33 ExtensionInstallPrompt* mock_install_prompt_for_testing = NULL;
34
35 // Called to get an extension install UI object. In tests, will return
36 // a mock if the test calls download_util::SetMockInstallPromptForTesting()
37 // to set one.
CreateExtensionInstallPrompt(Profile * profile,const DownloadItem & download_item)38 scoped_ptr<ExtensionInstallPrompt> CreateExtensionInstallPrompt(
39 Profile* profile,
40 const DownloadItem& download_item) {
41 // Use a mock if one is present. Otherwise, create a real extensions
42 // install UI.
43 if (mock_install_prompt_for_testing) {
44 ExtensionInstallPrompt* result = mock_install_prompt_for_testing;
45 mock_install_prompt_for_testing = NULL;
46 return scoped_ptr<ExtensionInstallPrompt>(result);
47 } else {
48 content::WebContents* web_contents = download_item.GetWebContents();
49 if (!web_contents) {
50 chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop();
51 Browser* browser = chrome::FindLastActiveWithProfile(profile,
52 active_desktop);
53 if (!browser)
54 browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED,
55 profile, active_desktop));
56 web_contents = browser->tab_strip_model()->GetActiveWebContents();
57 }
58 return scoped_ptr<ExtensionInstallPrompt>(
59 new ExtensionInstallPrompt(web_contents));
60 }
61 }
62
63 } // namespace
64
65 // Tests can call this method to inject a mock ExtensionInstallPrompt
66 // to be used to confirm permissions on a downloaded CRX.
SetMockInstallPromptForTesting(scoped_ptr<ExtensionInstallPrompt> mock_prompt)67 void SetMockInstallPromptForTesting(
68 scoped_ptr<ExtensionInstallPrompt> mock_prompt) {
69 mock_install_prompt_for_testing = mock_prompt.release();
70 }
71
CreateCrxInstaller(Profile * profile,const content::DownloadItem & download_item)72 scoped_refptr<extensions::CrxInstaller> CreateCrxInstaller(
73 Profile* profile,
74 const content::DownloadItem& download_item) {
75 ExtensionService* service = extensions::ExtensionSystem::Get(profile)->
76 extension_service();
77 CHECK(service);
78
79 scoped_refptr<extensions::CrxInstaller> installer(
80 extensions::CrxInstaller::Create(
81 service,
82 CreateExtensionInstallPrompt(profile, download_item),
83 WebstoreInstaller::GetAssociatedApproval(download_item)));
84
85 installer->set_error_on_unsupported_requirements(true);
86 installer->set_delete_source(true);
87 installer->set_install_cause(extension_misc::INSTALL_CAUSE_USER_DOWNLOAD);
88 installer->set_original_mime_type(download_item.GetOriginalMimeType());
89 installer->set_apps_require_extension_mime_type(true);
90
91 return installer;
92 }
93
OpenChromeExtension(Profile * profile,const DownloadItem & download_item)94 scoped_refptr<extensions::CrxInstaller> OpenChromeExtension(
95 Profile* profile,
96 const DownloadItem& download_item) {
97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
98
99 scoped_refptr<extensions::CrxInstaller> installer(
100 CreateCrxInstaller(profile, download_item));
101
102 if (OffStoreInstallAllowedByPrefs(profile, download_item)) {
103 installer->set_off_store_install_allow_reason(
104 extensions::CrxInstaller::OffStoreInstallAllowedBecausePref);
105 }
106
107 if (extensions::UserScript::IsURLUserScript(download_item.GetURL(),
108 download_item.GetMimeType())) {
109 installer->InstallUserScript(download_item.GetFullPath(),
110 download_item.GetURL());
111 } else {
112 DCHECK(!WebstoreInstaller::GetAssociatedApproval(download_item));
113 installer->InstallCrx(download_item.GetFullPath());
114 }
115
116 return installer;
117 }
118
IsExtensionDownload(const DownloadItem & download_item)119 bool IsExtensionDownload(const DownloadItem& download_item) {
120 if (download_item.GetTargetDisposition() ==
121 DownloadItem::TARGET_DISPOSITION_PROMPT)
122 return false;
123
124 if (download_item.GetMimeType() == extensions::Extension::kMimeType ||
125 extensions::UserScript::IsURLUserScript(download_item.GetURL(),
126 download_item.GetMimeType())) {
127 return true;
128 } else {
129 return false;
130 }
131 }
132
OffStoreInstallAllowedByPrefs(Profile * profile,const DownloadItem & item)133 bool OffStoreInstallAllowedByPrefs(Profile* profile, const DownloadItem& item) {
134 // TODO(aa): RefererURL is cleared in some cases, for example when going
135 // between secure and non-secure URLs. It would be better if DownloadItem
136 // tracked the initiating page explicitly.
137 return extensions::ExtensionManagementFactory::GetForBrowserContext(profile)
138 ->IsOffstoreInstallAllowed(item.GetURL(), item.GetReferrerUrl());
139 }
140
141 } // namespace download_crx_util
142