Refactor management of overview window copy lifetime into a separate class.
[chromium-blink-merge.git] / content / renderer / pepper / quota_file_io.cc
blobaf87432fa9f72a54215a2da16dc240951c3ea053
1 // Copyright (c) 2012 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.
5 #include "content/renderer/pepper/quota_file_io.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/stl_util.h"
14 #include "base/task_runner_util.h"
16 using base::PlatformFile;
17 using base::PlatformFileError;
18 using quota::StorageType;
20 namespace content {
22 namespace {
23 StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) {
24 switch (type) {
25 case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
26 return quota::kStorageTypePersistent;
27 case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
28 return quota::kStorageTypeTemporary;
29 default:
30 return quota::kStorageTypeUnknown;
32 NOTREACHED();
33 return quota::kStorageTypeUnknown;
36 int WriteAdapter(PlatformFile file, int64 offset,
37 scoped_ptr<char[]> data, int size) {
38 return base::WritePlatformFile(file, offset, data.get(), size);
41 } // namespace
43 class QuotaFileIO::PendingOperationBase {
44 public:
45 virtual ~PendingOperationBase() {}
47 // Either one of Run() or DidFail() is called (the latter is called when
48 // there was more than one error during quota queries).
49 virtual void Run() = 0;
50 virtual void DidFail(PlatformFileError error) = 0;
52 protected:
53 explicit PendingOperationBase(QuotaFileIO* quota_io) : quota_io_(quota_io) {
54 DCHECK(quota_io_);
55 quota_io_->WillUpdate();
58 QuotaFileIO* quota_io_;
61 class QuotaFileIO::WriteOperation : public PendingOperationBase {
62 public:
63 WriteOperation(QuotaFileIO* quota_io,
64 int64_t offset,
65 const char* buffer,
66 int32_t bytes_to_write,
67 const WriteCallback& callback)
68 : PendingOperationBase(quota_io),
69 offset_(offset),
70 bytes_to_write_(bytes_to_write),
71 callback_(callback),
72 finished_(false),
73 status_(base::PLATFORM_FILE_OK),
74 bytes_written_(0),
75 weak_factory_(this) {
76 // TODO(kinuko): Check the API convention if we really need to keep a copy
77 // of the buffer during the async write operations.
78 buffer_.reset(new char[bytes_to_write]);
79 memcpy(buffer_.get(), buffer, bytes_to_write);
81 virtual ~WriteOperation() {}
82 virtual void Run() OVERRIDE {
83 DCHECK(quota_io_);
84 if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) {
85 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
86 return;
88 DCHECK(buffer_.get());
90 if (!base::PostTaskAndReplyWithResult(
91 quota_io_->delegate()->GetFileThreadMessageLoopProxy().get(),
92 FROM_HERE,
93 base::Bind(&WriteAdapter,
94 quota_io_->file_,
95 offset_,
96 base::Passed(&buffer_),
97 bytes_to_write_),
98 base::Bind(&WriteOperation::DidWrite,
99 weak_factory_.GetWeakPtr()))) {
100 DidFail(base::PLATFORM_FILE_ERROR_FAILED);
101 return;
105 virtual void DidFail(PlatformFileError error) OVERRIDE {
106 DidFinish(error, 0);
109 bool finished() const { return finished_; }
111 virtual void WillRunCallback() {
112 base::MessageLoopProxy::current()->PostTask(
113 FROM_HERE,
114 base::Bind(&WriteOperation::RunCallback, weak_factory_.GetWeakPtr()));
117 private:
118 void DidWrite(int bytes_written) {
119 base::PlatformFileError error = bytes_written > 0 ?
120 base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_FAILED;
121 DidFinish(error, bytes_written);
124 void DidFinish(PlatformFileError status, int bytes_written) {
125 finished_ = true;
126 status_ = status;
127 bytes_written_ = bytes_written;
128 int64_t max_offset =
129 (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written;
130 // This may delete itself by calling RunCallback.
131 quota_io_->DidWrite(this, max_offset);
134 virtual void RunCallback() {
135 DCHECK_EQ(false, callback_.is_null());
136 callback_.Run(status_, bytes_written_);
137 delete this;
140 const int64_t offset_;
141 scoped_ptr<char[]> buffer_;
142 const int32_t bytes_to_write_;
143 WriteCallback callback_;
144 bool finished_;
145 PlatformFileError status_;
146 int64_t bytes_written_;
147 base::WeakPtrFactory<WriteOperation> weak_factory_;
150 class QuotaFileIO::SetLengthOperation : public PendingOperationBase {
151 public:
152 SetLengthOperation(QuotaFileIO* quota_io,
153 int64_t length,
154 const StatusCallback& callback)
155 : PendingOperationBase(quota_io),
156 length_(length),
157 callback_(callback),
158 weak_factory_(this) {}
160 virtual ~SetLengthOperation() {}
162 virtual void Run() OVERRIDE {
163 DCHECK(quota_io_);
164 if (quota_io_->CheckIfExceedsQuota(length_)) {
165 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE);
166 return;
169 if (!base::FileUtilProxy::Truncate(
170 quota_io_->delegate()->GetFileThreadMessageLoopProxy().get(),
171 quota_io_->file_,
172 length_,
173 base::Bind(&SetLengthOperation::DidFinish,
174 weak_factory_.GetWeakPtr()))) {
175 DidFail(base::PLATFORM_FILE_ERROR_FAILED);
176 return;
180 virtual void DidFail(PlatformFileError error) OVERRIDE {
181 DidFinish(error);
184 private:
185 void DidFinish(PlatformFileError status) {
186 quota_io_->DidSetLength(status, length_);
187 DCHECK_EQ(false, callback_.is_null());
188 callback_.Run(status);
189 delete this;
192 int64_t length_;
193 StatusCallback callback_;
194 base::WeakPtrFactory<SetLengthOperation> weak_factory_;
197 // QuotaFileIO --------------------------------------------------------------
199 QuotaFileIO::QuotaFileIO(
200 Delegate* delegate,
201 PlatformFile file,
202 const GURL& file_url,
203 PP_FileSystemType type)
204 : delegate_(delegate),
205 file_(file),
206 file_url_(file_url),
207 storage_type_(PPFileSystemTypeToQuotaStorageType(type)),
208 cached_file_size_(0),
209 cached_available_space_(0),
210 outstanding_quota_queries_(0),
211 outstanding_errors_(0),
212 max_written_offset_(0),
213 inflight_operations_(0),
214 weak_factory_(this) {
215 DCHECK_NE(base::kInvalidPlatformFileValue, file_);
216 DCHECK_NE(quota::kStorageTypeUnknown, storage_type_);
219 QuotaFileIO::~QuotaFileIO() {
220 // Note that this doesn't dispatch pending callbacks.
221 STLDeleteContainerPointers(pending_operations_.begin(),
222 pending_operations_.end());
223 STLDeleteContainerPointers(pending_callbacks_.begin(),
224 pending_callbacks_.end());
227 bool QuotaFileIO::Write(
228 int64_t offset, const char* buffer, int32_t bytes_to_write,
229 const WriteCallback& callback) {
230 if (bytes_to_write <= 0)
231 return false;
233 WriteOperation* op = new WriteOperation(
234 this, offset, buffer, bytes_to_write, callback);
235 return RegisterOperationForQuotaChecks(op);
238 bool QuotaFileIO::SetLength(int64_t length, const StatusCallback& callback) {
239 DCHECK(pending_operations_.empty());
240 SetLengthOperation* op = new SetLengthOperation(this, length, callback);
241 return RegisterOperationForQuotaChecks(op);
244 bool QuotaFileIO::RegisterOperationForQuotaChecks(
245 PendingOperationBase* op_ptr) {
246 scoped_ptr<PendingOperationBase> op(op_ptr);
247 if (pending_operations_.empty()) {
248 // This is the first pending quota check. Run querying the file size
249 // and available space.
250 outstanding_quota_queries_ = 0;
251 outstanding_errors_ = 0;
253 // Query the file size.
254 ++outstanding_quota_queries_;
255 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
256 delegate_->GetFileThreadMessageLoopProxy().get(),
257 file_,
258 base::Bind(&QuotaFileIO::DidQueryInfoForQuota,
259 weak_factory_.GetWeakPtr()))) {
260 // This makes the call fail synchronously; we do not fire the callback
261 // here but just delete the operation and return false.
262 return false;
265 // Query the current available space.
266 ++outstanding_quota_queries_;
267 delegate_->QueryAvailableSpace(
268 file_url_.GetOrigin(), storage_type_,
269 base::Bind(&QuotaFileIO::DidQueryAvailableSpace,
270 weak_factory_.GetWeakPtr()));
272 pending_operations_.push_back(op.release());
273 return true;
276 void QuotaFileIO::DidQueryInfoForQuota(
277 base::PlatformFileError error_code,
278 const base::PlatformFileInfo& file_info) {
279 if (error_code != base::PLATFORM_FILE_OK)
280 ++outstanding_errors_;
281 cached_file_size_ = file_info.size;
282 DCHECK_GT(outstanding_quota_queries_, 0);
283 if (--outstanding_quota_queries_ == 0)
284 DidQueryForQuotaCheck();
287 void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) {
288 cached_available_space_ = avail_space;
289 DCHECK_GT(outstanding_quota_queries_, 0);
290 if (--outstanding_quota_queries_ == 0)
291 DidQueryForQuotaCheck();
294 void QuotaFileIO::DidQueryForQuotaCheck() {
295 DCHECK(!pending_operations_.empty());
296 DCHECK_GT(inflight_operations_, 0);
297 while (!pending_operations_.empty()) {
298 PendingOperationBase* op = pending_operations_.front();
299 pending_operations_.pop_front();
300 pending_callbacks_.push_back(op);
301 if (outstanding_errors_ > 0) {
302 op->DidFail(base::PLATFORM_FILE_ERROR_FAILED);
303 continue;
305 op->Run();
309 bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const {
310 DCHECK_GE(cached_file_size_, 0);
311 DCHECK_GE(cached_available_space_, 0);
312 return new_file_size - cached_file_size_ > cached_available_space_;
315 void QuotaFileIO::WillUpdate() {
316 if (inflight_operations_++ == 0) {
317 delegate_->WillUpdateFile(file_url_);
318 DCHECK_EQ(0, max_written_offset_);
322 void QuotaFileIO::DidWrite(WriteOperation* op,
323 int64_t written_offset_end) {
324 max_written_offset_ = std::max(max_written_offset_, written_offset_end);
325 DCHECK_GT(inflight_operations_, 0);
326 DCHECK(!pending_callbacks_.empty());
327 // Fire callbacks for finished operations.
328 while (!pending_callbacks_.empty()) {
329 WriteOperation* op = static_cast<WriteOperation*>(
330 pending_callbacks_.front());
331 if (!op->finished())
332 break;
333 pending_callbacks_.pop_front();
334 op->WillRunCallback();
336 // If we have no more pending writes, notify the browser that we did
337 // update the file.
338 if (--inflight_operations_ == 0) {
339 DCHECK(pending_operations_.empty());
340 int64_t growth = max_written_offset_ - cached_file_size_;
341 growth = growth < 0 ? 0 : growth;
343 delegate_->DidUpdateFile(file_url_, growth);
344 max_written_offset_ = 0;
348 void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) {
349 DCHECK_EQ(1, inflight_operations_);
350 pending_callbacks_.pop_front();
351 DCHECK(pending_callbacks_.empty());
352 int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 :
353 new_file_size - cached_file_size_;
355 delegate_->DidUpdateFile(file_url_, delta);
356 inflight_operations_ = 0;
359 } // namespace content