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