1 // Copyright 2014 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/pending_extension_manager.h"
6
7 #include <algorithm>
8
9 #include "base/logging.h"
10 #include "base/version.h"
11 #include "chrome/common/extensions/extension_constants.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "extensions/browser/extension_prefs.h"
14 #include "extensions/browser/extension_registry.h"
15 #include "extensions/common/constants.h"
16 #include "extensions/common/extension.h"
17 #include "url/gurl.h"
18
19 using content::BrowserThread;
20
21 namespace {
22
23 // Install predicate used by AddFromExternalUpdateUrl().
AlwaysInstall(const extensions::Extension * extension)24 bool AlwaysInstall(const extensions::Extension* extension) {
25 return true;
26 }
27
GetVersionString(const Version & version)28 std::string GetVersionString(const Version& version) {
29 return version.IsValid() ? version.GetString() : "invalid";
30 }
31
32 } // namespace
33
34 namespace extensions {
35
PendingExtensionManager(content::BrowserContext * context)36 PendingExtensionManager::PendingExtensionManager(
37 content::BrowserContext* context)
38 : context_(context) {}
39
~PendingExtensionManager()40 PendingExtensionManager::~PendingExtensionManager() {}
41
GetById(const std::string & id) const42 const PendingExtensionInfo* PendingExtensionManager::GetById(
43 const std::string& id) const {
44 PendingExtensionList::const_iterator iter;
45 for (iter = pending_extension_list_.begin();
46 iter != pending_extension_list_.end();
47 ++iter) {
48 if (id == iter->id())
49 return &(*iter);
50 }
51
52 return NULL;
53 }
54
Remove(const std::string & id)55 bool PendingExtensionManager::Remove(const std::string& id) {
56 PendingExtensionList::iterator iter;
57 for (iter = pending_extension_list_.begin();
58 iter != pending_extension_list_.end();
59 ++iter) {
60 if (id == iter->id()) {
61 pending_extension_list_.erase(iter);
62 return true;
63 }
64 }
65
66 return false;
67 }
68
IsIdPending(const std::string & id) const69 bool PendingExtensionManager::IsIdPending(const std::string& id) const {
70 return GetById(id) != NULL;
71 }
72
HasPendingExtensions() const73 bool PendingExtensionManager::HasPendingExtensions() const {
74 return !pending_extension_list_.empty();
75 }
76
HasPendingExtensionFromSync() const77 bool PendingExtensionManager::HasPendingExtensionFromSync() const {
78 PendingExtensionList::const_iterator iter;
79 for (iter = pending_extension_list_.begin();
80 iter != pending_extension_list_.end();
81 ++iter) {
82 if (iter->is_from_sync())
83 return true;
84 }
85
86 return false;
87 }
88
AddFromSync(const std::string & id,const GURL & update_url,PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,bool install_silently,bool remote_install,bool installed_by_custodian)89 bool PendingExtensionManager::AddFromSync(
90 const std::string& id,
91 const GURL& update_url,
92 PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
93 bool install_silently,
94 bool remote_install,
95 bool installed_by_custodian) {
96 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
97
98 if (ExtensionRegistry::Get(context_)->GetExtensionById(
99 id, ExtensionRegistry::EVERYTHING)) {
100 LOG(ERROR) << "Trying to add pending extension " << id
101 << " which already exists";
102 return false;
103 }
104
105 // Make sure we don't ever try to install the CWS app, because even though
106 // it is listed as a syncable app (because its values need to be synced) it
107 // should already be installed on every instance.
108 if (id == extensions::kWebStoreAppId) {
109 NOTREACHED();
110 return false;
111 }
112
113 int creation_flags = Extension::NO_FLAGS;
114 if (installed_by_custodian) {
115 creation_flags |= Extension::WAS_INSTALLED_BY_CUSTODIAN;
116 }
117
118 static const bool kIsFromSync = true;
119 static const Manifest::Location kSyncLocation = Manifest::INTERNAL;
120 static const bool kMarkAcknowledged = false;
121
122 return AddExtensionImpl(id,
123 std::string(),
124 update_url,
125 Version(),
126 should_allow_install,
127 kIsFromSync,
128 install_silently,
129 kSyncLocation,
130 creation_flags,
131 kMarkAcknowledged,
132 remote_install);
133 }
134
AddFromExtensionImport(const std::string & id,const GURL & update_url,PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install)135 bool PendingExtensionManager::AddFromExtensionImport(
136 const std::string& id,
137 const GURL& update_url,
138 PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install) {
139 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
140
141 if (ExtensionRegistry::Get(context_)->GetExtensionById(
142 id, ExtensionRegistry::EVERYTHING)) {
143 LOG(ERROR) << "Trying to add pending extension " << id
144 << " which already exists";
145 return false;
146 }
147
148 static const bool kIsFromSync = false;
149 static const bool kInstallSilently = true;
150 static const Manifest::Location kManifestLocation = Manifest::INTERNAL;
151 static const bool kMarkAcknowledged = false;
152 static const bool kRemoteInstall = false;
153
154 return AddExtensionImpl(id,
155 std::string(),
156 update_url,
157 Version(),
158 should_allow_install,
159 kIsFromSync,
160 kInstallSilently,
161 kManifestLocation,
162 Extension::NO_FLAGS,
163 kMarkAcknowledged,
164 kRemoteInstall);
165 }
166
AddFromExternalUpdateUrl(const std::string & id,const std::string & install_parameter,const GURL & update_url,Manifest::Location location,int creation_flags,bool mark_acknowledged)167 bool PendingExtensionManager::AddFromExternalUpdateUrl(
168 const std::string& id,
169 const std::string& install_parameter,
170 const GURL& update_url,
171 Manifest::Location location,
172 int creation_flags,
173 bool mark_acknowledged) {
174 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
175
176 static const bool kIsFromSync = false;
177 static const bool kInstallSilently = true;
178 static const bool kRemoteInstall = false;
179
180 const Extension* extension = ExtensionRegistry::Get(context_)
181 ->GetExtensionById(id, ExtensionRegistry::EVERYTHING);
182 if (extension && location == Manifest::GetHigherPriorityLocation(
183 location, extension->location())) {
184 // If the new location has higher priority than the location of an existing
185 // extension, let the update process overwrite the existing extension.
186 } else {
187 if (ExtensionPrefs::Get(context_)->IsExternalExtensionUninstalled(id))
188 return false;
189
190 if (extension) {
191 LOG(DFATAL) << "Trying to add extension " << id
192 << " by external update, but it is already installed.";
193 return false;
194 }
195 }
196
197 return AddExtensionImpl(id,
198 install_parameter,
199 update_url,
200 Version(),
201 &AlwaysInstall,
202 kIsFromSync,
203 kInstallSilently,
204 location,
205 creation_flags,
206 mark_acknowledged,
207 kRemoteInstall);
208 }
209
210
AddFromExternalFile(const std::string & id,Manifest::Location install_source,const Version & version,int creation_flags,bool mark_acknowledged)211 bool PendingExtensionManager::AddFromExternalFile(
212 const std::string& id,
213 Manifest::Location install_source,
214 const Version& version,
215 int creation_flags,
216 bool mark_acknowledged) {
217 // TODO(skerner): AddFromSync() checks to see if the extension is
218 // installed, but this method assumes that the caller already
219 // made sure it is not installed. Make all AddFrom*() methods
220 // consistent.
221 const GURL& kUpdateUrl = GURL::EmptyGURL();
222 static const bool kIsFromSync = false;
223 static const bool kInstallSilently = true;
224 static const bool kRemoteInstall = false;
225
226 return AddExtensionImpl(id,
227 std::string(),
228 kUpdateUrl,
229 version,
230 &AlwaysInstall,
231 kIsFromSync,
232 kInstallSilently,
233 install_source,
234 creation_flags,
235 mark_acknowledged,
236 kRemoteInstall);
237 }
238
GetPendingIdsForUpdateCheck(std::list<std::string> * out_ids_for_update_check) const239 void PendingExtensionManager::GetPendingIdsForUpdateCheck(
240 std::list<std::string>* out_ids_for_update_check) const {
241 PendingExtensionList::const_iterator iter;
242 for (iter = pending_extension_list_.begin();
243 iter != pending_extension_list_.end();
244 ++iter) {
245 Manifest::Location install_source = iter->install_source();
246
247 // Some install sources read a CRX from the filesystem. They can
248 // not be fetched from an update URL, so don't include them in the
249 // set of ids.
250 if (install_source == Manifest::EXTERNAL_PREF ||
251 install_source == Manifest::EXTERNAL_REGISTRY)
252 continue;
253
254 out_ids_for_update_check->push_back(iter->id());
255 }
256 }
257
AddExtensionImpl(const std::string & id,const std::string & install_parameter,const GURL & update_url,const Version & version,PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,bool is_from_sync,bool install_silently,Manifest::Location install_source,int creation_flags,bool mark_acknowledged,bool remote_install)258 bool PendingExtensionManager::AddExtensionImpl(
259 const std::string& id,
260 const std::string& install_parameter,
261 const GURL& update_url,
262 const Version& version,
263 PendingExtensionInfo::ShouldAllowInstallPredicate should_allow_install,
264 bool is_from_sync,
265 bool install_silently,
266 Manifest::Location install_source,
267 int creation_flags,
268 bool mark_acknowledged,
269 bool remote_install) {
270 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271
272 PendingExtensionInfo info(id,
273 install_parameter,
274 update_url,
275 version,
276 should_allow_install,
277 is_from_sync,
278 install_silently,
279 install_source,
280 creation_flags,
281 mark_acknowledged,
282 remote_install);
283
284 if (const PendingExtensionInfo* pending = GetById(id)) {
285 // Bugs in this code will manifest as sporadic incorrect extension
286 // locations in situations where multiple install sources run at the
287 // same time. For example, on first login to a chrome os machine, an
288 // extension may be requested by sync and the default extension set.
289 // The following logging will help diagnose such issues.
290 VLOG(1) << "Extension id " << id
291 << " was entered for update more than once."
292 << " old location: " << pending->install_source()
293 << " new location: " << install_source
294 << " old version: " << GetVersionString(pending->version())
295 << " new version: " << GetVersionString(version);
296
297 // Never override an existing extension with an older version. Only
298 // extensions from local CRX files have a known version; extensions from an
299 // update URL will get the latest version.
300
301 // If |pending| has the same or higher precedence than |info| then don't
302 // install |info| over |pending|.
303 if (pending->CompareTo(info) >= 0)
304 return false;
305
306 VLOG(1) << "Overwrite existing record.";
307
308 std::replace(pending_extension_list_.begin(),
309 pending_extension_list_.end(),
310 *pending,
311 info);
312 } else {
313 pending_extension_list_.push_back(info);
314 }
315
316 return true;
317 }
318
AddForTesting(const PendingExtensionInfo & pending_extension_info)319 void PendingExtensionManager::AddForTesting(
320 const PendingExtensionInfo& pending_extension_info) {
321 pending_extension_list_.push_back(pending_extension_info);
322 }
323
324 } // namespace extensions
325