1 // Copyright 2018 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/forced_extensions/force_installed_tracker.h"
6
7 #include "base/bind.h"
8 #include "base/values.h"
9 #include "chrome/browser/extensions/extension_management_constants.h"
10 #include "chrome/browser/extensions/external_provider_impl.h"
11 #include "chrome/browser/extensions/forced_extensions/install_stage_tracker.h"
12 #include "chrome/browser/policy/profile_policy_connector.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "components/prefs/pref_service.h"
15 #include "extensions/browser/install/crx_install_error.h"
16 #include "extensions/browser/pref_names.h"
17 #include "extensions/common/extension_urls.h"
18
19 #if defined(OS_CHROMEOS)
20 #include "components/arc/arc_prefs.h"
21 #endif // defined(OS_CHROMEOS)
22
23 namespace extensions {
24
ForceInstalledTracker(ExtensionRegistry * registry,Profile * profile)25 ForceInstalledTracker::ForceInstalledTracker(ExtensionRegistry* registry,
26 Profile* profile)
27 : extension_management_(
28 ExtensionManagementFactory::GetForBrowserContext(profile)),
29 registry_(registry),
30 profile_(profile),
31 pref_service_(profile->GetPrefs()) {
32 // Load immediately if PolicyService is ready, or wait for it to finish
33 // initializing first.
34 if (policy_service()->IsInitializationComplete(policy::POLICY_DOMAIN_CHROME))
35 OnForcedExtensionsPrefReady();
36 else
37 policy_service()->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
38 }
39
~ForceInstalledTracker()40 ForceInstalledTracker::~ForceInstalledTracker() {
41 policy_service()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
42 }
43
UpdateCounters(ExtensionStatus status,int delta)44 void ForceInstalledTracker::UpdateCounters(ExtensionStatus status, int delta) {
45 switch (status) {
46 case ExtensionStatus::PENDING:
47 load_pending_count_ += delta;
48 FALLTHROUGH;
49 case ExtensionStatus::LOADED:
50 ready_pending_count_ += delta;
51 break;
52 case ExtensionStatus::READY:
53 case ExtensionStatus::FAILED:
54 break;
55 }
56 }
57
AddExtensionInfo(const ExtensionId & extension_id,ExtensionStatus status,bool is_from_store)58 void ForceInstalledTracker::AddExtensionInfo(const ExtensionId& extension_id,
59 ExtensionStatus status,
60 bool is_from_store) {
61 auto result =
62 extensions_.emplace(extension_id, ExtensionInfo{status, is_from_store});
63 DCHECK(result.second);
64 UpdateCounters(result.first->second.status, +1);
65 }
66
ChangeExtensionStatus(const ExtensionId & extension_id,ExtensionStatus status)67 void ForceInstalledTracker::ChangeExtensionStatus(
68 const ExtensionId& extension_id,
69 ExtensionStatus status) {
70 DCHECK_GE(status_, kWaitingForExtensionLoads);
71 auto item = extensions_.find(extension_id);
72 if (item == extensions_.end())
73 return;
74 UpdateCounters(item->second.status, -1);
75 item->second.status = status;
76 UpdateCounters(item->second.status, +1);
77 }
78
OnPolicyUpdated(const policy::PolicyNamespace & ns,const policy::PolicyMap & previous,const policy::PolicyMap & current)79 void ForceInstalledTracker::OnPolicyUpdated(const policy::PolicyNamespace& ns,
80 const policy::PolicyMap& previous,
81 const policy::PolicyMap& current) {}
82
OnPolicyServiceInitialized(policy::PolicyDomain domain)83 void ForceInstalledTracker::OnPolicyServiceInitialized(
84 policy::PolicyDomain domain) {
85 DCHECK_EQ(domain, policy::POLICY_DOMAIN_CHROME);
86 DCHECK_EQ(status_, kWaitingForPolicyService);
87 policy_service()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
88 OnForcedExtensionsPrefReady();
89 }
90
OnForcedExtensionsPrefReady()91 void ForceInstalledTracker::OnForcedExtensionsPrefReady() {
92 DCHECK(
93 policy_service()->IsInitializationComplete(policy::POLICY_DOMAIN_CHROME));
94 DCHECK_EQ(status_, kWaitingForPolicyService);
95
96 // Listen for extension loads and install failures.
97 status_ = kWaitingForExtensionLoads;
98 registry_observer_.Add(registry_);
99 collector_observer_.Add(InstallStageTracker::Get(profile_));
100
101 const base::DictionaryValue* value =
102 pref_service_->GetDictionary(pref_names::kInstallForceList);
103 if (value) {
104 // Add each extension to |extensions_|.
105 for (const auto& entry : *value) {
106 const ExtensionId& extension_id = entry.first;
107 std::string* update_url = nullptr;
108 if (entry.second->is_dict()) {
109 update_url = entry.second->FindStringKey(
110 ExternalProviderImpl::kExternalUpdateUrl);
111 }
112 bool is_from_store =
113 update_url && *update_url == extension_urls::kChromeWebstoreUpdateURL;
114
115 ExtensionStatus status = ExtensionStatus::PENDING;
116 if (registry_->enabled_extensions().Contains(extension_id)) {
117 status = registry_->ready_extensions().Contains(extension_id)
118 ? ExtensionStatus::READY
119 : ExtensionStatus::LOADED;
120 }
121 AddExtensionInfo(extension_id, status, is_from_store);
122 }
123 }
124
125 // Run observers if there are no pending installs.
126 MaybeNotifyObservers();
127 }
128
OnShutdown(ExtensionRegistry *)129 void ForceInstalledTracker::OnShutdown(ExtensionRegistry*) {
130 registry_observer_.RemoveAll();
131 }
132
AddObserver(Observer * obs)133 void ForceInstalledTracker::AddObserver(Observer* obs) {
134 observers_.AddObserver(obs);
135 }
136
RemoveObserver(Observer * obs)137 void ForceInstalledTracker::RemoveObserver(Observer* obs) {
138 observers_.RemoveObserver(obs);
139 }
140
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)141 void ForceInstalledTracker::OnExtensionLoaded(
142 content::BrowserContext* browser_context,
143 const Extension* extension) {
144 ChangeExtensionStatus(extension->id(), ExtensionStatus::LOADED);
145 MaybeNotifyObservers();
146 }
147
OnExtensionReady(content::BrowserContext * browser_context,const Extension * extension)148 void ForceInstalledTracker::OnExtensionReady(
149 content::BrowserContext* browser_context,
150 const Extension* extension) {
151 ChangeExtensionStatus(extension->id(), ExtensionStatus::READY);
152 MaybeNotifyObservers();
153 }
154
OnExtensionInstallationFailed(const ExtensionId & extension_id,InstallStageTracker::FailureReason reason)155 void ForceInstalledTracker::OnExtensionInstallationFailed(
156 const ExtensionId& extension_id,
157 InstallStageTracker::FailureReason reason) {
158 auto item = extensions_.find(extension_id);
159 // If the extension is loaded, ignore the failure.
160 if (item == extensions_.end() ||
161 item->second.status == ExtensionStatus::LOADED ||
162 item->second.status == ExtensionStatus::READY)
163 return;
164 ChangeExtensionStatus(extension_id, ExtensionStatus::FAILED);
165 MaybeNotifyObservers();
166 }
167
IsDoneLoading() const168 bool ForceInstalledTracker::IsDoneLoading() const {
169 return status_ == kWaitingForExtensionReady || status_ == kComplete;
170 }
171
OnExtensionDownloadCacheStatusRetrieved(const ExtensionId & id,ExtensionDownloaderDelegate::CacheStatus cache_status)172 void ForceInstalledTracker::OnExtensionDownloadCacheStatusRetrieved(
173 const ExtensionId& id,
174 ExtensionDownloaderDelegate::CacheStatus cache_status) {
175 // Report cache status only for forced installed extension.
176 if (extensions_.find(id) != extensions_.end()) {
177 for (auto& obs : observers_)
178 obs.OnExtensionDownloadCacheStatusRetrieved(id, cache_status);
179 }
180 }
181
IsReady() const182 bool ForceInstalledTracker::IsReady() const {
183 return status_ == kComplete;
184 }
185
IsMisconfiguration(const InstallStageTracker::InstallationData & installation_data,const ExtensionId & id) const186 bool ForceInstalledTracker::IsMisconfiguration(
187 const InstallStageTracker::InstallationData& installation_data,
188 const ExtensionId& id) const {
189 if (installation_data.install_error_detail) {
190 CrxInstallErrorDetail detail =
191 installation_data.install_error_detail.value();
192 if (detail == CrxInstallErrorDetail::KIOSK_MODE_ONLY)
193 return true;
194
195 if (installation_data.extension_type &&
196 detail == CrxInstallErrorDetail::DISALLOWED_BY_POLICY &&
197 !extension_management_->IsAllowedManifestType(
198 installation_data.extension_type.value(), id)) {
199 return true;
200 }
201 }
202
203 #if defined(OS_CHROMEOS)
204 // REPLACED_BY_ARC_APP error is a misconfiguration if ARC++ is enabled for
205 // the device.
206 if (profile_->GetPrefs()->IsManagedPreference(arc::prefs::kArcEnabled) &&
207 profile_->GetPrefs()->GetBoolean(arc::prefs::kArcEnabled) &&
208 installation_data.failure_reason ==
209 InstallStageTracker::FailureReason::REPLACED_BY_ARC_APP) {
210 return true;
211 }
212 #endif // defined(OS_CHROMEOS)
213
214 if (installation_data.failure_reason ==
215 InstallStageTracker::FailureReason::NOT_PERFORMING_NEW_INSTALL) {
216 return true;
217 }
218 if (installation_data.failure_reason ==
219 InstallStageTracker::FailureReason::CRX_FETCH_URL_EMPTY) {
220 DCHECK(installation_data.no_updates_info);
221 if (installation_data.no_updates_info.value() ==
222 InstallStageTracker::NoUpdatesInfo::kEmpty) {
223 return true;
224 }
225 }
226
227 if (installation_data.manifest_invalid_error ==
228 ManifestInvalidError::BAD_APP_STATUS &&
229 installation_data.app_status_error ==
230 InstallStageTracker::AppStatusError::kErrorUnknownApplication) {
231 return true;
232 }
233
234 if (installation_data.unpacker_failure_reason ==
235 SandboxedUnpackerFailureReason::CRX_HEADER_INVALID) {
236 auto extension = extensions_.find(id);
237 // Extension id may be missing from this list if there is a change in
238 // ExtensionInstallForcelist policy after the user has logged in and
239 // |IsMisconfiguration| method is called from
240 // |ExtensionInstallEventLogCollector|.
241 if (extension != extensions_.end() && !extension->second.is_from_store &&
242 !IsExtensionFetchedFromCache(
243 installation_data.downloading_cache_status)) {
244 return true;
245 }
246 }
247
248 return false;
249 }
250
251 // static
IsExtensionFetchedFromCache(const base::Optional<ExtensionDownloaderDelegate::CacheStatus> & status)252 bool ForceInstalledTracker::IsExtensionFetchedFromCache(
253 const base::Optional<ExtensionDownloaderDelegate::CacheStatus>& status) {
254 if (!status)
255 return false;
256 return status.value() == ExtensionDownloaderDelegate::CacheStatus::
257 CACHE_HIT_ON_MANIFEST_FETCH_FAILURE ||
258 status.value() == ExtensionDownloaderDelegate::CacheStatus::CACHE_HIT;
259 }
260
policy_service()261 policy::PolicyService* ForceInstalledTracker::policy_service() {
262 return profile_->GetProfilePolicyConnector()->policy_service();
263 }
264
MaybeNotifyObservers()265 void ForceInstalledTracker::MaybeNotifyObservers() {
266 DCHECK_GE(status_, kWaitingForExtensionLoads);
267 if (status_ == kWaitingForExtensionLoads && load_pending_count_ == 0) {
268 for (auto& obs : observers_)
269 obs.OnForceInstalledExtensionsLoaded();
270 status_ = kWaitingForExtensionReady;
271 }
272 if (status_ == kWaitingForExtensionReady && ready_pending_count_ == 0) {
273 for (auto& obs : observers_)
274 obs.OnForceInstalledExtensionsReady();
275 status_ = kComplete;
276 registry_observer_.RemoveAll();
277 collector_observer_.RemoveAll();
278 InstallStageTracker::Get(profile_)->Clear();
279 }
280 }
281
282 } // namespace extensions
283