// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // Download code which handles CRX files (extensions, themes, apps, ...). #include "chrome/browser/download/download_crx_util.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_install_prompt.h" #include "chrome/browser/extensions/webstore_installer.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/host_desktop.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "content/public/browser/download_item.h" #include "content/public/browser/notification_service.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_system.h" #include "extensions/common/user_script.h" using content::BrowserThread; using content::DownloadItem; using extensions::WebstoreInstaller; namespace download_crx_util { namespace { // Hold a mock ExtensionInstallPrompt object that will be used when the // download system opens a CRX. ExtensionInstallPrompt* mock_install_prompt_for_testing = NULL; // Called to get an extension install UI object. In tests, will return // a mock if the test calls download_util::SetMockInstallPromptForTesting() // to set one. scoped_ptr<ExtensionInstallPrompt> CreateExtensionInstallPrompt( Profile* profile, const DownloadItem& download_item) { // Use a mock if one is present. Otherwise, create a real extensions // install UI. if (mock_install_prompt_for_testing) { ExtensionInstallPrompt* result = mock_install_prompt_for_testing; mock_install_prompt_for_testing = NULL; return scoped_ptr<ExtensionInstallPrompt>(result); } else { content::WebContents* web_contents = download_item.GetWebContents(); if (!web_contents) { chrome::HostDesktopType active_desktop = chrome::GetActiveDesktop(); Browser* browser = chrome::FindLastActiveWithProfile(profile, active_desktop); if (!browser) browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, profile, active_desktop)); web_contents = browser->tab_strip_model()->GetActiveWebContents(); } return scoped_ptr<ExtensionInstallPrompt>( new ExtensionInstallPrompt(web_contents)); } } } // namespace // Tests can call this method to inject a mock ExtensionInstallPrompt // to be used to confirm permissions on a downloaded CRX. void SetMockInstallPromptForTesting( scoped_ptr<ExtensionInstallPrompt> mock_prompt) { mock_install_prompt_for_testing = mock_prompt.release(); } scoped_refptr<extensions::CrxInstaller> CreateCrxInstaller( Profile* profile, const content::DownloadItem& download_item) { ExtensionService* service = extensions::ExtensionSystem::Get(profile)-> extension_service(); CHECK(service); scoped_refptr<extensions::CrxInstaller> installer( extensions::CrxInstaller::Create( service, CreateExtensionInstallPrompt(profile, download_item), WebstoreInstaller::GetAssociatedApproval(download_item))); installer->set_error_on_unsupported_requirements(true); installer->set_delete_source(true); installer->set_install_cause(extension_misc::INSTALL_CAUSE_USER_DOWNLOAD); installer->set_original_mime_type(download_item.GetOriginalMimeType()); installer->set_apps_require_extension_mime_type(true); return installer; } scoped_refptr<extensions::CrxInstaller> OpenChromeExtension( Profile* profile, const DownloadItem& download_item) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); scoped_refptr<extensions::CrxInstaller> installer( CreateCrxInstaller(profile, download_item)); if (OffStoreInstallAllowedByPrefs(profile, download_item)) { installer->set_off_store_install_allow_reason( extensions::CrxInstaller::OffStoreInstallAllowedBecausePref); } if (extensions::UserScript::IsURLUserScript(download_item.GetURL(), download_item.GetMimeType())) { installer->InstallUserScript(download_item.GetFullPath(), download_item.GetURL()); } else { DCHECK(!WebstoreInstaller::GetAssociatedApproval(download_item)); installer->InstallCrx(download_item.GetFullPath()); } return installer; } bool IsExtensionDownload(const DownloadItem& download_item) { if (download_item.GetTargetDisposition() == DownloadItem::TARGET_DISPOSITION_PROMPT) return false; if (download_item.GetMimeType() == extensions::Extension::kMimeType || extensions::UserScript::IsURLUserScript(download_item.GetURL(), download_item.GetMimeType())) { return true; } else { return false; } } bool OffStoreInstallAllowedByPrefs(Profile* profile, const DownloadItem& item) { extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile); CHECK(prefs); extensions::URLPatternSet url_patterns = prefs->GetAllowedInstallSites(); if (!url_patterns.MatchesURL(item.GetURL())) return false; // The referrer URL must also be whitelisted, unless the URL has the file // scheme (there's no referrer for those URLs). // TODO(aa): RefererURL is cleared in some cases, for example when going // between secure and non-secure URLs. It would be better if DownloadItem // tracked the initiating page explicitly. return url_patterns.MatchesURL(item.GetReferrerUrl()) || item.GetURL().SchemeIsFile(); } } // namespace download_crx_util