OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "content/browser/background_sync/background_sync_manager.h" | 5 #include "content/browser/background_sync/background_sync_manager.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/barrier_closure.h" | 9 #include "base/barrier_closure.h" |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/location.h" | 11 #include "base/location.h" |
12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
13 #include "base/single_thread_task_runner.h" | 13 #include "base/single_thread_task_runner.h" |
14 #include "base/thread_task_runner_handle.h" | 14 #include "base/thread_task_runner_handle.h" |
15 #include "base/time/default_clock.h" | 15 #include "base/time/default_clock.h" |
16 #include "build/build_config.h" | 16 #include "build/build_config.h" |
17 #include "content/browser/background_sync/background_sync_metrics.h" | 17 #include "content/browser/background_sync/background_sync_metrics.h" |
18 #include "content/browser/background_sync/background_sync_network_observer.h" | 18 #include "content/browser/background_sync/background_sync_network_observer.h" |
19 #include "content/browser/background_sync/background_sync_registration_options.h
" | 19 #include "content/browser/background_sync/background_sync_registration_options.h
" |
20 #include "content/browser/service_worker/service_worker_context_wrapper.h" | 20 #include "content/browser/service_worker/service_worker_context_wrapper.h" |
21 #include "content/browser/service_worker/service_worker_storage.h" | 21 #include "content/browser/service_worker/service_worker_storage.h" |
22 #include "content/browser/storage_partition_impl.h" | 22 #include "content/browser/storage_partition_impl.h" |
23 #include "content/common/service_worker/service_worker_type_converters.h" | 23 #include "content/common/service_worker/service_worker_type_converters.h" |
24 #include "content/public/browser/background_sync_controller.h" | 24 #include "content/public/browser/background_sync_controller.h" |
25 #include "content/public/browser/browser_context.h" | 25 #include "content/public/browser/browser_context.h" |
26 #include "content/public/browser/browser_thread.h" | 26 #include "content/public/browser/browser_thread.h" |
27 #include "content/public/browser/permission_manager.h" | 27 #include "content/public/browser/permission_manager.h" |
28 #include "content/public/browser/permission_type.h" | 28 #include "content/public/browser/permission_type.h" |
29 #include "content/public/common/background_sync.mojom.h" | |
30 | 29 |
31 #if defined(OS_ANDROID) | 30 #if defined(OS_ANDROID) |
32 #include "content/browser/android/background_sync_network_observer_android.h" | 31 #include "content/browser/android/background_sync_network_observer_android.h" |
33 #endif | 32 #endif |
34 | 33 |
35 namespace content { | 34 namespace content { |
36 | 35 |
37 namespace { | 36 namespace { |
38 | 37 |
39 // The key used to index the background sync data in ServiceWorkerStorage. | 38 // The key used to index the background sync data in ServiceWorkerStorage. |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 } | 142 } |
144 | 143 |
145 background_sync_controller->GetParameterOverrides(parameters.get()); | 144 background_sync_controller->GetParameterOverrides(parameters.get()); |
146 return parameters; | 145 return parameters; |
147 } | 146 } |
148 | 147 |
149 void OnSyncEventFinished( | 148 void OnSyncEventFinished( |
150 const scoped_refptr<ServiceWorkerVersion>& active_version, | 149 const scoped_refptr<ServiceWorkerVersion>& active_version, |
151 int request_id, | 150 int request_id, |
152 const ServiceWorkerVersion::StatusCallback& callback, | 151 const ServiceWorkerVersion::StatusCallback& callback, |
153 mojom::ServiceWorkerEventStatus status) { | 152 blink::mojom::ServiceWorkerEventStatus status) { |
154 if (!active_version->FinishRequest( | 153 if (!active_version->FinishRequest( |
155 request_id, | 154 request_id, |
156 status == content::mojom::ServiceWorkerEventStatus::COMPLETED)) { | 155 status == blink::mojom::ServiceWorkerEventStatus::COMPLETED)) { |
157 return; | 156 return; |
158 } | 157 } |
159 callback.Run(mojo::ConvertTo<ServiceWorkerStatusCode>(status)); | 158 callback.Run(mojo::ConvertTo<ServiceWorkerStatusCode>(status)); |
160 } | 159 } |
161 | 160 |
162 } // namespace | 161 } // namespace |
163 | 162 |
164 BackgroundSyncManager::BackgroundSyncRegistrations:: | 163 BackgroundSyncManager::BackgroundSyncRegistrations:: |
165 BackgroundSyncRegistrations() | 164 BackgroundSyncRegistrations() |
166 : next_id(BackgroundSyncRegistration::kInitialId) { | 165 : next_id(BackgroundSyncRegistration::kInitialId) { |
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire = | 497 BackgroundSyncMetrics::RegistrationCouldFire registration_could_fire = |
499 AreOptionConditionsMet(options) | 498 AreOptionConditionsMet(options) |
500 ? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE | 499 ? BackgroundSyncMetrics::REGISTRATION_COULD_FIRE |
501 : BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE; | 500 : BackgroundSyncMetrics::REGISTRATION_COULD_NOT_FIRE; |
502 BackgroundSyncMetrics::CountRegisterSuccess( | 501 BackgroundSyncMetrics::CountRegisterSuccess( |
503 registration_could_fire, | 502 registration_could_fire, |
504 BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE); | 503 BackgroundSyncMetrics::REGISTRATION_IS_DUPLICATE); |
505 | 504 |
506 if (existing_registration->IsFiring()) { | 505 if (existing_registration->IsFiring()) { |
507 existing_registration->set_sync_state( | 506 existing_registration->set_sync_state( |
508 mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING); | 507 blink::mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING); |
509 } | 508 } |
510 | 509 |
511 base::ThreadTaskRunnerHandle::Get()->PostTask( | 510 base::ThreadTaskRunnerHandle::Get()->PostTask( |
512 FROM_HERE, | 511 FROM_HERE, |
513 base::Bind(callback, BACKGROUND_SYNC_STATUS_OK, | 512 base::Bind(callback, BACKGROUND_SYNC_STATUS_OK, |
514 base::Passed(base::WrapUnique(new BackgroundSyncRegistration( | 513 base::Passed(base::WrapUnique(new BackgroundSyncRegistration( |
515 *existing_registration))))); | 514 *existing_registration))))); |
516 return; | 515 return; |
517 } | 516 } |
518 | 517 |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
731 callback) { | 730 callback) { |
732 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 731 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
733 | 732 |
734 service_worker_context_->GetUserDataForAllRegistrations(backend_key, | 733 service_worker_context_->GetUserDataForAllRegistrations(backend_key, |
735 callback); | 734 callback); |
736 } | 735 } |
737 | 736 |
738 void BackgroundSyncManager::DispatchSyncEvent( | 737 void BackgroundSyncManager::DispatchSyncEvent( |
739 const std::string& tag, | 738 const std::string& tag, |
740 const scoped_refptr<ServiceWorkerVersion>& active_version, | 739 const scoped_refptr<ServiceWorkerVersion>& active_version, |
741 mojom::BackgroundSyncEventLastChance last_chance, | 740 blink::mojom::BackgroundSyncEventLastChance last_chance, |
742 const ServiceWorkerVersion::StatusCallback& callback) { | 741 const ServiceWorkerVersion::StatusCallback& callback) { |
743 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 742 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
744 DCHECK(active_version); | 743 DCHECK(active_version); |
745 | 744 |
746 if (active_version->running_status() != ServiceWorkerVersion::RUNNING) { | 745 if (active_version->running_status() != ServiceWorkerVersion::RUNNING) { |
747 active_version->RunAfterStartWorker( | 746 active_version->RunAfterStartWorker( |
748 ServiceWorkerMetrics::EventType::SYNC, | 747 ServiceWorkerMetrics::EventType::SYNC, |
749 base::Bind(&BackgroundSyncManager::DispatchSyncEvent, | 748 base::Bind(&BackgroundSyncManager::DispatchSyncEvent, |
750 weak_ptr_factory_.GetWeakPtr(), tag, active_version, | 749 weak_ptr_factory_.GetWeakPtr(), tag, active_version, |
751 last_chance, callback), | 750 last_chance, callback), |
752 callback); | 751 callback); |
753 return; | 752 return; |
754 } | 753 } |
755 | 754 |
756 int request_id = active_version->StartRequestWithCustomTimeout( | 755 int request_id = active_version->StartRequestWithCustomTimeout( |
757 ServiceWorkerMetrics::EventType::SYNC, callback, | 756 ServiceWorkerMetrics::EventType::SYNC, callback, |
758 parameters_->max_sync_event_duration, | 757 parameters_->max_sync_event_duration, |
759 ServiceWorkerVersion::CONTINUE_ON_TIMEOUT); | 758 ServiceWorkerVersion::CONTINUE_ON_TIMEOUT); |
760 base::WeakPtr<mojom::BackgroundSyncServiceClient> client = | 759 base::WeakPtr<blink::mojom::BackgroundSyncServiceClient> client = |
761 active_version | 760 active_version |
762 ->GetMojoServiceForRequest<mojom::BackgroundSyncServiceClient>( | 761 ->GetMojoServiceForRequest<blink::mojom::BackgroundSyncServiceClient>( |
763 request_id); | 762 request_id); |
764 | 763 |
765 client->Sync( | 764 client->Sync( |
766 tag, last_chance, | 765 tag, last_chance, |
767 base::Bind(&OnSyncEventFinished, active_version, request_id, callback)); | 766 base::Bind(&OnSyncEventFinished, active_version, request_id, callback)); |
768 } | 767 } |
769 | 768 |
770 void BackgroundSyncManager::ScheduleDelayedTask(const base::Closure& callback, | 769 void BackgroundSyncManager::ScheduleDelayedTask(const base::Closure& callback, |
771 base::TimeDelta delay) { | 770 base::TimeDelta delay) { |
772 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, callback, | 771 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, callback, |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
816 bool BackgroundSyncManager::AreOptionConditionsMet( | 815 bool BackgroundSyncManager::AreOptionConditionsMet( |
817 const BackgroundSyncRegistrationOptions& options) { | 816 const BackgroundSyncRegistrationOptions& options) { |
818 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 817 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
819 return network_observer_->NetworkSufficient(options.network_state); | 818 return network_observer_->NetworkSufficient(options.network_state); |
820 } | 819 } |
821 | 820 |
822 bool BackgroundSyncManager::IsRegistrationReadyToFire( | 821 bool BackgroundSyncManager::IsRegistrationReadyToFire( |
823 const BackgroundSyncRegistration& registration) { | 822 const BackgroundSyncRegistration& registration) { |
824 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 823 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
825 | 824 |
826 if (registration.sync_state() != mojom::BackgroundSyncState::PENDING) | 825 if (registration.sync_state() != blink::mojom::BackgroundSyncState::PENDING) |
827 return false; | 826 return false; |
828 | 827 |
829 if (clock_->Now() < registration.delay_until()) | 828 if (clock_->Now() < registration.delay_until()) |
830 return false; | 829 return false; |
831 | 830 |
832 return AreOptionConditionsMet(*registration.options()); | 831 return AreOptionConditionsMet(*registration.options()); |
833 } | 832 } |
834 | 833 |
835 void BackgroundSyncManager::RunInBackgroundIfNecessary() { | 834 void BackgroundSyncManager::RunInBackgroundIfNecessary() { |
836 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 835 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
837 base::TimeDelta soonest_wakeup_delta = base::TimeDelta::Max(); | 836 base::TimeDelta soonest_wakeup_delta = base::TimeDelta::Max(); |
838 | 837 |
839 for (const auto& sw_id_and_registrations : active_registrations_) { | 838 for (const auto& sw_id_and_registrations : active_registrations_) { |
840 for (const auto& key_and_registration : | 839 for (const auto& key_and_registration : |
841 sw_id_and_registrations.second.registration_map) { | 840 sw_id_and_registrations.second.registration_map) { |
842 const BackgroundSyncRegistration& registration = | 841 const BackgroundSyncRegistration& registration = |
843 key_and_registration.second; | 842 key_and_registration.second; |
844 if (registration.sync_state() == mojom::BackgroundSyncState::PENDING) { | 843 if (registration.sync_state() == |
| 844 blink::mojom::BackgroundSyncState::PENDING) { |
845 if (clock_->Now() >= registration.delay_until()) { | 845 if (clock_->Now() >= registration.delay_until()) { |
846 soonest_wakeup_delta = base::TimeDelta(); | 846 soonest_wakeup_delta = base::TimeDelta(); |
847 } else { | 847 } else { |
848 base::TimeDelta delay_delta = | 848 base::TimeDelta delay_delta = |
849 registration.delay_until() - clock_->Now(); | 849 registration.delay_until() - clock_->Now(); |
850 if (delay_delta < soonest_wakeup_delta) | 850 if (delay_delta < soonest_wakeup_delta) |
851 soonest_wakeup_delta = delay_delta; | 851 soonest_wakeup_delta = delay_delta; |
852 } | 852 } |
853 } | 853 } |
854 } | 854 } |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
905 const int64_t service_worker_id = sw_id_and_registrations.first; | 905 const int64_t service_worker_id = sw_id_and_registrations.first; |
906 for (auto& key_and_registration : | 906 for (auto& key_and_registration : |
907 sw_id_and_registrations.second.registration_map) { | 907 sw_id_and_registrations.second.registration_map) { |
908 BackgroundSyncRegistration* registration = &key_and_registration.second; | 908 BackgroundSyncRegistration* registration = &key_and_registration.second; |
909 if (IsRegistrationReadyToFire(*registration)) { | 909 if (IsRegistrationReadyToFire(*registration)) { |
910 sw_id_and_tags_to_fire.push_back( | 910 sw_id_and_tags_to_fire.push_back( |
911 std::make_pair(service_worker_id, key_and_registration.first)); | 911 std::make_pair(service_worker_id, key_and_registration.first)); |
912 // The state change is not saved to persistent storage because | 912 // The state change is not saved to persistent storage because |
913 // if the sync event is killed mid-sync then it should return to | 913 // if the sync event is killed mid-sync then it should return to |
914 // SYNC_STATE_PENDING. | 914 // SYNC_STATE_PENDING. |
915 registration->set_sync_state(mojom::BackgroundSyncState::FIRING); | 915 registration->set_sync_state(blink::mojom::BackgroundSyncState::FIRING); |
916 } | 916 } |
917 } | 917 } |
918 } | 918 } |
919 | 919 |
920 if (sw_id_and_tags_to_fire.empty()) { | 920 if (sw_id_and_tags_to_fire.empty()) { |
921 RunInBackgroundIfNecessary(); | 921 RunInBackgroundIfNecessary(); |
922 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | 922 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
923 base::Bind(callback)); | 923 base::Bind(callback)); |
924 return; | 924 return; |
925 } | 925 } |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
970 FROM_HERE, base::Bind(event_completed_callback)); | 970 FROM_HERE, base::Bind(event_completed_callback)); |
971 return; | 971 return; |
972 } | 972 } |
973 | 973 |
974 BackgroundSyncRegistration* registration = | 974 BackgroundSyncRegistration* registration = |
975 LookupActiveRegistration(service_worker_registration->id(), tag); | 975 LookupActiveRegistration(service_worker_registration->id(), tag); |
976 DCHECK(registration); | 976 DCHECK(registration); |
977 | 977 |
978 num_firing_registrations_ += 1; | 978 num_firing_registrations_ += 1; |
979 | 979 |
980 mojom::BackgroundSyncEventLastChance last_chance = | 980 blink::mojom::BackgroundSyncEventLastChance last_chance = |
981 registration->num_attempts() == parameters_->max_sync_attempts - 1 | 981 registration->num_attempts() == parameters_->max_sync_attempts - 1 |
982 ? mojom::BackgroundSyncEventLastChance::IS_LAST_CHANCE | 982 ? blink::mojom::BackgroundSyncEventLastChance::IS_LAST_CHANCE |
983 : mojom::BackgroundSyncEventLastChance::IS_NOT_LAST_CHANCE; | 983 : blink::mojom::BackgroundSyncEventLastChance::IS_NOT_LAST_CHANCE; |
984 | 984 |
985 HasMainFrameProviderHost( | 985 HasMainFrameProviderHost( |
986 service_worker_registration->pattern().GetOrigin(), | 986 service_worker_registration->pattern().GetOrigin(), |
987 base::Bind(&BackgroundSyncMetrics::RecordEventStarted)); | 987 base::Bind(&BackgroundSyncMetrics::RecordEventStarted)); |
988 | 988 |
989 DispatchSyncEvent( | 989 DispatchSyncEvent( |
990 registration->options()->tag, | 990 registration->options()->tag, |
991 service_worker_registration->active_version(), last_chance, | 991 service_worker_registration->active_version(), last_chance, |
992 base::Bind(&BackgroundSyncManager::EventComplete, | 992 base::Bind(&BackgroundSyncManager::EventComplete, |
993 weak_ptr_factory_.GetWeakPtr(), service_worker_registration, | 993 weak_ptr_factory_.GetWeakPtr(), service_worker_registration, |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1044 num_firing_registrations_ -= 1; | 1044 num_firing_registrations_ -= 1; |
1045 | 1045 |
1046 BackgroundSyncRegistration* registration = | 1046 BackgroundSyncRegistration* registration = |
1047 LookupActiveRegistration(service_worker_id, tag); | 1047 LookupActiveRegistration(service_worker_id, tag); |
1048 if (!registration) { | 1048 if (!registration) { |
1049 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, | 1049 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
1050 base::Bind(callback)); | 1050 base::Bind(callback)); |
1051 return; | 1051 return; |
1052 } | 1052 } |
1053 | 1053 |
1054 DCHECK_NE(mojom::BackgroundSyncState::PENDING, registration->sync_state()); | 1054 DCHECK_NE(blink::mojom::BackgroundSyncState::PENDING, |
| 1055 registration->sync_state()); |
1055 | 1056 |
1056 registration->set_num_attempts(registration->num_attempts() + 1); | 1057 registration->set_num_attempts(registration->num_attempts() + 1); |
1057 | 1058 |
1058 // The event ran to completion, we should count it, no matter what happens | 1059 // The event ran to completion, we should count it, no matter what happens |
1059 // from here. | 1060 // from here. |
1060 ServiceWorkerRegistration* sw_registration = | 1061 ServiceWorkerRegistration* sw_registration = |
1061 service_worker_context_->GetLiveRegistration(service_worker_id); | 1062 service_worker_context_->GetLiveRegistration(service_worker_id); |
1062 if (sw_registration) { | 1063 if (sw_registration) { |
1063 HasMainFrameProviderHost( | 1064 HasMainFrameProviderHost( |
1064 sw_registration->pattern().GetOrigin(), | 1065 sw_registration->pattern().GetOrigin(), |
1065 base::Bind(&BackgroundSyncMetrics::RecordEventResult, | 1066 base::Bind(&BackgroundSyncMetrics::RecordEventResult, |
1066 status_code == SERVICE_WORKER_OK)); | 1067 status_code == SERVICE_WORKER_OK)); |
1067 } | 1068 } |
1068 | 1069 |
1069 bool registration_completed = true; | 1070 bool registration_completed = true; |
1070 bool can_retry = | 1071 bool can_retry = |
1071 registration->num_attempts() < parameters_->max_sync_attempts; | 1072 registration->num_attempts() < parameters_->max_sync_attempts; |
1072 | 1073 |
1073 if (registration->sync_state() == | 1074 if (registration->sync_state() == |
1074 mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING) { | 1075 blink::mojom::BackgroundSyncState::REREGISTERED_WHILE_FIRING) { |
1075 registration->set_sync_state(mojom::BackgroundSyncState::PENDING); | 1076 registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING); |
1076 registration->set_num_attempts(0); | 1077 registration->set_num_attempts(0); |
1077 registration_completed = false; | 1078 registration_completed = false; |
1078 } else if (status_code != SERVICE_WORKER_OK && | 1079 } else if (status_code != SERVICE_WORKER_OK && |
1079 can_retry) { // Sync failed but can retry | 1080 can_retry) { // Sync failed but can retry |
1080 registration->set_sync_state(mojom::BackgroundSyncState::PENDING); | 1081 registration->set_sync_state(blink::mojom::BackgroundSyncState::PENDING); |
1081 registration->set_delay_until(clock_->Now() + | 1082 registration->set_delay_until(clock_->Now() + |
1082 parameters_->initial_retry_delay * | 1083 parameters_->initial_retry_delay * |
1083 pow(parameters_->retry_delay_factor, | 1084 pow(parameters_->retry_delay_factor, |
1084 registration->num_attempts() - 1)); | 1085 registration->num_attempts() - 1)); |
1085 registration_completed = false; | 1086 registration_completed = false; |
1086 } | 1087 } |
1087 | 1088 |
1088 if (registration_completed) { | 1089 if (registration_completed) { |
1089 const std::string& tag = registration->options()->tag; | 1090 const std::string& tag = registration->options()->tag; |
1090 BackgroundSyncRegistration* active_registration = | 1091 BackgroundSyncRegistration* active_registration = |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1242 BackgroundSyncManager::MakeStatusCompletion(const StatusCallback& callback) { | 1243 BackgroundSyncManager::MakeStatusCompletion(const StatusCallback& callback) { |
1243 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 1244 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
1244 | 1245 |
1245 return base::Bind( | 1246 return base::Bind( |
1246 &BackgroundSyncManager::CompleteOperationCallback<StatusCallback, | 1247 &BackgroundSyncManager::CompleteOperationCallback<StatusCallback, |
1247 BackgroundSyncStatus>, | 1248 BackgroundSyncStatus>, |
1248 weak_ptr_factory_.GetWeakPtr(), callback); | 1249 weak_ptr_factory_.GetWeakPtr(), callback); |
1249 } | 1250 } |
1250 | 1251 |
1251 } // namespace content | 1252 } // namespace content |
OLD | NEW |