blob: 9396b56453e64367d1abe0221dd3059b801236b2 [file] [log] [blame]
[email protected]655b2b1a2011-10-13 17:13:061// Copyright (c) 2011 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#include "chrome/browser/extensions/webstore_installer.h"
6
[email protected]99dfecd2011-10-18 01:11:507#include "base/string_util.h"
[email protected]655b2b1a2011-10-13 17:13:068#include "chrome/browser/browser_process.h"
9#include "chrome/browser/extensions/crx_installer.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/tabs/tab_strip_model.h"
12#include "chrome/browser/ui/browser_list.h"
13#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
14#include "chrome/common/chrome_notification_types.h"
15#include "chrome/common/extensions/extension.h"
[email protected]99dfecd2011-10-18 01:11:5016#include "chrome/common/extensions/extension_constants.h"
[email protected]655b2b1a2011-10-13 17:13:0617#include "content/browser/tab_contents/navigation_controller.h"
18#include "content/common/notification_details.h"
19#include "content/common/notification_source.h"
20#include "googleurl/src/gurl.h"
[email protected]99dfecd2011-10-18 01:11:5021#include "net/base/escape.h"
[email protected]655b2b1a2011-10-13 17:13:0622
23namespace {
24
25const char kInvalidIdError[] = "Invalid id";
26const char kNoBrowserError[] = "No browser found";
27
[email protected]99dfecd2011-10-18 01:11:5028const char kInlineInstallSource[] = "inline";
29const char kDefaultInstallSource[] = "";
30
31GURL GetWebstoreInstallUrl(
32 const std::string& extension_id, const std::string& install_source) {
33 std::vector<std::string> params;
34 params.push_back("id=" + extension_id);
35 if (!install_source.empty()) {
36 params.push_back("installsource=" + install_source);
37 }
38 params.push_back("lang=" + g_browser_process->GetApplicationLocale());
39 params.push_back("uc");
40 std::string url_string = extension_urls::GetWebstoreUpdateUrl(true).spec();
41
42 GURL url(url_string + "?response=redirect&x=" +
43 net::EscapeQueryParamValue(JoinString(params, '&'), true));
44 DCHECK(url.is_valid());
45
46 return url;
47}
48
[email protected]655b2b1a2011-10-13 17:13:0649} // namespace
50
51
52WebstoreInstaller::WebstoreInstaller(Profile* profile)
53 : profile_(profile) {
54 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
55 Source<Profile>(profile));
56 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
57 Source<CrxInstaller>(NULL));
58}
59
60WebstoreInstaller::~WebstoreInstaller() {}
61
62struct WebstoreInstaller::PendingInstall {
63 PendingInstall() : delegate(NULL) {}
64 PendingInstall(const std::string& id, const GURL& url, Delegate* delegate)
65 : id(id), download_url(url), delegate(delegate) {}
66 ~PendingInstall() {}
67
68 // The id of the extension.
69 std::string id;
70
71 // The gallery download URL for the extension.
72 GURL download_url;
73
74 // The delegate for this install.
75 Delegate* delegate;
76};
77
78void WebstoreInstaller::InstallExtension(
79 const std::string& id, Delegate* delegate, int flags) {
80 if (!Extension::IdIsValid(id)) {
81 ReportFailure(id, kInvalidIdError);
82 return;
83 }
84
85 Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
86 if (!browser) {
87 ReportFailure(id, kNoBrowserError);
88 return;
89 }
90
[email protected]99dfecd2011-10-18 01:11:5091 GURL install_url = GetWebstoreInstallUrl(id, flags & FLAG_INLINE_INSTALL ?
92 kInlineInstallSource : kDefaultInstallSource);
93 PendingInstall pending_install = CreatePendingInstall(
94 id, install_url, delegate);
[email protected]655b2b1a2011-10-13 17:13:0695
96 // The download url for the given |id| is now contained in
97 // |pending_install.download_url|. We navigate the current tab to this url
98 // which will result in a download starting. Once completed it will go through
99 // the normal extension install flow.
100 NavigationController& controller =
101 browser->tabstrip_model()->GetActiveTabContents()->controller();
102
103 // TODO(mihaip, jstritar): For inline installs, we pretend like the referrer
104 // is the gallery, even though this could be an inline install, in order to
105 // pass the checks in ExtensionService::IsDownloadFromGallery. We should
106 // instead pass the real referrer, track if this is an inline install in the
107 // whitelist entry and look that up when checking that this is a valid
108 // download.
109 GURL referrer = controller.GetActiveEntry()->url();
[email protected]99dfecd2011-10-18 01:11:50110 if (flags & FLAG_INLINE_INSTALL)
[email protected]655b2b1a2011-10-13 17:13:06111 referrer = GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id);
112
[email protected]99dfecd2011-10-18 01:11:50113 controller.LoadURL(install_url,
[email protected]655b2b1a2011-10-13 17:13:06114 referrer,
115 content::PAGE_TRANSITION_LINK,
116 std::string());
117}
118
119void WebstoreInstaller::Observe(int type,
120 const NotificationSource& source,
121 const NotificationDetails& details) {
122 switch (type) {
123 case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
124 CHECK(profile_->IsSameProfile(Source<Profile>(source).ptr()));
125 const Extension* extension = Details<const Extension>(details).ptr();
126 ReportSuccess(extension->id());
127 break;
128 }
129
130 case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
131 CrxInstaller* crx_installer = Source<CrxInstaller>(source).ptr();
132 CHECK(crx_installer);
133 if (!profile_->IsSameProfile(crx_installer->profile()))
134 return;
135
136 std::string id = GetPendingInstallId(
137 crx_installer->original_download_url());
138 const std::string* error = Details<const std::string>(details).ptr();
139 if (!id.empty())
140 ReportFailure(id, *error);
141 break;
142 }
143
144 default:
145 NOTREACHED();
146 }
147}
148
149bool WebstoreInstaller::ClearPendingInstall(
150 const std::string& id, PendingInstall* install) {
151 for (size_t i = 0; i < pending_installs_.size(); ++i) {
152 if (pending_installs_[i].id == id) {
153 *install = pending_installs_[i];
154 pending_installs_.erase(pending_installs_.begin() + i);
155 return true;
156 }
157 }
158 return false;
159}
160
161const WebstoreInstaller::PendingInstall&
162 WebstoreInstaller::CreatePendingInstall(
[email protected]99dfecd2011-10-18 01:11:50163 const std::string& id, GURL install_url, Delegate* delegate) {
[email protected]655b2b1a2011-10-13 17:13:06164 PendingInstall pending_install(id, install_url, delegate);
165 pending_installs_.push_back(pending_install);
166
167 return *(pending_installs_.end() - 1);
168}
169
170
171std::string WebstoreInstaller::GetPendingInstallId(const GURL& url) {
172 if (url.is_empty())
173 return std::string();
174
175 for (size_t i = 0; i < pending_installs_.size(); ++i) {
176 if (pending_installs_[i].download_url == url)
177 return pending_installs_[i].id;
178 }
179
180 return std::string();
181}
182
183void WebstoreInstaller::ReportFailure(
184 const std::string& id, const std::string& error) {
185 PendingInstall install;
186 if (!ClearPendingInstall(id, &install))
187 return;
188
189 if (install.delegate)
190 install.delegate->OnExtensionInstallFailure(id, error);
191}
192
193void WebstoreInstaller::ReportSuccess(const std::string& id) {
194 PendingInstall install;
195 if (!ClearPendingInstall(id, &install))
196 return;
197
198 if (install.delegate)
199 install.delegate->OnExtensionInstallSuccess(id);
200}