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
23using content::BrowserThread;
24using content::DownloadItem;
25using extensions::WebstoreInstaller;
26
27namespace download_crx_util {
28
29namespace {
30
31// Hold a mock ExtensionInstallPrompt object that will be used when the
32// download system opens a CRX.
33ExtensionInstallPrompt* 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.
38scoped_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.
67void SetMockInstallPromptForTesting(
68    scoped_ptr<ExtensionInstallPrompt> mock_prompt) {
69  mock_install_prompt_for_testing = mock_prompt.release();
70}
71
72scoped_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
116bool 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
130bool 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