OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/extension_service.h" | 5 #include "chrome/browser/extensions/extension_service.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <iterator> | 10 #include <iterator> |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 using extensions::ExternalProviderInterface; | 126 using extensions::ExternalProviderInterface; |
127 using extensions::FeatureSwitch; | 127 using extensions::FeatureSwitch; |
128 using extensions::InstallVerifier; | 128 using extensions::InstallVerifier; |
129 using extensions::ManagementPolicy; | 129 using extensions::ManagementPolicy; |
130 using extensions::Manifest; | 130 using extensions::Manifest; |
131 using extensions::PermissionID; | 131 using extensions::PermissionID; |
132 using extensions::PermissionIDSet; | 132 using extensions::PermissionIDSet; |
133 using extensions::PermissionSet; | 133 using extensions::PermissionSet; |
134 using extensions::SharedModuleInfo; | 134 using extensions::SharedModuleInfo; |
135 using extensions::SharedModuleService; | 135 using extensions::SharedModuleService; |
136 using extensions::UnloadedExtensionInfo; | 136 using extensions::UnloadedExtensionReason; |
137 | 137 |
138 namespace { | 138 namespace { |
139 | 139 |
140 // Wait this many seconds after an extensions becomes idle before updating it. | 140 // Wait this many seconds after an extensions becomes idle before updating it. |
141 const int kUpdateIdleDelay = 5; | 141 const int kUpdateIdleDelay = 5; |
142 | 142 |
143 // Comma-separated list of directories with extensions to load. | 143 // Comma-separated list of directories with extensions to load. |
144 // TODO(samuong): Remove this in M58 (see comment in ExtensionService::Init). | 144 // TODO(samuong): Remove this in M58 (see comment in ExtensionService::Init). |
145 const char kDeprecatedLoadComponentExtension[] = "load-component-extension"; | 145 const char kDeprecatedLoadComponentExtension[] = "load-component-extension"; |
146 | 146 |
(...skipping 667 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
814 } | 814 } |
815 | 815 |
816 InstallVerifier::Get(GetBrowserContext())->Remove(extension->id()); | 816 InstallVerifier::Get(GetBrowserContext())->Remove(extension->id()); |
817 | 817 |
818 UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallType", | 818 UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallType", |
819 extension->GetType(), 100); | 819 extension->GetType(), 100); |
820 RecordPermissionMessagesHistogram(extension.get(), "Uninstall"); | 820 RecordPermissionMessagesHistogram(extension.get(), "Uninstall"); |
821 | 821 |
822 // Unload before doing more cleanup to ensure that nothing is hanging on to | 822 // Unload before doing more cleanup to ensure that nothing is hanging on to |
823 // any of these resources. | 823 // any of these resources. |
824 UnloadExtension(extension->id(), UnloadedExtensionInfo::REASON_UNINSTALL); | 824 UnloadExtension(extension->id(), UnloadedExtensionReason::UNINSTALL); |
825 if (registry_->blacklisted_extensions().Contains(extension->id())) | 825 if (registry_->blacklisted_extensions().Contains(extension->id())) |
826 registry_->RemoveBlacklisted(extension->id()); | 826 registry_->RemoveBlacklisted(extension->id()); |
827 | 827 |
828 // Tell the backend to start deleting installed extensions on the file thread. | 828 // Tell the backend to start deleting installed extensions on the file thread. |
829 if (!Manifest::IsUnpackedLocation(extension->location())) { | 829 if (!Manifest::IsUnpackedLocation(extension->location())) { |
830 if (!GetFileTaskRunner()->PostTask( | 830 if (!GetFileTaskRunner()->PostTask( |
831 FROM_HERE, | 831 FROM_HERE, |
832 base::BindOnce(&ExtensionService::UninstallExtensionOnFileThread, | 832 base::BindOnce(&ExtensionService::UninstallExtensionOnFileThread, |
833 extension->id(), profile_, install_directory_, | 833 extension->id(), profile_, install_directory_, |
834 extension->path()))) | 834 extension->path()))) |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
984 | 984 |
985 // The extension is either enabled or terminated. | 985 // The extension is either enabled or terminated. |
986 DCHECK(registry_->enabled_extensions().Contains(extension->id()) || | 986 DCHECK(registry_->enabled_extensions().Contains(extension->id()) || |
987 registry_->terminated_extensions().Contains(extension->id())); | 987 registry_->terminated_extensions().Contains(extension->id())); |
988 | 988 |
989 // Move it over to the disabled list. Don't send a second unload notification | 989 // Move it over to the disabled list. Don't send a second unload notification |
990 // for terminated extensions being disabled. | 990 // for terminated extensions being disabled. |
991 registry_->AddDisabled(make_scoped_refptr(extension)); | 991 registry_->AddDisabled(make_scoped_refptr(extension)); |
992 if (registry_->enabled_extensions().Contains(extension->id())) { | 992 if (registry_->enabled_extensions().Contains(extension->id())) { |
993 registry_->RemoveEnabled(extension->id()); | 993 registry_->RemoveEnabled(extension->id()); |
994 NotifyExtensionUnloaded(extension, UnloadedExtensionInfo::REASON_DISABLE); | 994 NotifyExtensionUnloaded(extension, UnloadedExtensionReason::DISABLE); |
995 } else { | 995 } else { |
996 registry_->RemoveTerminated(extension->id()); | 996 registry_->RemoveTerminated(extension->id()); |
997 } | 997 } |
998 } | 998 } |
999 | 999 |
1000 void ExtensionService::DisableUserExtensionsExcept( | 1000 void ExtensionService::DisableUserExtensionsExcept( |
1001 const std::vector<std::string>& except_ids) { | 1001 const std::vector<std::string>& except_ids) { |
1002 extensions::ManagementPolicy* management_policy = | 1002 extensions::ManagementPolicy* management_policy = |
1003 system_->management_policy(); | 1003 system_->management_policy(); |
1004 extensions::ExtensionList to_disable; | 1004 extensions::ExtensionList to_disable; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1042 const std::string& id = extension->id(); | 1042 const std::string& id = extension->id(); |
1043 | 1043 |
1044 if (!CanBlockExtension(extension.get())) | 1044 if (!CanBlockExtension(extension.get())) |
1045 continue; | 1045 continue; |
1046 | 1046 |
1047 registry_->RemoveEnabled(id); | 1047 registry_->RemoveEnabled(id); |
1048 registry_->RemoveDisabled(id); | 1048 registry_->RemoveDisabled(id); |
1049 registry_->RemoveTerminated(id); | 1049 registry_->RemoveTerminated(id); |
1050 | 1050 |
1051 registry_->AddBlocked(extension.get()); | 1051 registry_->AddBlocked(extension.get()); |
1052 UnloadExtension(id, extensions::UnloadedExtensionInfo::REASON_LOCK_ALL); | 1052 UnloadExtension(id, extensions::UnloadedExtensionReason::LOCK_ALL); |
1053 } | 1053 } |
1054 } | 1054 } |
1055 | 1055 |
1056 // All locked extensions should revert to being either enabled or disabled | 1056 // All locked extensions should revert to being either enabled or disabled |
1057 // as appropriate. | 1057 // as appropriate. |
1058 void ExtensionService::UnblockAllExtensions() { | 1058 void ExtensionService::UnblockAllExtensions() { |
1059 block_extensions_ = false; | 1059 block_extensions_ = false; |
1060 std::unique_ptr<ExtensionSet> to_unblock = | 1060 std::unique_ptr<ExtensionSet> to_unblock = |
1061 registry_->GenerateInstalledExtensionsSet(ExtensionRegistry::BLOCKED); | 1061 registry_->GenerateInstalledExtensionsSet(ExtensionRegistry::BLOCKED); |
1062 | 1062 |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1162 } | 1162 } |
1163 } | 1163 } |
1164 | 1164 |
1165 void ExtensionService::OnExtensionRegisteredWithRequestContexts( | 1165 void ExtensionService::OnExtensionRegisteredWithRequestContexts( |
1166 scoped_refptr<const extensions::Extension> extension) { | 1166 scoped_refptr<const extensions::Extension> extension) { |
1167 registry_->AddReady(extension); | 1167 registry_->AddReady(extension); |
1168 if (registry_->enabled_extensions().Contains(extension->id())) | 1168 if (registry_->enabled_extensions().Contains(extension->id())) |
1169 registry_->TriggerOnReady(extension.get()); | 1169 registry_->TriggerOnReady(extension.get()); |
1170 } | 1170 } |
1171 | 1171 |
1172 void ExtensionService::NotifyExtensionUnloaded( | 1172 void ExtensionService::NotifyExtensionUnloaded(const Extension* extension, |
1173 const Extension* extension, | 1173 UnloadedExtensionReason reason) { |
1174 UnloadedExtensionInfo::Reason reason) { | |
1175 registry_->TriggerOnUnloaded(extension, reason); | 1174 registry_->TriggerOnUnloaded(extension, reason); |
1176 | 1175 |
1177 renderer_helper_->OnExtensionUnloaded(*extension); | 1176 renderer_helper_->OnExtensionUnloaded(*extension); |
1178 | 1177 |
1179 system_->UnregisterExtensionWithRequestContexts(extension->id(), reason); | 1178 system_->UnregisterExtensionWithRequestContexts(extension->id(), reason); |
1180 | 1179 |
1181 // TODO(kalman): Convert ExtensionSpecialStoragePolicy to a | 1180 // TODO(kalman): Convert ExtensionSpecialStoragePolicy to a |
1182 // BrowserContextKeyedService and use ExtensionRegistryObserver. | 1181 // BrowserContextKeyedService and use ExtensionRegistryObserver. |
1183 profile_->GetExtensionSpecialStoragePolicy()-> | 1182 profile_->GetExtensionSpecialStoragePolicy()-> |
1184 RevokeRightsForExtension(extension); | 1183 RevokeRightsForExtension(extension); |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1280 // We need to enable those disabled *only* due to minimum version | 1279 // We need to enable those disabled *only* due to minimum version |
1281 // requirement. | 1280 // requirement. |
1282 to_enable.push_back(extension->id()); | 1281 to_enable.push_back(extension->id()); |
1283 } | 1282 } |
1284 extension_prefs_->RemoveDisableReason( | 1283 extension_prefs_->RemoveDisableReason( |
1285 extension->id(), Extension::DISABLE_UPDATE_REQUIRED_BY_POLICY); | 1284 extension->id(), Extension::DISABLE_UPDATE_REQUIRED_BY_POLICY); |
1286 } | 1285 } |
1287 } | 1286 } |
1288 | 1287 |
1289 for (const std::string& id : to_unload) | 1288 for (const std::string& id : to_unload) |
1290 UnloadExtension(id, UnloadedExtensionInfo::REASON_DISABLE); | 1289 UnloadExtension(id, UnloadedExtensionReason::DISABLE); |
1291 | 1290 |
1292 for (const auto& i : to_disable) | 1291 for (const auto& i : to_disable) |
1293 DisableExtension(i.first, i.second); | 1292 DisableExtension(i.first, i.second); |
1294 | 1293 |
1295 // No extension is getting re-enabled here after disabling/unloading | 1294 // No extension is getting re-enabled here after disabling/unloading |
1296 // because to_enable is mutually exclusive to to_disable + to_unload. | 1295 // because to_enable is mutually exclusive to to_disable + to_unload. |
1297 for (const std::string& id : to_enable) | 1296 for (const std::string& id : to_enable) |
1298 EnableExtension(id); | 1297 EnableExtension(id); |
1299 | 1298 |
1300 if (updater_.get()) { | 1299 if (updater_.get()) { |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1395 ExtensionInfo* info = extensions_info->at(i).get(); | 1394 ExtensionInfo* info = extensions_info->at(i).get(); |
1396 if (Manifest::IsExternalLocation(info->extension_location)) | 1395 if (Manifest::IsExternalLocation(info->extension_location)) |
1397 CheckExternalUninstall(info->extension_id); | 1396 CheckExternalUninstall(info->extension_id); |
1398 } | 1397 } |
1399 | 1398 |
1400 error_controller_->ShowErrorIfNeeded(); | 1399 error_controller_->ShowErrorIfNeeded(); |
1401 | 1400 |
1402 external_install_manager_->UpdateExternalExtensionAlert(); | 1401 external_install_manager_->UpdateExternalExtensionAlert(); |
1403 } | 1402 } |
1404 | 1403 |
1405 void ExtensionService::UnloadExtension( | 1404 void ExtensionService::UnloadExtension(const std::string& extension_id, |
1406 const std::string& extension_id, | 1405 UnloadedExtensionReason reason) { |
1407 UnloadedExtensionInfo::Reason reason) { | |
1408 // Make sure the extension gets deleted after we return from this function. | 1406 // Make sure the extension gets deleted after we return from this function. |
1409 int include_mask = | 1407 int include_mask = |
1410 ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::TERMINATED; | 1408 ExtensionRegistry::EVERYTHING & ~ExtensionRegistry::TERMINATED; |
1411 scoped_refptr<const Extension> extension( | 1409 scoped_refptr<const Extension> extension( |
1412 registry_->GetExtensionById(extension_id, include_mask)); | 1410 registry_->GetExtensionById(extension_id, include_mask)); |
1413 | 1411 |
1414 // This method can be called via PostTask, so the extension may have been | 1412 // This method can be called via PostTask, so the extension may have been |
1415 // unloaded by the time this runs. | 1413 // unloaded by the time this runs. |
1416 if (!extension.get()) { | 1414 if (!extension.get()) { |
1417 // In case the extension may have crashed/uninstalled. Allow the profile to | 1415 // In case the extension may have crashed/uninstalled. Allow the profile to |
(...skipping 26 matching lines...) Expand all Loading... |
1444 content::NotificationService::current()->Notify( | 1442 content::NotificationService::current()->Notify( |
1445 extensions::NOTIFICATION_EXTENSION_REMOVED, | 1443 extensions::NOTIFICATION_EXTENSION_REMOVED, |
1446 content::Source<Profile>(profile_), | 1444 content::Source<Profile>(profile_), |
1447 content::Details<const Extension>(extension.get())); | 1445 content::Details<const Extension>(extension.get())); |
1448 } | 1446 } |
1449 | 1447 |
1450 void ExtensionService::RemoveComponentExtension( | 1448 void ExtensionService::RemoveComponentExtension( |
1451 const std::string& extension_id) { | 1449 const std::string& extension_id) { |
1452 scoped_refptr<const Extension> extension( | 1450 scoped_refptr<const Extension> extension( |
1453 GetExtensionById(extension_id, false)); | 1451 GetExtensionById(extension_id, false)); |
1454 UnloadExtension(extension_id, UnloadedExtensionInfo::REASON_UNINSTALL); | 1452 UnloadExtension(extension_id, UnloadedExtensionReason::UNINSTALL); |
1455 if (extension.get()) { | 1453 if (extension.get()) { |
1456 ExtensionRegistry::Get(profile_)->TriggerOnUninstalled( | 1454 ExtensionRegistry::Get(profile_)->TriggerOnUninstalled( |
1457 extension.get(), extensions::UNINSTALL_REASON_COMPONENT_REMOVED); | 1455 extension.get(), extensions::UNINSTALL_REASON_COMPONENT_REMOVED); |
1458 } | 1456 } |
1459 } | 1457 } |
1460 | 1458 |
1461 void ExtensionService::UnloadAllExtensionsForTest() { | 1459 void ExtensionService::UnloadAllExtensionsForTest() { |
1462 UnloadAllExtensionsInternal(); | 1460 UnloadAllExtensionsInternal(); |
1463 } | 1461 } |
1464 | 1462 |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1561 // If a terminated extension is loaded, remove it from the terminated list. | 1559 // If a terminated extension is loaded, remove it from the terminated list. |
1562 UntrackTerminatedExtension(extension->id()); | 1560 UntrackTerminatedExtension(extension->id()); |
1563 | 1561 |
1564 // Check if the extension's privileges have changed and mark the | 1562 // Check if the extension's privileges have changed and mark the |
1565 // extension disabled if necessary. | 1563 // extension disabled if necessary. |
1566 CheckPermissionsIncrease(extension, is_extension_loaded); | 1564 CheckPermissionsIncrease(extension, is_extension_loaded); |
1567 | 1565 |
1568 if (is_extension_loaded && !reloading) { | 1566 if (is_extension_loaded && !reloading) { |
1569 // To upgrade an extension in place, unload the old one and then load the | 1567 // To upgrade an extension in place, unload the old one and then load the |
1570 // new one. ReloadExtension disables the extension, which is sufficient. | 1568 // new one. ReloadExtension disables the extension, which is sufficient. |
1571 UnloadExtension(extension->id(), UnloadedExtensionInfo::REASON_UPDATE); | 1569 UnloadExtension(extension->id(), UnloadedExtensionReason::UPDATE); |
1572 } | 1570 } |
1573 | 1571 |
1574 if (extension_prefs_->IsExtensionBlacklisted(extension->id())) { | 1572 if (extension_prefs_->IsExtensionBlacklisted(extension->id())) { |
1575 // Only prefs is checked for the blacklist. We rely on callers to check the | 1573 // Only prefs is checked for the blacklist. We rely on callers to check the |
1576 // blacklist before calling into here, e.g. CrxInstaller checks before | 1574 // blacklist before calling into here, e.g. CrxInstaller checks before |
1577 // installation then threads through the install and pending install flow | 1575 // installation then threads through the install and pending install flow |
1578 // of this class, and we check when loading installed extensions. | 1576 // of this class, and we check when loading installed extensions. |
1579 registry_->AddBlacklisted(extension); | 1577 registry_->AddBlacklisted(extension); |
1580 } else if (block_extensions_ && CanBlockExtension(extension)) { | 1578 } else if (block_extensions_ && CanBlockExtension(extension)) { |
1581 registry_->AddBlocked(extension); | 1579 registry_->AddBlocked(extension); |
(...skipping 488 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2070 const std::string& extension_id) { | 2068 const std::string& extension_id) { |
2071 extensions_being_terminated_.erase(extension_id); | 2069 extensions_being_terminated_.erase(extension_id); |
2072 | 2070 |
2073 const Extension* extension = GetInstalledExtension(extension_id); | 2071 const Extension* extension = GetInstalledExtension(extension_id); |
2074 if (!extension) { | 2072 if (!extension) { |
2075 return; | 2073 return; |
2076 } | 2074 } |
2077 | 2075 |
2078 // No need to check for duplicates; inserting a duplicate is a no-op. | 2076 // No need to check for duplicates; inserting a duplicate is a no-op. |
2079 registry_->AddTerminated(make_scoped_refptr(extension)); | 2077 registry_->AddTerminated(make_scoped_refptr(extension)); |
2080 UnloadExtension(extension->id(), UnloadedExtensionInfo::REASON_TERMINATE); | 2078 UnloadExtension(extension->id(), UnloadedExtensionReason::TERMINATE); |
2081 } | 2079 } |
2082 | 2080 |
2083 void ExtensionService::TerminateExtension(const std::string& extension_id) { | 2081 void ExtensionService::TerminateExtension(const std::string& extension_id) { |
2084 TrackTerminatedExtension(extension_id); | 2082 TrackTerminatedExtension(extension_id); |
2085 } | 2083 } |
2086 | 2084 |
2087 void ExtensionService::UntrackTerminatedExtension(const std::string& id) { | 2085 void ExtensionService::UntrackTerminatedExtension(const std::string& id) { |
2088 std::string lowercase_id = base::ToLowerASCII(id); | 2086 std::string lowercase_id = base::ToLowerASCII(id); |
2089 const Extension* extension = | 2087 const Extension* extension = |
2090 registry_->terminated_extensions().GetByID(lowercase_id); | 2088 registry_->terminated_extensions().GetByID(lowercase_id); |
(...skipping 373 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2464 it != not_yet_blocked.end(); ++it) { | 2462 it != not_yet_blocked.end(); ++it) { |
2465 scoped_refptr<const Extension> extension = GetInstalledExtension(*it); | 2463 scoped_refptr<const Extension> extension = GetInstalledExtension(*it); |
2466 if (!extension.get()) { | 2464 if (!extension.get()) { |
2467 NOTREACHED() << "Extension " << *it << " needs to be " | 2465 NOTREACHED() << "Extension " << *it << " needs to be " |
2468 << "blacklisted, but it's not installed."; | 2466 << "blacklisted, but it's not installed."; |
2469 continue; | 2467 continue; |
2470 } | 2468 } |
2471 registry_->AddBlacklisted(extension); | 2469 registry_->AddBlacklisted(extension); |
2472 extension_prefs_->SetExtensionBlacklistState( | 2470 extension_prefs_->SetExtensionBlacklistState( |
2473 extension->id(), extensions::BLACKLISTED_MALWARE); | 2471 extension->id(), extensions::BLACKLISTED_MALWARE); |
2474 UnloadExtension(*it, UnloadedExtensionInfo::REASON_BLACKLIST); | 2472 UnloadExtension(*it, UnloadedExtensionReason::BLACKLIST); |
2475 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlacklistInstalled", | 2473 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlacklistInstalled", |
2476 extension->location(), Manifest::NUM_LOCATIONS); | 2474 extension->location(), Manifest::NUM_LOCATIONS); |
2477 } | 2475 } |
2478 } | 2476 } |
2479 | 2477 |
2480 // TODO(oleg): UMA logging | 2478 // TODO(oleg): UMA logging |
2481 void ExtensionService::UpdateGreylistedExtensions( | 2479 void ExtensionService::UpdateGreylistedExtensions( |
2482 const ExtensionIdSet& greylist, | 2480 const ExtensionIdSet& greylist, |
2483 const ExtensionIdSet& unchanged, | 2481 const ExtensionIdSet& unchanged, |
2484 const extensions::Blacklist::BlacklistStateMap& state_map) { | 2482 const extensions::Blacklist::BlacklistStateMap& state_map) { |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2558 // TODO(erikkay) should there be a notification for this? We can't use | 2556 // TODO(erikkay) should there be a notification for this? We can't use |
2559 // EXTENSION_UNLOADED since that implies that the extension has been disabled | 2557 // EXTENSION_UNLOADED since that implies that the extension has been disabled |
2560 // or uninstalled. | 2558 // or uninstalled. |
2561 } | 2559 } |
2562 | 2560 |
2563 void ExtensionService::OnProfileDestructionStarted() { | 2561 void ExtensionService::OnProfileDestructionStarted() { |
2564 ExtensionIdSet ids_to_unload = registry_->enabled_extensions().GetIDs(); | 2562 ExtensionIdSet ids_to_unload = registry_->enabled_extensions().GetIDs(); |
2565 for (ExtensionIdSet::iterator it = ids_to_unload.begin(); | 2563 for (ExtensionIdSet::iterator it = ids_to_unload.begin(); |
2566 it != ids_to_unload.end(); | 2564 it != ids_to_unload.end(); |
2567 ++it) { | 2565 ++it) { |
2568 UnloadExtension(*it, UnloadedExtensionInfo::REASON_PROFILE_SHUTDOWN); | 2566 UnloadExtension(*it, UnloadedExtensionReason::PROFILE_SHUTDOWN); |
2569 } | 2567 } |
2570 } | 2568 } |
OLD | NEW |