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_service.h" 13 #include "chrome/browser/extensions/extension_system.h" 14 #include "chrome/browser/extensions/webstore_installer.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/ui/browser_finder.h" 17 #include "chrome/browser/ui/host_desktop.h" 18 #include "chrome/browser/ui/tabs/tab_strip_model.h" 19 #include "content/public/browser/download_item.h" 20 #include "content/public/browser/notification_service.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. 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. 67 void SetMockInstallPromptForTesting( 68 scoped_ptr<ExtensionInstallPrompt> mock_prompt) { 69 mock_install_prompt_for_testing = mock_prompt.release(); 70 } 71 72 scoped_refptr<extensions::CrxInstaller> OpenChromeExtension( 73 Profile* profile, 74 const DownloadItem& download_item) { 75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 76 77 ExtensionService* service = extensions::ExtensionSystem::Get(profile)-> 78 extension_service(); 79 CHECK(service); 80 81 scoped_refptr<extensions::CrxInstaller> installer( 82 extensions::CrxInstaller::Create( 83 service, 84 CreateExtensionInstallPrompt(profile, download_item), 85 WebstoreInstaller::GetAssociatedApproval(download_item))); 86 87 installer->set_error_on_unsupported_requirements(true); 88 installer->set_delete_source(true); 89 installer->set_install_cause(extension_misc::INSTALL_CAUSE_USER_DOWNLOAD); 90 91 if (OffStoreInstallAllowedByPrefs(profile, download_item)) { 92 installer->set_off_store_install_allow_reason( 93 extensions::CrxInstaller::OffStoreInstallAllowedBecausePref); 94 } 95 96 if (extensions::UserScript::IsURLUserScript(download_item.GetURL(), 97 download_item.GetMimeType())) { 98 installer->InstallUserScript(download_item.GetFullPath(), 99 download_item.GetURL()); 100 } else { 101 bool is_gallery_download = 102 WebstoreInstaller::GetAssociatedApproval(download_item) != NULL; 103 installer->set_original_mime_type(download_item.GetOriginalMimeType()); 104 installer->set_apps_require_extension_mime_type(true); 105 installer->set_download_url(download_item.GetURL()); 106 installer->set_is_gallery_install(is_gallery_download); 107 if (is_gallery_download) 108 installer->set_original_download_url(download_item.GetOriginalUrl()); 109 installer->set_allow_silent_install(is_gallery_download); 110 installer->InstallCrx(download_item.GetFullPath()); 111 } 112 113 return installer; 114 } 115 116 bool IsExtensionDownload(const DownloadItem& download_item) { 117 if (download_item.GetTargetDisposition() == 118 DownloadItem::TARGET_DISPOSITION_PROMPT) 119 return false; 120 121 if (download_item.GetMimeType() == extensions::Extension::kMimeType || 122 extensions::UserScript::IsURLUserScript(download_item.GetURL(), 123 download_item.GetMimeType())) { 124 return true; 125 } else { 126 return false; 127 } 128 } 129 130 bool OffStoreInstallAllowedByPrefs(Profile* profile, const DownloadItem& item) { 131 ExtensionService* service = extensions::ExtensionSystem::Get( 132 profile)->extension_service(); 133 if (!service) 134 return false; 135 136 extensions::ExtensionPrefs* prefs = service->extension_prefs(); 137 CHECK(prefs); 138 139 extensions::URLPatternSet url_patterns = prefs->GetAllowedInstallSites(); 140 141 if (!url_patterns.MatchesURL(item.GetURL())) 142 return false; 143 144 // The referrer URL must also be whitelisted, unless the URL has the file 145 // scheme (there's no referrer for those URLs). 146 // TODO(aa): RefererURL is cleared in some cases, for example when going 147 // between secure and non-secure URLs. It would be better if DownloadItem 148 // tracked the initiating page explicitly. 149 return url_patterns.MatchesURL(item.GetReferrerUrl()) || 150 item.GetURL().SchemeIsFile(); 151 } 152 153 } // namespace download_crx_util 154