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