blob: 91aa03d20039fbec822de28aeb25f008fd6571ae [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_manager.h"
[email protected]cdcb1dee2012-01-04 00:46:2031#include "content/public/browser/navigation_controller.h"
[email protected]ad23a092011-12-28 07:02:0432#include "content/public/browser/navigation_entry.h"
[email protected]86ab86b2011-10-19 03:07:5533#include "content/public/browser/notification_details.h"
34#include "content/public/browser/notification_source.h"
[email protected]655b2b1a2011-10-13 17:13:0635#include "googleurl/src/gurl.h"
[email protected]99dfecd2011-10-18 01:11:5036#include "net/base/escape.h"
[email protected]655b2b1a2011-10-13 17:13:0637
[email protected]545ab7a2011-11-03 14:54:2138using content::BrowserThread;
[email protected]c5eed492012-01-04 17:07:5039using content::NavigationController;
[email protected]545ab7a2011-11-03 14:54:2140
[email protected]655b2b1a2011-10-13 17:13:0641namespace {
42
43const char kInvalidIdError[] = "Invalid id";
44const char kNoBrowserError[] = "No browser found";
[email protected]9c265d02011-12-29 22:13:4345const char kDownloadDirectoryError[] = "Could not create download directory";
[email protected]655b2b1a2011-10-13 17:13:0646
[email protected]99dfecd2011-10-18 01:11:5047const char kInlineInstallSource[] = "inline";
48const char kDefaultInstallSource[] = "";
49
[email protected]9c265d02011-12-29 22:13:4350FilePath* g_download_directory_for_tests = NULL;
51
[email protected]e577c592011-10-25 22:53:3052GURL GetWebstoreInstallURL(
[email protected]99dfecd2011-10-18 01:11:5053 const std::string& extension_id, const std::string& install_source) {
[email protected]f66a50a2011-11-02 23:53:4654 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
55 if (cmd_line->HasSwitch(switches::kAppsGalleryDownloadURL)) {
56 std::string download_url =
57 cmd_line->GetSwitchValueASCII(switches::kAppsGalleryDownloadURL);
58 return GURL(base::StringPrintf(download_url.c_str(),
59 extension_id.c_str()));
60 }
[email protected]99dfecd2011-10-18 01:11:5061 std::vector<std::string> params;
62 params.push_back("id=" + extension_id);
63 if (!install_source.empty()) {
64 params.push_back("installsource=" + install_source);
65 }
66 params.push_back("lang=" + g_browser_process->GetApplicationLocale());
67 params.push_back("uc");
68 std::string url_string = extension_urls::GetWebstoreUpdateUrl(true).spec();
69
70 GURL url(url_string + "?response=redirect&x=" +
71 net::EscapeQueryParamValue(JoinString(params, '&'), true));
72 DCHECK(url.is_valid());
73
74 return url;
75}
76
[email protected]f66a50a2011-11-02 23:53:4677// Must be executed on the FILE thread.
[email protected]4d43111a2012-01-13 03:04:5978void GetDownloadFilePath(const FilePath& download_directory,
[email protected]609dc3f2012-01-10 00:04:0579 const std::string& id,
[email protected]f66a50a2011-11-02 23:53:4680 const base::Callback<void(FilePath)>& callback) {
[email protected]4d43111a2012-01-13 03:04:5981 const FilePath& directory(g_download_directory_for_tests ?
82 *g_download_directory_for_tests : download_directory);
[email protected]9c265d02011-12-29 22:13:4383
84 // Ensure the download directory exists. TODO(asargent) - make this use
85 // common code from the downloads system.
86 if (!file_util::DirectoryExists(directory)) {
87 if (!file_util::CreateDirectory(directory)) {
88 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
89 base::Bind(callback, FilePath()));
90 return;
91 }
92 }
93
[email protected]1dba4b92012-01-26 02:59:2794 // This is to help avoid a race condition between when we generate this
95 // filename and when the download starts writing to it (think concurrently
96 // running sharded browser tests installing the same test file, for
97 // instance).
98 std::string random_number =
99 base::Uint64ToString(base::RandGenerator(kuint16max));
100
101 FilePath file = directory.AppendASCII(id + "_" + random_number + ".crx");
[email protected]f66a50a2011-11-02 23:53:46102
[email protected]e285afa2012-01-31 23:16:39103 int uniquifier = file_util::GetUniquePathNumber(file, FILE_PATH_LITERAL(""));
[email protected]f66a50a2011-11-02 23:53:46104 if (uniquifier > 0)
[email protected]e285afa2012-01-31 23:16:39105 file = file.InsertBeforeExtensionASCII(StringPrintf(" (%d)", uniquifier));
[email protected]f66a50a2011-11-02 23:53:46106
107 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
108 base::Bind(callback, file));
109}
110
[email protected]655b2b1a2011-10-13 17:13:06111} // namespace
112
113
[email protected]98e4e522011-10-25 13:00:16114WebstoreInstaller::WebstoreInstaller(Profile* profile,
115 Delegate* delegate,
[email protected]c5eed492012-01-04 17:07:50116 NavigationController* controller,
[email protected]98e4e522011-10-25 13:00:16117 const std::string& id,
118 int flags)
119 : profile_(profile),
120 delegate_(delegate),
121 controller_(controller),
122 id_(id),
123 flags_(flags) {
[email protected]e577c592011-10-25 22:53:30124 download_url_ = GetWebstoreInstallURL(id, flags & FLAG_INLINE_INSTALL ?
[email protected]98e4e522011-10-25 13:00:16125 kInlineInstallSource : kDefaultInstallSource);
126
[email protected]655b2b1a2011-10-13 17:13:06127 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
[email protected]86ab86b2011-10-19 03:07:55128 content::Source<Profile>(profile));
[email protected]655b2b1a2011-10-13 17:13:06129 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
[email protected]86ab86b2011-10-19 03:07:55130 content::Source<CrxInstaller>(NULL));
[email protected]655b2b1a2011-10-13 17:13:06131}
132
133WebstoreInstaller::~WebstoreInstaller() {}
134
[email protected]98e4e522011-10-25 13:00:16135void WebstoreInstaller::Start() {
[email protected]f66a50a2011-11-02 23:53:46136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]98e4e522011-10-25 13:00:16137 AddRef(); // Balanced in ReportSuccess and ReportFailure.
[email protected]655b2b1a2011-10-13 17:13:06138
[email protected]98e4e522011-10-25 13:00:16139 if (!Extension::IdIsValid(id_)) {
140 ReportFailure(kInvalidIdError);
[email protected]655b2b1a2011-10-13 17:13:06141 return;
142 }
143
[email protected]609dc3f2012-01-10 00:04:05144 FilePath download_path = DownloadPrefs::FromDownloadManager(
145 profile_->GetDownloadManager())->download_path();
[email protected]f66a50a2011-11-02 23:53:46146 BrowserThread::PostTask(
147 BrowserThread::FILE, FROM_HERE,
[email protected]609dc3f2012-01-10 00:04:05148 base::Bind(&GetDownloadFilePath, download_path, id_,
[email protected]f66a50a2011-11-02 23:53:46149 base::Bind(&WebstoreInstaller::StartDownload,
150 base::Unretained(this))));
[email protected]655b2b1a2011-10-13 17:13:06151
[email protected]655b2b1a2011-10-13 17:13:06152}
153
154void WebstoreInstaller::Observe(int type,
[email protected]86ab86b2011-10-19 03:07:55155 const content::NotificationSource& source,
156 const content::NotificationDetails& details) {
[email protected]655b2b1a2011-10-13 17:13:06157 switch (type) {
158 case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
[email protected]86ab86b2011-10-19 03:07:55159 CHECK(profile_->IsSameProfile(content::Source<Profile>(source).ptr()));
160 const Extension* extension =
161 content::Details<const Extension>(details).ptr();
[email protected]98e4e522011-10-25 13:00:16162 if (id_ == extension->id())
163 ReportSuccess();
[email protected]655b2b1a2011-10-13 17:13:06164 break;
165 }
166
167 case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
[email protected]86ab86b2011-10-19 03:07:55168 CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr();
[email protected]655b2b1a2011-10-13 17:13:06169 CHECK(crx_installer);
170 if (!profile_->IsSameProfile(crx_installer->profile()))
171 return;
172
[email protected]fc670822011-12-17 09:33:49173 // TODO(rdevlin.cronin): Continue removing std::string errors and
174 // replacing with string16
175 const string16* error = content::Details<const string16>(details).ptr();
176 const std::string utf8_error = UTF16ToUTF8(*error);
[email protected]98e4e522011-10-25 13:00:16177 if (download_url_ == crx_installer->original_download_url())
[email protected]fc670822011-12-17 09:33:49178 ReportFailure(utf8_error);
[email protected]655b2b1a2011-10-13 17:13:06179 break;
180 }
181
182 default:
183 NOTREACHED();
184 }
185}
186
[email protected]9c265d02011-12-29 22:13:43187void WebstoreInstaller::SetDownloadDirectoryForTests(FilePath* directory) {
188 g_download_directory_for_tests = directory;
189}
190
[email protected]f66a50a2011-11-02 23:53:46191void WebstoreInstaller::StartDownload(FilePath file) {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193
[email protected]9c265d02011-12-29 22:13:43194 if (file.empty()) {
195 ReportFailure(kDownloadDirectoryError);
196 return;
197 }
198
[email protected]f66a50a2011-11-02 23:53:46199 // TODO(mihaip): For inline installs, we pretend like the referrer is the
200 // gallery, even though this could be an inline install, in order to pass the
201 // checks in ExtensionService::IsDownloadFromGallery. We should instead pass
202 // the real referrer, track if this is an inline install in the whitelist
203 // entry and look that up when checking that this is a valid download.
[email protected]36fc0392011-12-25 03:59:51204 GURL referrer = controller_->GetActiveEntry()->GetURL();
[email protected]f66a50a2011-11-02 23:53:46205 if (flags_ & FLAG_INLINE_INSTALL)
206 referrer = GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id_);
207
208 DownloadSaveInfo save_info;
209 save_info.file_path = file;
210
211 // The download url for the given extension is contained in |download_url_|.
212 // We will navigate the current tab to this url to start the download. The
213 // download system will then pass the crx to the CrxInstaller.
[email protected]731809e2012-02-15 21:56:48214 download_util::RecordDownloadSource(
215 download_util::INITIATED_BY_WEBSTORE_INSTALLER);
[email protected]0d4e30c2012-01-28 00:47:53216 profile_->GetDownloadManager()->DownloadUrl(
217 download_url_, referrer, "",
[email protected]27678b2a2012-02-04 22:09:14218 false, -1, save_info, controller_->GetWebContents());
[email protected]f66a50a2011-11-02 23:53:46219}
220
[email protected]98e4e522011-10-25 13:00:16221void WebstoreInstaller::ReportFailure(const std::string& error) {
222 if (delegate_)
223 delegate_->OnExtensionInstallFailure(id_, error);
224
225 Release(); // Balanced in Start().
[email protected]655b2b1a2011-10-13 17:13:06226}
227
[email protected]98e4e522011-10-25 13:00:16228void WebstoreInstaller::ReportSuccess() {
229 if (delegate_)
230 delegate_->OnExtensionInstallSuccess(id_);
[email protected]655b2b1a2011-10-13 17:13:06231
[email protected]98e4e522011-10-25 13:00:16232 Release(); // Balanced in Start().
[email protected]655b2b1a2011-10-13 17:13:06233}