Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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 "webkit/plugins/ppapi/quota_file_io.h" | |
| 6 | |
| 7 #include "base/stl_util.h" | |
| 8 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | |
| 9 | |
| 10 using base::PlatformFile; | |
| 11 using base::PlatformFileError; | |
| 12 using quota::StorageType; | |
| 13 | |
| 14 namespace webkit { | |
| 15 namespace ppapi { | |
| 16 | |
| 17 namespace { | |
| 18 StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) { | |
| 19 switch (type) { | |
| 20 case PP_FILESYSTEMTYPE_LOCALPERSISTENT: | |
| 21 return quota::kStorageTypePersistent; | |
| 22 case PP_FILESYSTEMTYPE_LOCALTEMPORARY: | |
| 23 return quota::kStorageTypeTemporary; | |
| 24 default: | |
| 25 return quota::kStorageTypeUnknown; | |
| 26 } | |
| 27 NOTREACHED(); | |
| 28 } | |
| 29 } // namespace | |
| 30 | |
| 31 class QuotaFileIO::PendingOperationBase { | |
| 32 public: | |
| 33 virtual ~PendingOperationBase() {} | |
| 34 | |
| 35 // Either one of Run() or DidFail() is called (the latter is called when | |
| 36 // there was more than one error during quota queries). | |
| 37 virtual void Run() = 0; | |
| 38 virtual void DidFail(PlatformFileError error) = 0; | |
| 39 | |
| 40 protected: | |
| 41 PendingOperationBase(QuotaFileIO* quota_io, bool is_will_operation) | |
| 42 : quota_io_(quota_io), is_will_operation_(is_will_operation) { | |
| 43 DCHECK(quota_io_); | |
| 44 quota_io_->RegisterOperation(this); | |
| 45 quota_io_->WillUpdate(); | |
| 46 } | |
| 47 | |
| 48 QuotaFileIO* quota_io_; | |
| 49 const bool is_will_operation_; | |
| 50 }; | |
| 51 | |
| 52 class QuotaFileIO::WriteOperation : public PendingOperationBase { | |
| 53 public: | |
| 54 WriteOperation(QuotaFileIO* quota_io, | |
| 55 bool is_will_operation, | |
| 56 int64_t offset, | |
| 57 const char* buffer, | |
| 58 int32_t bytes_to_write, | |
| 59 WriteCallback* callback) | |
| 60 : PendingOperationBase(quota_io, is_will_operation), | |
| 61 offset_(offset), | |
| 62 bytes_to_write_(bytes_to_write), | |
| 63 callback_(callback), | |
| 64 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
| 65 if (!is_will_operation) { | |
| 66 DCHECK(buffer); | |
| 67 buffer_.reset(new char[bytes_to_write]); | |
|
michaeln
2011/07/26 19:25:08
Do we need the copy the bytes? If the caller is re
kinuko
2011/07/27 09:33:14
Right. I wanted to keep the pointers to make my t
yzshen1
2011/07/27 18:26:07
The reason why PPB_FileIO_Impl doesn't need to mak
kinuko
2011/07/28 14:11:00
Ok, let me revert this change for now and keep a c
| |
| 68 memcpy(buffer_.get(), buffer, bytes_to_write); | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 virtual ~WriteOperation() OVERRIDE { | |
|
yzshen1
2011/07/26 19:26:44
I think it is not OVERRIDE, is it?
kinuko
2011/07/27 09:33:14
Done.
| |
| 73 if (callback_.get()) | |
| 74 callback_->Run(base::PLATFORM_FILE_ERROR_ABORT, 0); | |
| 75 } | |
| 76 virtual void Run() OVERRIDE { | |
| 77 DCHECK(quota_io_); | |
| 78 if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) { | |
|
yzshen1
2011/07/26 19:26:44
In addition to the problem that I mentioned in ano
michaeln
2011/07/26 19:58:07
Doh... right... my vote would be to make QuotaFile
kinuko
2011/07/27 09:33:14
Done. Thanks for your careful review.
Changed the
| |
| 79 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE); | |
| 80 return; | |
| 81 } | |
| 82 if (is_will_operation_) { | |
| 83 // Assuming the write will succeed. | |
| 84 DidFinish(base::PLATFORM_FILE_OK, bytes_to_write_); | |
| 85 return; | |
| 86 } | |
| 87 DCHECK(buffer_.get()); | |
| 88 if (!base::FileUtilProxy::Write( | |
| 89 quota_io_->instance_->delegate()->GetFileThreadMessageLoopProxy(), | |
| 90 quota_io_->file_, offset_, buffer_.get(), bytes_to_write_, | |
| 91 callback_factory_.NewCallback(&WriteOperation::DidFinish))) { | |
| 92 DidFail(base::PLATFORM_FILE_ERROR_FAILED); | |
| 93 return; | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 virtual void DidFail(PlatformFileError error) OVERRIDE { | |
| 98 DidFinish(error, 0); | |
| 99 } | |
| 100 | |
| 101 private: | |
| 102 void DidFinish(PlatformFileError status, int bytes_written) { | |
| 103 quota_io_->DidWrite(offset_ + bytes_written); | |
| 104 quota_io_->UnregisterOperation(this); | |
| 105 DCHECK(callback_.get()); | |
| 106 callback_->Run(status, bytes_written); | |
| 107 callback_.reset(); | |
| 108 delete this; | |
| 109 } | |
| 110 | |
| 111 const int64_t offset_; | |
| 112 scoped_array<char> buffer_; | |
| 113 const int32_t bytes_to_write_; | |
| 114 scoped_ptr<WriteCallback> callback_; | |
| 115 base::ScopedCallbackFactory<QuotaFileIO::WriteOperation> callback_factory_; | |
| 116 }; | |
| 117 | |
| 118 class QuotaFileIO::SetLengthOperation : public PendingOperationBase { | |
| 119 public: | |
| 120 SetLengthOperation(QuotaFileIO* quota_io, | |
| 121 bool is_will_operation, | |
| 122 int64_t length, | |
| 123 StatusCallback* callback) | |
| 124 : PendingOperationBase(quota_io, is_will_operation), | |
| 125 length_(length), | |
| 126 callback_(callback), | |
| 127 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {} | |
| 128 | |
| 129 virtual ~SetLengthOperation() OVERRIDE { | |
|
yzshen1
2011/07/26 19:26:44
I think it is not OVERRIDE, is it?
kinuko
2011/07/27 09:33:14
Done.
| |
| 130 if (callback_.get()) | |
| 131 callback_->Run(base::PLATFORM_FILE_ERROR_ABORT); | |
| 132 } | |
| 133 | |
| 134 virtual void Run() OVERRIDE { | |
| 135 DCHECK(quota_io_); | |
| 136 if (quota_io_->CheckIfExceedsQuota(length_)) { | |
| 137 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE); | |
| 138 return; | |
| 139 } | |
| 140 quota_io_->WillUpdate(); | |
| 141 if (is_will_operation_) { | |
| 142 DidFinish(base::PLATFORM_FILE_OK); | |
| 143 return; | |
| 144 } | |
| 145 if (!base::FileUtilProxy::Truncate( | |
| 146 quota_io_->instance_->delegate()->GetFileThreadMessageLoopProxy(), | |
| 147 quota_io_->file_, length_, | |
| 148 callback_factory_.NewCallback(&SetLengthOperation::DidFinish))) { | |
| 149 DidFail(base::PLATFORM_FILE_ERROR_FAILED); | |
| 150 return; | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 virtual void DidFail(PlatformFileError error) OVERRIDE { | |
| 155 DidFinish(error); | |
| 156 } | |
| 157 | |
| 158 private: | |
| 159 void DidFinish(PlatformFileError status) { | |
| 160 quota_io_->DidSetLength(status != base::PLATFORM_FILE_OK ? -1 : length_); | |
| 161 quota_io_->UnregisterOperation(this); | |
| 162 DCHECK(callback_.get()); | |
| 163 callback_->Run(status); | |
| 164 callback_.reset(); | |
| 165 delete this; | |
| 166 } | |
| 167 | |
| 168 int64_t length_; | |
| 169 scoped_ptr<StatusCallback> callback_; | |
| 170 base::ScopedCallbackFactory<QuotaFileIO::SetLengthOperation> | |
| 171 callback_factory_; | |
| 172 }; | |
| 173 | |
| 174 // QuotaFileIO -------------------------------------------------------------- | |
| 175 | |
| 176 QuotaFileIO::QuotaFileIO( | |
| 177 PluginInstance* instance, | |
| 178 PlatformFile file, | |
| 179 const GURL& file_url, | |
| 180 PP_FileSystemType type) | |
| 181 : instance_(instance), | |
| 182 file_(file), | |
| 183 file_url_(file_url), | |
| 184 storage_type_(PPFileSystemTypeToQuotaStorageType(type)), | |
| 185 cached_file_size_(0), | |
| 186 cached_available_space_(0), | |
| 187 outstanding_quota_queries_(0), | |
| 188 outstanding_errors_(0), | |
| 189 did_notify_will_update_(false), | |
| 190 max_written_offset_(0), | |
| 191 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
| 192 DCHECK(instance_); | |
| 193 DCHECK_NE(base::kInvalidPlatformFileValue, file_); | |
| 194 DCHECK_NE(quota::kStorageTypeUnknown, storage_type_); | |
| 195 } | |
| 196 | |
| 197 QuotaFileIO::~QuotaFileIO() { | |
| 198 STLDeleteContainerPointers(registered_operations_.begin(), | |
| 199 registered_operations_.end()); | |
| 200 } | |
| 201 | |
| 202 bool QuotaFileIO::Write( | |
| 203 int64_t offset, const char* buffer, int32_t bytes_to_write, | |
| 204 WriteCallback* callback) { | |
| 205 WriteOperation* op = new WriteOperation( | |
| 206 this, false, offset, buffer, bytes_to_write, callback); | |
| 207 return RegisterOperationForQuotaChecks(op); | |
| 208 } | |
| 209 | |
| 210 bool QuotaFileIO::SetLength(int64_t length, StatusCallback* callback) { | |
| 211 DCHECK(pending_operations_.empty()); | |
| 212 SetLengthOperation* op = new SetLengthOperation( | |
| 213 this, false, length, callback); | |
| 214 return RegisterOperationForQuotaChecks(op); | |
| 215 } | |
| 216 | |
| 217 bool QuotaFileIO::WillWrite( | |
| 218 int64_t offset, int32_t bytes_to_write, WriteCallback* callback) { | |
| 219 WriteOperation* op = new WriteOperation( | |
| 220 this, true, offset, NULL, bytes_to_write, callback); | |
| 221 return RegisterOperationForQuotaChecks(op); | |
| 222 } | |
| 223 | |
| 224 bool QuotaFileIO::WillSetLength(int64_t length, StatusCallback* callback) { | |
| 225 DCHECK(pending_operations_.empty()); | |
| 226 SetLengthOperation* op = new SetLengthOperation(this, true, length, callback); | |
| 227 return RegisterOperationForQuotaChecks(op); | |
| 228 } | |
| 229 | |
| 230 bool QuotaFileIO::RegisterOperationForQuotaChecks( | |
| 231 PendingOperationBase* op_ptr) { | |
| 232 scoped_ptr<PendingOperationBase> op(op_ptr); | |
| 233 if (pending_operations_.empty()) { | |
| 234 // This is the first pending quota check. Run querying the file size | |
| 235 // and available space. | |
| 236 outstanding_quota_queries_ = 0; | |
| 237 outstanding_errors_ = 0; | |
| 238 | |
| 239 // Query the file size. | |
| 240 ++outstanding_quota_queries_; | |
| 241 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile( | |
| 242 instance_->delegate()->GetFileThreadMessageLoopProxy(), file_, | |
| 243 callback_factory_.NewCallback(&QuotaFileIO::DidQueryInfoForQuota))) | |
| 244 return false; | |
|
brettw
2011/07/26 16:40:14
Can you add a comment here that the op_ptr destruc
kinuko
2011/07/27 09:33:14
Changed to explicitly call DidFail.
| |
| 245 | |
| 246 // Query the current available space. | |
| 247 ++outstanding_quota_queries_; | |
| 248 instance_->delegate()->QueryAvailableSpace( | |
| 249 GURL(file_url_.path()).GetOrigin(), storage_type_, | |
| 250 callback_factory_.NewCallback(&QuotaFileIO::DidQueryAvailableSpace)); | |
| 251 } | |
| 252 pending_operations_.push(op.release()); | |
| 253 return true; | |
| 254 } | |
| 255 | |
| 256 void QuotaFileIO::DidQueryInfoForQuota( | |
| 257 base::PlatformFileError error_code, | |
| 258 const base::PlatformFileInfo& file_info) { | |
| 259 if (error_code != base::PLATFORM_FILE_OK) | |
| 260 ++outstanding_errors_; | |
| 261 cached_file_size_ = file_info.size; | |
| 262 DCHECK_GT(outstanding_quota_queries_, 0); | |
| 263 if (--outstanding_quota_queries_ == 0) | |
| 264 DidQueryForQuotaCheck(); | |
| 265 } | |
| 266 | |
| 267 void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) { | |
| 268 cached_available_space_ = avail_space; | |
| 269 DCHECK_GT(outstanding_quota_queries_, 0); | |
| 270 if (--outstanding_quota_queries_ == 0) | |
| 271 DidQueryForQuotaCheck(); | |
| 272 } | |
| 273 | |
| 274 void QuotaFileIO::DidQueryForQuotaCheck() { | |
| 275 DCHECK(!pending_operations_.empty()); | |
| 276 while (!pending_operations_.empty()) { | |
| 277 PendingOperationBase* op = pending_operations_.front(); | |
| 278 pending_operations_.pop(); | |
| 279 if (outstanding_errors_ > 0) { | |
| 280 op->DidFail(base::PLATFORM_FILE_ERROR_FAILED); | |
| 281 continue; | |
| 282 } | |
| 283 op->Run(); | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const { | |
| 288 DCHECK_GE(cached_file_size_, 0); | |
| 289 DCHECK_GE(cached_available_space_, 0); | |
| 290 return new_file_size - cached_file_size_ > cached_available_space_; | |
| 291 } | |
| 292 | |
| 293 void QuotaFileIO::WillUpdate() { | |
| 294 if (!did_notify_will_update_) { | |
| 295 instance_->delegate()->WillUpdateFile(file_url_); | |
| 296 did_notify_will_update_ = true; | |
| 297 DCHECK_EQ(0, max_written_offset_); | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 void QuotaFileIO::DidWrite(int64_t written_offset_end) { | |
| 302 max_written_offset_ = std::max(max_written_offset_, written_offset_end); | |
| 303 DCHECK(did_notify_will_update_); | |
| 304 // If we have no more pending writes, notify the browser that we did | |
| 305 // update the file. | |
| 306 if (registered_operations_.size() == 1) { | |
| 307 int64_t growth = max_written_offset_ - cached_file_size_; | |
| 308 growth = growth < 0 ? 0 : growth; | |
| 309 instance_->delegate()->DidUpdateFile(file_url_, growth); | |
| 310 did_notify_will_update_ = false; | |
| 311 max_written_offset_ = 0; | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 void QuotaFileIO::DidSetLength(int64_t new_file_size) { | |
| 316 DCHECK(did_notify_will_update_); | |
| 317 int64_t delta = new_file_size >= 0 ? new_file_size - cached_file_size_ : 0; | |
| 318 instance_->delegate()->DidUpdateFile(file_url_, delta); | |
| 319 did_notify_will_update_ = false; | |
| 320 } | |
| 321 | |
| 322 void QuotaFileIO::RegisterOperation(PendingOperationBase* operation) { | |
| 323 registered_operations_.insert(operation); | |
| 324 } | |
| 325 | |
| 326 void QuotaFileIO::UnregisterOperation(PendingOperationBase* op) { | |
| 327 DCHECK(registered_operations_.find(op) != registered_operations_.end()); | |
| 328 registered_operations_.erase(op); | |
| 329 } | |
| 330 | |
| 331 } // namespace ppapi | |
| 332 } // namespace webkit | |
| OLD | NEW |