OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 "webkit/appcache/appcache_storage_impl.h" | 5 #include "webkit/appcache/appcache_storage_impl.h" |
6 | 6 |
7 #include "app/sql/connection.h" | 7 #include "app/sql/connection.h" |
8 #include "app/sql/transaction.h" | 8 #include "app/sql/transaction.h" |
9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 857 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
868 base::MessageLoopProxy* cache_thread) { | 868 base::MessageLoopProxy* cache_thread) { |
869 cache_directory_ = cache_directory; | 869 cache_directory_ = cache_directory; |
870 cache_thread_ = cache_thread; | 870 cache_thread_ = cache_thread; |
871 is_incognito_ = cache_directory_.empty(); | 871 is_incognito_ = cache_directory_.empty(); |
872 | 872 |
873 FilePath db_file_path; | 873 FilePath db_file_path; |
874 if (!is_incognito_) | 874 if (!is_incognito_) |
875 db_file_path = cache_directory_.Append(kAppCacheDatabaseName); | 875 db_file_path = cache_directory_.Append(kAppCacheDatabaseName); |
876 database_ = new AppCacheDatabase(db_file_path); | 876 database_ = new AppCacheDatabase(db_file_path); |
877 | 877 |
878 scoped_refptr<InitTask> task = new InitTask(this); | 878 scoped_refptr<InitTask> task(new InitTask(this)); |
879 task->Schedule(); | 879 task->Schedule(); |
880 } | 880 } |
881 | 881 |
882 void AppCacheStorageImpl::Disable() { | 882 void AppCacheStorageImpl::Disable() { |
883 if (is_disabled_) | 883 if (is_disabled_) |
884 return; | 884 return; |
885 VLOG(1) << "Disabling appcache storage."; | 885 VLOG(1) << "Disabling appcache storage."; |
886 is_disabled_ = true; | 886 is_disabled_ = true; |
887 origins_with_groups_.clear(); | 887 origins_with_groups_.clear(); |
888 working_set()->Disable(); | 888 working_set()->Disable(); |
889 if (disk_cache_.get()) | 889 if (disk_cache_.get()) |
890 disk_cache_->Disable(); | 890 disk_cache_->Disable(); |
891 scoped_refptr<DisableDatabaseTask> task = new DisableDatabaseTask(this); | 891 scoped_refptr<DisableDatabaseTask> task(new DisableDatabaseTask(this)); |
892 task->Schedule(); | 892 task->Schedule(); |
893 } | 893 } |
894 | 894 |
895 void AppCacheStorageImpl::GetAllInfo(Delegate* delegate) { | 895 void AppCacheStorageImpl::GetAllInfo(Delegate* delegate) { |
896 DCHECK(delegate); | 896 DCHECK(delegate); |
897 scoped_refptr<GetAllInfoTask> task = new GetAllInfoTask(this); | 897 scoped_refptr<GetAllInfoTask> task(new GetAllInfoTask(this)); |
898 task->AddDelegate(GetOrCreateDelegateReference(delegate)); | 898 task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
899 task->Schedule(); | 899 task->Schedule(); |
900 } | 900 } |
901 | 901 |
902 void AppCacheStorageImpl::LoadCache(int64 id, Delegate* delegate) { | 902 void AppCacheStorageImpl::LoadCache(int64 id, Delegate* delegate) { |
903 DCHECK(delegate); | 903 DCHECK(delegate); |
904 if (is_disabled_) { | 904 if (is_disabled_) { |
905 delegate->OnCacheLoaded(NULL, id); | 905 delegate->OnCacheLoaded(NULL, id); |
906 return; | 906 return; |
907 } | 907 } |
908 | 908 |
909 AppCache* cache = working_set_.GetCache(id); | 909 AppCache* cache = working_set_.GetCache(id); |
910 if (cache) { | 910 if (cache) { |
911 delegate->OnCacheLoaded(cache, id); | 911 delegate->OnCacheLoaded(cache, id); |
912 if (cache->owning_group()) { | 912 if (cache->owning_group()) { |
913 scoped_refptr<DatabaseTask> update_task = | 913 scoped_refptr<DatabaseTask> update_task( |
914 new UpdateGroupLastAccessTimeTask( | 914 new UpdateGroupLastAccessTimeTask( |
915 this, cache->owning_group()->group_id(), base::Time::Now()); | 915 this, cache->owning_group()->group_id(), base::Time::Now())); |
916 update_task->Schedule(); | 916 update_task->Schedule(); |
917 } | 917 } |
918 return; | 918 return; |
919 } | 919 } |
920 scoped_refptr<CacheLoadTask> task = GetPendingCacheLoadTask(id); | 920 scoped_refptr<CacheLoadTask> task(GetPendingCacheLoadTask(id)); |
921 if (task) { | 921 if (task) { |
922 task->AddDelegate(GetOrCreateDelegateReference(delegate)); | 922 task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
923 return; | 923 return; |
924 } | 924 } |
925 task = new CacheLoadTask(id, this); | 925 task = new CacheLoadTask(id, this); |
926 task->AddDelegate(GetOrCreateDelegateReference(delegate)); | 926 task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
927 task->Schedule(); | 927 task->Schedule(); |
928 pending_cache_loads_[id] = task; | 928 pending_cache_loads_[id] = task; |
929 } | 929 } |
930 | 930 |
931 void AppCacheStorageImpl::LoadOrCreateGroup( | 931 void AppCacheStorageImpl::LoadOrCreateGroup( |
932 const GURL& manifest_url, Delegate* delegate) { | 932 const GURL& manifest_url, Delegate* delegate) { |
933 DCHECK(delegate); | 933 DCHECK(delegate); |
934 if (is_disabled_) { | 934 if (is_disabled_) { |
935 delegate->OnGroupLoaded(NULL, manifest_url); | 935 delegate->OnGroupLoaded(NULL, manifest_url); |
936 return; | 936 return; |
937 } | 937 } |
938 | 938 |
939 AppCacheGroup* group = working_set_.GetGroup(manifest_url); | 939 AppCacheGroup* group = working_set_.GetGroup(manifest_url); |
940 if (group) { | 940 if (group) { |
941 delegate->OnGroupLoaded(group, manifest_url); | 941 delegate->OnGroupLoaded(group, manifest_url); |
942 scoped_refptr<DatabaseTask> update_task = | 942 scoped_refptr<DatabaseTask> update_task( |
943 new UpdateGroupLastAccessTimeTask( | 943 new UpdateGroupLastAccessTimeTask( |
944 this, group->group_id(), base::Time::Now()); | 944 this, group->group_id(), base::Time::Now())); |
945 update_task->Schedule(); | 945 update_task->Schedule(); |
946 return; | 946 return; |
947 } | 947 } |
948 | 948 |
949 scoped_refptr<GroupLoadTask> task = GetPendingGroupLoadTask(manifest_url); | 949 scoped_refptr<GroupLoadTask> task(GetPendingGroupLoadTask(manifest_url)); |
950 if (task) { | 950 if (task) { |
951 task->AddDelegate(GetOrCreateDelegateReference(delegate)); | 951 task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
952 return; | 952 return; |
953 } | 953 } |
954 | 954 |
955 if (origins_with_groups_.find(manifest_url.GetOrigin()) == | 955 if (origins_with_groups_.find(manifest_url.GetOrigin()) == |
956 origins_with_groups_.end()) { | 956 origins_with_groups_.end()) { |
957 // No need to query the database, return a new group immediately. | 957 // No need to query the database, return a new group immediately. |
958 scoped_refptr<AppCacheGroup> group = new AppCacheGroup( | 958 scoped_refptr<AppCacheGroup> group(new AppCacheGroup( |
959 service_, manifest_url, NewGroupId()); | 959 service_, manifest_url, NewGroupId())); |
960 delegate->OnGroupLoaded(group, manifest_url); | 960 delegate->OnGroupLoaded(group, manifest_url); |
961 return; | 961 return; |
962 } | 962 } |
963 | 963 |
964 task = new GroupLoadTask(manifest_url, this); | 964 task = new GroupLoadTask(manifest_url, this); |
965 task->AddDelegate(GetOrCreateDelegateReference(delegate)); | 965 task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
966 task->Schedule(); | 966 task->Schedule(); |
967 pending_group_loads_[manifest_url] = task.get(); | 967 pending_group_loads_[manifest_url] = task.get(); |
968 } | 968 } |
969 | 969 |
970 void AppCacheStorageImpl::StoreGroupAndNewestCache( | 970 void AppCacheStorageImpl::StoreGroupAndNewestCache( |
971 AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) { | 971 AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) { |
972 // TODO(michaeln): distinguish between a simple update of an existing | 972 // TODO(michaeln): distinguish between a simple update of an existing |
973 // cache that just adds new master entry(s), and the insertion of a | 973 // cache that just adds new master entry(s), and the insertion of a |
974 // whole new cache. The StoreGroupAndCacheTask as written will handle | 974 // whole new cache. The StoreGroupAndCacheTask as written will handle |
975 // the simple update case in a very heavy weight way (delete all and | 975 // the simple update case in a very heavy weight way (delete all and |
976 // the reinsert all over again). | 976 // the reinsert all over again). |
977 DCHECK(group && delegate && newest_cache); | 977 DCHECK(group && delegate && newest_cache); |
978 scoped_refptr<StoreGroupAndCacheTask> task = | 978 scoped_refptr<StoreGroupAndCacheTask> task( |
979 new StoreGroupAndCacheTask(this, group, newest_cache); | 979 new StoreGroupAndCacheTask(this, group, newest_cache)); |
980 task->AddDelegate(GetOrCreateDelegateReference(delegate)); | 980 task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
981 task->Schedule(); | 981 task->Schedule(); |
982 } | 982 } |
983 | 983 |
984 void AppCacheStorageImpl::FindResponseForMainRequest( | 984 void AppCacheStorageImpl::FindResponseForMainRequest( |
985 const GURL& url, Delegate* delegate) { | 985 const GURL& url, Delegate* delegate) { |
986 DCHECK(delegate); | 986 DCHECK(delegate); |
987 | 987 |
988 const GURL* url_ptr = &url; | 988 const GURL* url_ptr = &url; |
989 GURL url_no_ref; | 989 GURL url_no_ref; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1027 scoped_refptr<AppCacheGroup> no_group; | 1027 scoped_refptr<AppCacheGroup> no_group; |
1028 scoped_refptr<AppCache> no_cache; | 1028 scoped_refptr<AppCache> no_cache; |
1029 ScheduleSimpleTask(method_factory_.NewRunnableMethod( | 1029 ScheduleSimpleTask(method_factory_.NewRunnableMethod( |
1030 &AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse, | 1030 &AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse, |
1031 url, AppCacheEntry(), no_group, no_cache, | 1031 url, AppCacheEntry(), no_group, no_cache, |
1032 make_scoped_refptr(GetOrCreateDelegateReference(delegate)))); | 1032 make_scoped_refptr(GetOrCreateDelegateReference(delegate)))); |
1033 return; | 1033 return; |
1034 } | 1034 } |
1035 | 1035 |
1036 // We have to query the database, schedule a database task to do so. | 1036 // We have to query the database, schedule a database task to do so. |
1037 scoped_refptr<FindMainResponseTask> task = | 1037 scoped_refptr<FindMainResponseTask> task( |
1038 new FindMainResponseTask(this, *url_ptr, groups_in_use); | 1038 new FindMainResponseTask(this, *url_ptr, groups_in_use)); |
1039 task->AddDelegate(GetOrCreateDelegateReference(delegate)); | 1039 task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
1040 task->Schedule(); | 1040 task->Schedule(); |
1041 } | 1041 } |
1042 | 1042 |
1043 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse( | 1043 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse( |
1044 const GURL& url, AppCacheEntry found_entry, | 1044 const GURL& url, AppCacheEntry found_entry, |
1045 scoped_refptr<AppCacheGroup> group, scoped_refptr<AppCache> cache, | 1045 scoped_refptr<AppCacheGroup> group, scoped_refptr<AppCache> cache, |
1046 scoped_refptr<DelegateReference> delegate_ref) { | 1046 scoped_refptr<DelegateReference> delegate_ref) { |
1047 if (delegate_ref->delegate) { | 1047 if (delegate_ref->delegate) { |
1048 DelegateReferenceVector delegates(1, delegate_ref); | 1048 DelegateReferenceVector delegates(1, delegate_ref); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1099 | 1099 |
1100 void AppCacheStorageImpl::MarkEntryAsForeign( | 1100 void AppCacheStorageImpl::MarkEntryAsForeign( |
1101 const GURL& entry_url, int64 cache_id) { | 1101 const GURL& entry_url, int64 cache_id) { |
1102 AppCache* cache = working_set_.GetCache(cache_id); | 1102 AppCache* cache = working_set_.GetCache(cache_id); |
1103 if (cache) { | 1103 if (cache) { |
1104 AppCacheEntry* entry = cache->GetEntry(entry_url); | 1104 AppCacheEntry* entry = cache->GetEntry(entry_url); |
1105 DCHECK(entry); | 1105 DCHECK(entry); |
1106 if (entry) | 1106 if (entry) |
1107 entry->add_types(AppCacheEntry::FOREIGN); | 1107 entry->add_types(AppCacheEntry::FOREIGN); |
1108 } | 1108 } |
1109 scoped_refptr<MarkEntryAsForeignTask> task = | 1109 scoped_refptr<MarkEntryAsForeignTask> task( |
1110 new MarkEntryAsForeignTask(this, entry_url, cache_id); | 1110 new MarkEntryAsForeignTask(this, entry_url, cache_id)); |
1111 task->Schedule(); | 1111 task->Schedule(); |
1112 pending_foreign_markings_.push_back(std::make_pair(entry_url, cache_id)); | 1112 pending_foreign_markings_.push_back(std::make_pair(entry_url, cache_id)); |
1113 } | 1113 } |
1114 | 1114 |
1115 void AppCacheStorageImpl::MakeGroupObsolete( | 1115 void AppCacheStorageImpl::MakeGroupObsolete( |
1116 AppCacheGroup* group, Delegate* delegate) { | 1116 AppCacheGroup* group, Delegate* delegate) { |
1117 DCHECK(group && delegate); | 1117 DCHECK(group && delegate); |
1118 scoped_refptr<MakeGroupObsoleteTask> task = | 1118 scoped_refptr<MakeGroupObsoleteTask> task( |
1119 new MakeGroupObsoleteTask(this, group); | 1119 new MakeGroupObsoleteTask(this, group)); |
1120 task->AddDelegate(GetOrCreateDelegateReference(delegate)); | 1120 task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
1121 task->Schedule(); | 1121 task->Schedule(); |
1122 } | 1122 } |
1123 | 1123 |
1124 AppCacheResponseReader* AppCacheStorageImpl::CreateResponseReader( | 1124 AppCacheResponseReader* AppCacheStorageImpl::CreateResponseReader( |
1125 const GURL& manifest_url, int64 response_id) { | 1125 const GURL& manifest_url, int64 response_id) { |
1126 return new AppCacheResponseReader(response_id, disk_cache()); | 1126 return new AppCacheResponseReader(response_id, disk_cache()); |
1127 } | 1127 } |
1128 | 1128 |
1129 AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter( | 1129 AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter( |
1130 const GURL& manifest_url) { | 1130 const GURL& manifest_url) { |
1131 return new AppCacheResponseWriter(NewResponseId(), disk_cache()); | 1131 return new AppCacheResponseWriter(NewResponseId(), disk_cache()); |
1132 } | 1132 } |
1133 | 1133 |
1134 void AppCacheStorageImpl::DoomResponses( | 1134 void AppCacheStorageImpl::DoomResponses( |
1135 const GURL& manifest_url, const std::vector<int64>& response_ids) { | 1135 const GURL& manifest_url, const std::vector<int64>& response_ids) { |
1136 if (response_ids.empty()) | 1136 if (response_ids.empty()) |
1137 return; | 1137 return; |
1138 | 1138 |
1139 // Start deleting them from the disk cache lazily. | 1139 // Start deleting them from the disk cache lazily. |
1140 StartDeletingResponses(response_ids); | 1140 StartDeletingResponses(response_ids); |
1141 | 1141 |
1142 // Also schedule a database task to record these ids in the | 1142 // Also schedule a database task to record these ids in the |
1143 // deletable responses table. | 1143 // deletable responses table. |
1144 // TODO(michaeln): There is a race here. If the browser crashes | 1144 // TODO(michaeln): There is a race here. If the browser crashes |
1145 // prior to committing these rows to the database and prior to us | 1145 // prior to committing these rows to the database and prior to us |
1146 // having deleted them from the disk cache, we'll never delete them. | 1146 // having deleted them from the disk cache, we'll never delete them. |
1147 scoped_refptr<InsertDeletableResponseIdsTask> task = | 1147 scoped_refptr<InsertDeletableResponseIdsTask> task( |
1148 new InsertDeletableResponseIdsTask(this); | 1148 new InsertDeletableResponseIdsTask(this)); |
1149 task->response_ids_ = response_ids; | 1149 task->response_ids_ = response_ids; |
1150 task->Schedule(); | 1150 task->Schedule(); |
1151 } | 1151 } |
1152 | 1152 |
1153 void AppCacheStorageImpl::DeleteResponses( | 1153 void AppCacheStorageImpl::DeleteResponses( |
1154 const GURL& manifest_url, const std::vector<int64>& response_ids) { | 1154 const GURL& manifest_url, const std::vector<int64>& response_ids) { |
1155 if (response_ids.empty()) | 1155 if (response_ids.empty()) |
1156 return; | 1156 return; |
1157 StartDeletingResponses(response_ids); | 1157 StartDeletingResponses(response_ids); |
1158 } | 1158 } |
1159 | 1159 |
1160 void AppCacheStorageImpl::PurgeMemory() { | 1160 void AppCacheStorageImpl::PurgeMemory() { |
1161 scoped_refptr<CloseConnectionTask> task = new CloseConnectionTask(this); | 1161 scoped_refptr<CloseConnectionTask> task(new CloseConnectionTask(this)); |
1162 task->Schedule(); | 1162 task->Schedule(); |
1163 } | 1163 } |
1164 | 1164 |
1165 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() { | 1165 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() { |
1166 // Only if we haven't already begun. | 1166 // Only if we haven't already begun. |
1167 if (!did_start_deleting_responses_) { | 1167 if (!did_start_deleting_responses_) { |
1168 scoped_refptr<GetDeletableResponseIdsTask> task = | 1168 scoped_refptr<GetDeletableResponseIdsTask> task( |
1169 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_); | 1169 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_)); |
1170 task->Schedule(); | 1170 task->Schedule(); |
1171 } | 1171 } |
1172 } | 1172 } |
1173 | 1173 |
1174 void AppCacheStorageImpl::StartDeletingResponses( | 1174 void AppCacheStorageImpl::StartDeletingResponses( |
1175 const std::vector<int64>& response_ids) { | 1175 const std::vector<int64>& response_ids) { |
1176 DCHECK(!response_ids.empty()); | 1176 DCHECK(!response_ids.empty()); |
1177 did_start_deleting_responses_ = true; | 1177 did_start_deleting_responses_ = true; |
1178 deletable_response_ids_.insert( | 1178 deletable_response_ids_.insert( |
1179 deletable_response_ids_.end(), | 1179 deletable_response_ids_.end(), |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1216 return; | 1216 return; |
1217 | 1217 |
1218 int64 id = deletable_response_ids_.front(); | 1218 int64 id = deletable_response_ids_.front(); |
1219 deletable_response_ids_.pop_front(); | 1219 deletable_response_ids_.pop_front(); |
1220 if (rv != net::ERR_ABORTED) | 1220 if (rv != net::ERR_ABORTED) |
1221 deleted_response_ids_.push_back(id); | 1221 deleted_response_ids_.push_back(id); |
1222 | 1222 |
1223 const size_t kBatchSize = 50U; | 1223 const size_t kBatchSize = 50U; |
1224 if (deleted_response_ids_.size() >= kBatchSize || | 1224 if (deleted_response_ids_.size() >= kBatchSize || |
1225 deletable_response_ids_.empty()) { | 1225 deletable_response_ids_.empty()) { |
1226 scoped_refptr<DeleteDeletableResponseIdsTask> task = | 1226 scoped_refptr<DeleteDeletableResponseIdsTask> task( |
1227 new DeleteDeletableResponseIdsTask(this); | 1227 new DeleteDeletableResponseIdsTask(this)); |
1228 task->response_ids_.swap(deleted_response_ids_); | 1228 task->response_ids_.swap(deleted_response_ids_); |
1229 task->Schedule(); | 1229 task->Schedule(); |
1230 } | 1230 } |
1231 | 1231 |
1232 if (deletable_response_ids_.empty()) { | 1232 if (deletable_response_ids_.empty()) { |
1233 scoped_refptr<GetDeletableResponseIdsTask> task = | 1233 scoped_refptr<GetDeletableResponseIdsTask> task( |
1234 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_); | 1234 new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_)); |
1235 task->Schedule(); | 1235 task->Schedule(); |
1236 return; | 1236 return; |
1237 } | 1237 } |
1238 | 1238 |
1239 ScheduleDeleteOneResponse(); | 1239 ScheduleDeleteOneResponse(); |
1240 } | 1240 } |
1241 | 1241 |
1242 AppCacheStorageImpl::CacheLoadTask* | 1242 AppCacheStorageImpl::CacheLoadTask* |
1243 AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id) { | 1243 AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id) { |
1244 PendingCacheLoads::iterator found = pending_cache_loads_.find(cache_id); | 1244 PendingCacheLoads::iterator found = pending_cache_loads_.find(cache_id); |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1319 Disable(); | 1319 Disable(); |
1320 if (!is_incognito_) { | 1320 if (!is_incognito_) { |
1321 VLOG(1) << "Deleting existing appcache data and starting over."; | 1321 VLOG(1) << "Deleting existing appcache data and starting over."; |
1322 AppCacheThread::PostTask(AppCacheThread::db(), FROM_HERE, | 1322 AppCacheThread::PostTask(AppCacheThread::db(), FROM_HERE, |
1323 NewRunnableFunction(DeleteDirectory, cache_directory_)); | 1323 NewRunnableFunction(DeleteDirectory, cache_directory_)); |
1324 } | 1324 } |
1325 } | 1325 } |
1326 } | 1326 } |
1327 | 1327 |
1328 } // namespace appcache | 1328 } // namespace appcache |
OLD | NEW |