• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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