blob: 41c02771f7d3e0bf521ff81be1d53806a435214e [file] [log] [blame]
[email protected]609dc3f2012-01-10 00:04:051// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]655b2b1a2011-10-13 17:13:062// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/webstore_installer.h"
6
[email protected]1dba4b92012-01-26 02:59:277#include "base/basictypes.h"
[email protected]f66a50a2011-11-02 23:53:468#include "base/bind.h"
9#include "base/command_line.h"
10#include "base/file_util.h"
[email protected]1dba4b92012-01-26 02:59:2711#include "base/rand_util.h"
[email protected]f66a50a2011-11-02 23:53:4612#include "base/stringprintf.h"
[email protected]99dfecd2011-10-18 01:11:5013#include "base/string_util.h"
[email protected]1dba4b92012-01-26 02:59:2714#include "base/string_number_conversions.h"
[email protected]fc670822011-12-17 09:33:4915#include "base/utf_string_conversions.h"
[email protected]655b2b1a2011-10-13 17:13:0616#include "chrome/browser/browser_process.h"
[email protected]609dc3f2012-01-10 00:04:0517#include "chrome/browser/download/download_prefs.h"
[email protected]14365e32012-01-24 01:12:2218#include "chrome/browser/download/download_util.h"
[email protected]655b2b1a2011-10-13 17:13:0619#include "chrome/browser/extensions/crx_installer.h"
20#include "chrome/browser/profiles/profile.h"
21#include "chrome/browser/tabs/tab_strip_model.h"
22#include "chrome/browser/ui/browser_list.h"
23#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
24#include "chrome/common/chrome_notification_types.h"
[email protected]f66a50a2011-11-02 23:53:4625#include "chrome/common/chrome_switches.h"
[email protected]655b2b1a2011-10-13 17:13:0626#include "chrome/common/extensions/extension.h"
[email protected]99dfecd2011-10-18 01:11:5027#include "chrome/common/extensions/extension_constants.h"
[email protected]f66a50a2011-11-02 23:53:4628#include "content/browser/download/download_types.h"
[email protected]f66a50a2011-11-02 23:53:4629#include "content/public/browser/browser_thread.h"
[email protected]e582fdd2011-12-20 16:48:1730#include "content/public/browser/download_file.h"
31#include "content/public/browser/download_manager.h"
[email protected]cdcb1dee2012-01-04 00:46:2032#include "content/public/browser/navigation_controller.h"
[email protected]ad23a092011-12-28 07:02:0433#include "content/public/browser/navigation_entry.h"
[email protected]86ab86b2011-10-19 03:07:5534#include "content/public/browser/notification_details.h"
35#include "content/public/browser/notification_source.h"
[email protected]655b2b1a2011-10-13 17:13:0636#include "googleurl/src/gurl.h"
[email protected]99dfecd2011-10-18 01:11:5037#include "net/base/escape.h"
[email protected]655b2b1a2011-10-13 17:13:0638
[email protected]545ab7a2011-11-03 14:54:2139using content::BrowserThread;
[email protected]e582fdd2011-12-20 16:48:1740using content::DownloadFile;
[email protected]c5eed492012-01-04 17:07:5041using content::NavigationController;
[email protected]545ab7a2011-11-03 14:54:2142
[email protected]655b2b1a2011-10-13 17:13:0643namespace {
44
45const char kInvalidIdError[] = "Invalid id";
46const char kNoBrowserError[] = "No browser found";
[email protected]9c265d02011-12-29 22:13:4347const char kDownloadDirectoryError[] = "Could not create download directory";
[email protected]655b2b1a2011-10-13 17:13:0648
[email protected]99dfecd2011-10-18 01:11:5049const char kInlineInstallSource[] = "inline";
50const char kDefaultInstallSource[] = "";
51
[email protected]9c265d02011-12-29 22:13:4352FilePath* g_download_directory_for_tests = NULL;
53
[email protected]e577c592011-10-25 22:53:3054GURL GetWebstoreInstallURL(
[email protected]99dfecd2011-10-18 01:11:5055 const std::string& extension_id, const std::string& install_source) {
[email protected]f66a50a2011-11-02 23:53:4656 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
57 if (cmd_line->HasSwitch(switches::kAppsGalleryDownloadURL)) {
58 std::string download_url =
59 cmd_line->GetSwitchValueASCII(switches::kAppsGalleryDownloadURL);
60 return GURL(base::StringPrintf(download_url.c_str(),
61 extension_id.c_str()));
62 }
[email protected]99dfecd2011-10-18 01:11:5063 std::vector<std::string> params;
64 params.push_back("id=" + extension_id);
65 if (!install_source.empty()) {
66 params.push_back("installsource=" + install_source);
67 }
68 params.push_back("lang=" + g_browser_process->GetApplicationLocale());
69 params.push_back("uc");
70 std::string url_string = extension_urls::GetWebstoreUpdateUrl(true).spec();
71
72 GURL url(url_string + "?response=redirect&x=" +
73 net::EscapeQueryParamValue(JoinString(params, '&'), true));
74 DCHECK(url.is_valid());
75
76 return url;
77}
78
[email protected]f66a50a2011-11-02 23:53:4679// Must be executed on the FILE thread.
[email protected]4d43111a2012-01-13 03:04:5980void GetDownloadFilePath(const FilePath& download_directory,
[email protected]609dc3f2012-01-10 00:04:0581 const std::string& id,
[email protected]f66a50a2011-11-02 23:53:4682 const base::Callback<void(FilePath)>& callback) {
[email protected]4d43111a2012-01-13 03:04:5983 const FilePath& directory(g_download_directory_for_tests ?
84 *g_download_directory_for_tests : download_directory);
[email protected]9c265d02011-12-29 22:13:4385
86 // Ensure the download directory exists. TODO(asargent) - make this use
87 // common code from the downloads system.
88 if (!file_util::DirectoryExists(directory)) {
89 if (!file_util::CreateDirectory(directory)) {
90 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
91 base::Bind(callback, FilePath()));
92 return;
93 }
94 }
95
[email protected]1dba4b92012-01-26 02:59:2796 // This is to help avoid a race condition between when we generate this
97 // filename and when the download starts writing to it (think concurrently
98 // running sharded browser tests installing the same test file, for
99 // instance).
100 std::string random_number =
101 base::Uint64ToString(base::RandGenerator(kuint16max));
102
103 FilePath file = directory.AppendASCII(id + "_" + random_number + ".crx");
[email protected]f66a50a2011-11-02 23:53:46104
105 int uniquifier = DownloadFile::GetUniquePathNumber(file);
106 if (uniquifier > 0)
107 DownloadFile::AppendNumberToPath(&file, uniquifier);
108
109 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
110 base::Bind(callback, file));
111}
112
[email protected]655b2b1a2011-10-13 17:13:06113} // namespace
114
115
[email protected]98e4e522011-10-25 13:00:16116WebstoreInstaller::WebstoreInstaller(Profile* profile,
117 Delegate* delegate,
[email protected]c5eed492012-01-04 17:07:50118 NavigationController* controller,
[email protected]98e4e522011-10-25 13:00:16119 const std::string& id,
120 int flags)
121 : profile_(profile),
122 delegate_(delegate),
123 controller_(controller),
124 id_(id),
125 flags_(flags) {
[email protected]e577c592011-10-25 22:53:30126 download_url_ = GetWebstoreInstallURL(id, flags & FLAG_INLINE_INSTALL ?
[email protected]98e4e522011-10-25 13:00:16127 kInlineInstallSource : kDefaultInstallSource);
128
[email protected]655b2b1a2011-10-13 17:13:06129 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
[email protected]86ab86b2011-10-19 03:07:55130 content::Source<Profile>(profile));
[email protected]655b2b1a2011-10-13 17:13:06131 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
[email protected]86ab86b2011-10-19 03:07:55132 content::Source<CrxInstaller>(NULL));
[email protected]655b2b1a2011-10-13 17:13:06133}
134
135WebstoreInstaller::~WebstoreInstaller() {}
136
[email protected]98e4e522011-10-25 13:00:16137void WebstoreInstaller::Start() {
[email protected]f66a50a2011-11-02 23:53:46138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]98e4e522011-10-25 13:00:16139 AddRef(); // Balanced in ReportSuccess and ReportFailure.
[email protected]655b2b1a2011-10-13 17:13:06140
[email protected]98e4e522011-10-25 13:00:16141 if (!Extension::IdIsValid(id_)) {
142 ReportFailure(kInvalidIdError);
[email protected]655b2b1a2011-10-13 17:13:06143 return;
144 }
145
[email protected]609dc3f2012-01-10 00:04:05146 FilePath download_path = DownloadPrefs::FromDownloadManager(
147 profile_->GetDownloadManager())->download_path();
[email protected]f66a50a2011-11-02 23:53:46148 BrowserThread::PostTask(
149 BrowserThread::FILE, FROM_HERE,
[email protected]609dc3f2012-01-10 00:04:05150 base::Bind(&GetDownloadFilePath, download_path, id_,
[email protected]f66a50a2011-11-02 23:53:46151 base::Bind(&WebstoreInstaller::StartDownload,
152 base::Unretained(this))));
[email protected]655b2b1a2011-10-13 17:13:06153
[email protected]655b2b1a2011-10-13 17:13:06154}
155
156void WebstoreInstaller::Observe(int type,
[email protected]86ab86b2011-10-19 03:07:55157 const content::NotificationSource& source,
158 const content::NotificationDetails& details) {
[email protected]655b2b1a2011-10-13 17:13:06159 switch (type) {
160 case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
[email protected]86ab86b2011-10-19 03:07:55161 CHECK(profile_->IsSameProfile(content::Source<Profile>(source).ptr()));
162 const Extension* extension =
163 content::Details<const Extension>(details).ptr();
[email protected]98e4e522011-10-25 13:00:16164 if (id_ == extension->id())
165 ReportSuccess();
[email protected]655b2b1a2011-10-13 17:13:06166 break;
167 }
168
169 case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
[email protected]86ab86b2011-10-19 03:07:55170 CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr();
[email protected]655b2b1a2011-10-13 17:13:06171 CHECK(crx_installer);
172 if (!profile_->IsSameProfile(crx_installer->profile()))
173 return;
174
[email protected]fc670822011-12-17 09:33:49175 // TODO(rdevlin.cronin): Continue removing std::string errors and
176 // replacing with string16
177 const string16* error = content::Details<const string16>(details).ptr();
178 const std::string utf8_error = UTF16ToUTF8(*error);
[email protected]98e4e522011-10-25 13:00:16179 if (download_url_ == crx_installer->original_download_url())
[email protected]fc670822011-12-17 09:33:49180 ReportFailure(utf8_error);
[email protected]655b2b1a2011-10-13 17:13:06181 break;
182 }
183
184 default:
185 NOTREACHED();
186 }
187}
188
[email protected]9c265d02011-12-29 22:13:43189void WebstoreInstaller::SetDownloadDirectoryForTests(FilePath* directory) {
190 g_download_directory_for_tests = directory;
191}
192
[email protected]f66a50a2011-11-02 23:53:46193void WebstoreInstaller::StartDownload(FilePath file) {
194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195
[email protected]9c265d02011-12-29 22:13:43196 if (file.empty()) {
197 ReportFailure(kDownloadDirectoryError);
198 return;
199 }
200
[email protected]f66a50a2011-11-02 23:53:46201 // TODO(mihaip): For inline installs, we pretend like the referrer is the
202 // gallery, even though this could be an inline install, in order to pass the
203 // checks in ExtensionService::IsDownloadFromGallery. We should instead pass
204 // the real referrer, track if this is an inline install in the whitelist
205 // entry and look that up when checking that this is a valid download.
[email protected]36fc0392011-12-25 03:59:51206 GURL referrer = controller_->GetActiveEntry()->GetURL();
[email protected]f66a50a2011-11-02 23:53:46207 if (flags_ & FLAG_INLINE_INSTALL)
208 referrer = GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id_);
209
210 DownloadSaveInfo save_info;
211 save_info.file_path = file;
212
213 // The download url for the given extension is contained in |download_url_|.
214 // We will navigate the current tab to this url to start the download. The
215 // download system will then pass the crx to the CrxInstaller.
[email protected]14365e32012-01-24 01:12:22216 download_util::RecordDownloadCount(
217 download_util::INITIATED_BY_WEBSTORE_INSTALLER_COUNT);
[email protected]0d4e30c2012-01-28 00:47:53218 profile_->GetDownloadManager()->DownloadUrl(
219 download_url_, referrer, "",
220 false, save_info, controller_->GetWebContents());
[email protected]f66a50a2011-11-02 23:53:46221}
222
[email protected]98e4e522011-10-25 13:00:16223void WebstoreInstaller::ReportFailure(const std::string& error) {
224 if (delegate_)
225 delegate_->OnExtensionInstallFailure(id_, error);
226
227 Release(); // Balanced in Start().
[email protected]655b2b1a2011-10-13 17:13:06228}
229
[email protected]98e4e522011-10-25 13:00:16230void WebstoreInstaller::ReportSuccess() {
231 if (delegate_)
232 delegate_->OnExtensionInstallSuccess(id_);
[email protected]655b2b1a2011-10-13 17:13:06233
[email protected]98e4e522011-10-25 13:00:16234 Release(); // Balanced in Start().
[email protected]655b2b1a2011-10-13 17:13:06235}