Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/service_worker/service_worker_disk_cache_migrator.h" | |
| 6 | |
| 7 #include "base/memory/ref_counted.h" | |
| 8 #include "base/strings/string_number_conversions.h" | |
| 9 #include "content/common/service_worker/service_worker_types.h" | |
| 10 #include "net/base/io_buffer.h" | |
| 11 #include "net/base/net_errors.h" | |
| 12 #include "net/disk_cache/disk_cache.h" | |
| 13 | |
| 14 namespace content { | |
| 15 | |
| 16 // A task to move a cached resource from the src DiskCache to the dest | |
| 17 // DiskCache. This is owned by ServiceWorkerDiskCacheMigrator. | |
| 18 class ServiceWorkerDiskCacheMigrator::Task { | |
| 19 public: | |
| 20 Task(InflightTaskMap::KeyType task_id, | |
| 21 int64 resource_id, | |
| 22 int32 data_size, | |
| 23 ServiceWorkerDiskCache* src, | |
| 24 ServiceWorkerDiskCache* dest, | |
| 25 const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner); | |
| 26 ~Task(); | |
| 27 | |
| 28 void Run(); | |
| 29 | |
| 30 InflightTaskMap::KeyType task_id() const { return task_id_; } | |
| 31 | |
| 32 private: | |
| 33 void ReadResponseInfo(); | |
| 34 void OnReadResponseInfo( | |
| 35 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer, | |
| 36 int result); | |
| 37 void OnWriteResponseInfo( | |
| 38 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer, | |
| 39 int result); | |
| 40 void WriteResponseMetadata( | |
| 41 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer); | |
| 42 void OnWriteResponseMetadata( | |
| 43 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer, | |
| 44 int result); | |
| 45 void ReadResponseData(); | |
| 46 void OnReadResponseData(const scoped_refptr<net::IOBuffer>& buffer, | |
| 47 int result); | |
| 48 void OnWriteResponseData(int result); | |
| 49 void Finish(ServiceWorkerStatusCode status); | |
| 50 | |
| 51 InflightTaskMap::KeyType task_id_; | |
| 52 int64 resource_id_; | |
| 53 int32 data_size_; | |
| 54 base::WeakPtr<ServiceWorkerDiskCacheMigrator> owner_; | |
| 55 | |
| 56 scoped_ptr<ServiceWorkerResponseReader> reader_; | |
| 57 scoped_ptr<ServiceWorkerResponseWriter> writer_; | |
| 58 scoped_ptr<ServiceWorkerResponseMetadataWriter> metadata_writer_; | |
| 59 | |
| 60 base::WeakPtrFactory<Task> weak_factory_; | |
| 61 | |
| 62 DISALLOW_COPY_AND_ASSIGN(Task); | |
| 63 }; | |
| 64 | |
| 65 // A wrapper class for disk_cache::Entry. This is used for holding an open entry | |
| 66 // and ensuring that the entry gets closed on the dtor. | |
| 67 class ServiceWorkerDiskCacheMigrator::WrappedEntry { | |
| 68 public: | |
| 69 WrappedEntry() {} | |
| 70 | |
| 71 ~WrappedEntry() { | |
| 72 if (entry_) | |
| 73 entry_->Close(); | |
| 74 } | |
| 75 | |
| 76 disk_cache::Entry* Unwrap() { | |
| 77 disk_cache::Entry* entry = entry_; | |
| 78 entry_ = nullptr; | |
| 79 return entry; | |
| 80 } | |
| 81 | |
| 82 disk_cache::Entry** GetPtr() { return &entry_; } | |
| 83 | |
| 84 private: | |
| 85 disk_cache::Entry* entry_ = nullptr; | |
| 86 | |
| 87 DISALLOW_COPY_AND_ASSIGN(WrappedEntry); | |
| 88 }; | |
| 89 | |
| 90 ServiceWorkerDiskCacheMigrator::Task::Task( | |
| 91 InflightTaskMap::KeyType task_id, | |
| 92 int64 resource_id, | |
| 93 int32 data_size, | |
| 94 ServiceWorkerDiskCache* src, | |
| 95 ServiceWorkerDiskCache* dest, | |
| 96 const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner) | |
| 97 : task_id_(task_id), | |
| 98 resource_id_(resource_id), | |
| 99 data_size_(data_size), | |
| 100 owner_(owner), | |
| 101 weak_factory_(this) { | |
| 102 DCHECK_LT(0, data_size_); | |
| 103 reader_.reset(new ServiceWorkerResponseReader(resource_id, src)); | |
| 104 writer_.reset(new ServiceWorkerResponseWriter(resource_id, dest)); | |
| 105 metadata_writer_.reset( | |
| 106 new ServiceWorkerResponseMetadataWriter(resource_id, dest)); | |
| 107 } | |
| 108 | |
| 109 ServiceWorkerDiskCacheMigrator::Task::~Task() { | |
| 110 } | |
| 111 | |
| 112 void ServiceWorkerDiskCacheMigrator::Task::Run() { | |
| 113 ReadResponseInfo(); | |
| 114 } | |
| 115 | |
| 116 void ServiceWorkerDiskCacheMigrator::Task::ReadResponseInfo() { | |
| 117 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer( | |
| 118 new HttpResponseInfoIOBuffer); | |
| 119 reader_->ReadInfo(info_buffer.get(), | |
| 120 base::Bind(&Task::OnReadResponseInfo, | |
| 121 weak_factory_.GetWeakPtr(), info_buffer)); | |
| 122 } | |
| 123 | |
| 124 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseInfo( | |
| 125 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer, | |
| 126 int result) { | |
| 127 if (result < 0) { | |
| 128 LOG(ERROR) << "Failed to read the response info"; | |
| 129 Finish(SERVICE_WORKER_ERROR_FAILED); | |
| 130 return; | |
| 131 } | |
| 132 writer_->WriteInfo(info_buffer.get(), | |
| 133 base::Bind(&Task::OnWriteResponseInfo, | |
| 134 weak_factory_.GetWeakPtr(), info_buffer)); | |
| 135 } | |
| 136 | |
| 137 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseInfo( | |
| 138 const scoped_refptr<HttpResponseInfoIOBuffer>& buffer, | |
| 139 int result) { | |
| 140 if (result < 0) { | |
| 141 LOG(ERROR) << "Failed to write the response info"; | |
| 142 Finish(SERVICE_WORKER_ERROR_FAILED); | |
| 143 return; | |
| 144 } | |
| 145 | |
| 146 const net::HttpResponseInfo* http_info = buffer->http_info.get(); | |
| 147 if (http_info->metadata && http_info->metadata->size()) { | |
| 148 WriteResponseMetadata(buffer); | |
| 149 return; | |
| 150 } | |
| 151 ReadResponseData(); | |
| 152 } | |
| 153 | |
| 154 void ServiceWorkerDiskCacheMigrator::Task::WriteResponseMetadata( | |
| 155 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer) { | |
| 156 const net::HttpResponseInfo* http_info = info_buffer->http_info.get(); | |
| 157 DCHECK(http_info->metadata); | |
| 158 DCHECK(http_info->metadata->size()); | |
| 159 | |
| 160 // |wrapped_buffer| does not own the given metadata buffer, so a callback | |
| 161 // for WriteMetadata keeps |info_buffer| which is the real owner of the | |
| 162 // metadata buffer. | |
| 163 scoped_refptr<net::WrappedIOBuffer> wrapped_buffer = | |
| 164 new net::WrappedIOBuffer(http_info->metadata->data()); | |
| 165 metadata_writer_->WriteMetadata( | |
| 166 wrapped_buffer.get(), http_info->metadata->size(), | |
| 167 base::Bind(&Task::OnWriteResponseMetadata, weak_factory_.GetWeakPtr(), | |
| 168 info_buffer)); | |
| 169 } | |
| 170 | |
| 171 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseMetadata( | |
| 172 const scoped_refptr<HttpResponseInfoIOBuffer>& protect, | |
| 173 int result) { | |
| 174 if (result < 0) { | |
| 175 LOG(ERROR) << "Failed to write the response metadata"; | |
| 176 Finish(SERVICE_WORKER_ERROR_FAILED); | |
| 177 return; | |
| 178 } | |
| 179 ReadResponseData(); | |
| 180 } | |
| 181 | |
| 182 void ServiceWorkerDiskCacheMigrator::Task::ReadResponseData() { | |
| 183 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size_); | |
| 184 reader_->ReadData(buffer.get(), data_size_, | |
| 185 base::Bind(&Task::OnReadResponseData, | |
| 186 weak_factory_.GetWeakPtr(), buffer)); | |
| 187 } | |
| 188 | |
| 189 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseData( | |
| 190 const scoped_refptr<net::IOBuffer>& buffer, | |
| 191 int result) { | |
| 192 if (result < 0) { | |
| 193 LOG(ERROR) << "Failed to read the response data"; | |
| 194 Finish(SERVICE_WORKER_ERROR_FAILED); | |
| 195 return; | |
| 196 } | |
| 197 writer_->WriteData( | |
| 198 buffer.get(), result, | |
| 199 base::Bind(&Task::OnWriteResponseData, weak_factory_.GetWeakPtr())); | |
| 200 } | |
| 201 | |
| 202 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseData(int result) { | |
| 203 if (result < 0) { | |
| 204 LOG(ERROR) << "Failed to write the response data"; | |
| 205 Finish(SERVICE_WORKER_ERROR_FAILED); | |
| 206 return; | |
| 207 } | |
| 208 Finish(SERVICE_WORKER_OK); | |
| 209 } | |
| 210 | |
| 211 void ServiceWorkerDiskCacheMigrator::Task::Finish( | |
| 212 ServiceWorkerStatusCode status) { | |
| 213 DCHECK(owner_); | |
| 214 owner_->OnEntryMigrated(task_id_, status); | |
| 215 } | |
| 216 | |
| 217 ServiceWorkerDiskCacheMigrator::ServiceWorkerDiskCacheMigrator( | |
| 218 ServiceWorkerDiskCache* src, | |
| 219 ServiceWorkerDiskCache* dest) | |
| 220 : src_(src), dest_(dest), weak_factory_(this) { | |
| 221 DCHECK(!src_->is_disabled()); | |
| 222 DCHECK(!dest_->is_disabled()); | |
| 223 } | |
| 224 | |
| 225 ServiceWorkerDiskCacheMigrator::~ServiceWorkerDiskCacheMigrator() { | |
| 226 } | |
| 227 | |
| 228 void ServiceWorkerDiskCacheMigrator::Start(const StatusCallback& callback) { | |
| 229 callback_ = callback; | |
| 230 iterator_ = src_->disk_cache()->CreateIterator(); | |
| 231 OpenNextEntry(); | |
| 232 } | |
| 233 | |
| 234 void ServiceWorkerDiskCacheMigrator::OpenNextEntry() { | |
| 235 DCHECK(!pending_task_); | |
| 236 DCHECK(!is_iterating_); | |
| 237 is_iterating_ = true; | |
| 238 | |
| 239 scoped_ptr<WrappedEntry> wrapped_entry(new WrappedEntry); | |
| 240 disk_cache::Entry** entry_ptr = wrapped_entry->GetPtr(); | |
| 241 | |
| 242 net::CompletionCallback callback = base::Bind( | |
| 243 &ServiceWorkerDiskCacheMigrator::OnNextEntryOpened, | |
| 244 weak_factory_.GetWeakPtr(), base::Passed(wrapped_entry.Pass())); | |
| 245 int result = iterator_->OpenNextEntry(entry_ptr, callback); | |
| 246 if (result == net::ERR_IO_PENDING) | |
| 247 return; | |
| 248 callback.Run(result); | |
| 249 } | |
| 250 | |
| 251 void ServiceWorkerDiskCacheMigrator::OnNextEntryOpened( | |
| 252 scoped_ptr<WrappedEntry> wrapped_entry, | |
| 253 int result) { | |
| 254 DCHECK(!pending_task_); | |
| 255 is_iterating_ = false; | |
| 256 | |
| 257 if (result == net::ERR_FAILED) { | |
| 258 // ERR_FAILED means the iterator reaches the end of the enumeration. | |
|
falken
2015/06/03 06:45:33
nit: reached
nhiroki
2015/06/03 08:51:22
Done.
| |
| 259 if (inflight_tasks_.IsEmpty()) | |
| 260 Complete(SERVICE_WORKER_OK); | |
| 261 return; | |
| 262 } | |
| 263 | |
| 264 if (result != net::OK) { | |
| 265 LOG(ERROR) << "Failed to open the next entry"; | |
| 266 inflight_tasks_.Clear(); | |
| 267 Complete(SERVICE_WORKER_ERROR_FAILED); | |
| 268 return; | |
| 269 } | |
| 270 | |
| 271 disk_cache::ScopedEntryPtr scoped_entry(wrapped_entry->Unwrap()); | |
| 272 DCHECK(scoped_entry); | |
| 273 | |
| 274 int64 resource_id = kInvalidServiceWorkerResourceId; | |
| 275 if (!base::StringToInt64(scoped_entry->GetKey(), &resource_id)) { | |
| 276 LOG(ERROR) << "Failed to read the resource id"; | |
| 277 inflight_tasks_.Clear(); | |
| 278 Complete(SERVICE_WORKER_ERROR_FAILED); | |
| 279 return; | |
| 280 } | |
| 281 | |
| 282 InflightTaskMap::KeyType task_id = next_task_id_++; | |
| 283 pending_task_.reset(new Task(task_id, resource_id, | |
| 284 scoped_entry->GetDataSize(0), src_, dest_, | |
|
falken
2015/06/03 06:45:33
What's the 0 from?
nhiroki
2015/06/03 08:51:22
This is the data type and '0' means "ResponseInfo"
| |
| 285 weak_factory_.GetWeakPtr())); | |
| 286 if (inflight_tasks_.size() < max_number_of_inflight_tasks_) { | |
| 287 RunPendingTask(); | |
| 288 OpenNextEntry(); | |
| 289 return; | |
| 290 } | |
| 291 // |pending_task_| will run when an inflight task is completed. | |
| 292 } | |
| 293 | |
| 294 void ServiceWorkerDiskCacheMigrator::RunPendingTask() { | |
| 295 DCHECK(pending_task_); | |
| 296 DCHECK_GT(max_number_of_inflight_tasks_, inflight_tasks_.size()); | |
| 297 InflightTaskMap::KeyType task_id = pending_task_->task_id(); | |
| 298 pending_task_->Run(); | |
| 299 inflight_tasks_.AddWithID(pending_task_.release(), task_id); | |
| 300 } | |
| 301 | |
| 302 void ServiceWorkerDiskCacheMigrator::OnEntryMigrated( | |
| 303 InflightTaskMap::KeyType task_id, | |
| 304 ServiceWorkerStatusCode status) { | |
| 305 DCHECK(inflight_tasks_.Lookup(task_id)); | |
| 306 inflight_tasks_.Remove(task_id); | |
| 307 | |
| 308 if (status != SERVICE_WORKER_OK) { | |
| 309 inflight_tasks_.Clear(); | |
| 310 Complete(status); | |
| 311 return; | |
| 312 } | |
| 313 | |
| 314 if (pending_task_) { | |
| 315 RunPendingTask(); | |
| 316 OpenNextEntry(); | |
| 317 return; | |
| 318 } | |
| 319 | |
| 320 if (is_iterating_) | |
| 321 return; | |
| 322 | |
| 323 if (inflight_tasks_.IsEmpty()) { | |
| 324 Complete(SERVICE_WORKER_OK); | |
| 325 return; | |
|
falken
2015/06/03 06:45:33
nit: dont' need this return
nhiroki
2015/06/03 08:51:22
Done.
| |
| 326 } | |
| 327 } | |
| 328 | |
| 329 void ServiceWorkerDiskCacheMigrator::Complete(ServiceWorkerStatusCode status) { | |
| 330 DCHECK(inflight_tasks_.IsEmpty()); | |
| 331 // TODO(nhiroki): Add UMA for the result of migration. | |
| 332 callback_.Run(status); | |
| 333 } | |
| 334 | |
| 335 } // namespace content | |
| OLD | NEW |