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. 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> 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 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 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 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