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"
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
;
23 StorageType
PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type
) {
25 case PP_FILESYSTEMTYPE_LOCALPERSISTENT
:
26 return quota::kStorageTypePersistent
;
27 case PP_FILESYSTEMTYPE_LOCALTEMPORARY
:
28 return quota::kStorageTypeTemporary
;
30 return quota::kStorageTypeUnknown
;
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
);
43 class QuotaFileIO::PendingOperationBase
{
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;
53 explicit PendingOperationBase(QuotaFileIO
* quota_io
) : quota_io_(quota_io
) {
55 quota_io_
->WillUpdate();
58 QuotaFileIO
* quota_io_
;
61 class QuotaFileIO::WriteOperation
: public PendingOperationBase
{
63 WriteOperation(QuotaFileIO
* quota_io
,
66 int32_t bytes_to_write
,
67 const WriteCallback
& callback
)
68 : PendingOperationBase(quota_io
),
70 bytes_to_write_(bytes_to_write
),
73 status_(base::PLATFORM_FILE_OK
),
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
{
84 if (quota_io_
->CheckIfExceedsQuota(offset_
+ bytes_to_write_
)) {
85 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE
);
88 DCHECK(buffer_
.get());
90 if (!base::PostTaskAndReplyWithResult(
91 quota_io_
->delegate()->GetFileThreadMessageLoopProxy().get(),
93 base::Bind(&WriteAdapter
,
96 base::Passed(&buffer_
),
98 base::Bind(&WriteOperation::DidWrite
,
99 weak_factory_
.GetWeakPtr()))) {
100 DidFail(base::PLATFORM_FILE_ERROR_FAILED
);
105 virtual void DidFail(PlatformFileError error
) OVERRIDE
{
109 bool finished() const { return finished_
; }
111 virtual void WillRunCallback() {
112 base::MessageLoopProxy::current()->PostTask(
114 base::Bind(&WriteOperation::RunCallback
, weak_factory_
.GetWeakPtr()));
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
) {
127 bytes_written_
= bytes_written
;
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_
);
140 const int64_t offset_
;
141 scoped_ptr
<char[]> buffer_
;
142 const int32_t bytes_to_write_
;
143 WriteCallback callback_
;
145 PlatformFileError status_
;
146 int64_t bytes_written_
;
147 base::WeakPtrFactory
<WriteOperation
> weak_factory_
;
150 class QuotaFileIO::SetLengthOperation
: public PendingOperationBase
{
152 SetLengthOperation(QuotaFileIO
* quota_io
,
154 const StatusCallback
& callback
)
155 : PendingOperationBase(quota_io
),
158 weak_factory_(this) {}
160 virtual ~SetLengthOperation() {}
162 virtual void Run() OVERRIDE
{
164 if (quota_io_
->CheckIfExceedsQuota(length_
)) {
165 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE
);
169 if (!base::FileUtilProxy::Truncate(
170 quota_io_
->delegate()->GetFileThreadMessageLoopProxy().get(),
173 base::Bind(&SetLengthOperation::DidFinish
,
174 weak_factory_
.GetWeakPtr()))) {
175 DidFail(base::PLATFORM_FILE_ERROR_FAILED
);
180 virtual void DidFail(PlatformFileError error
) OVERRIDE
{
185 void DidFinish(PlatformFileError status
) {
186 quota_io_
->DidSetLength(status
, length_
);
187 DCHECK_EQ(false, callback_
.is_null());
188 callback_
.Run(status
);
193 StatusCallback callback_
;
194 base::WeakPtrFactory
<SetLengthOperation
> weak_factory_
;
197 // QuotaFileIO --------------------------------------------------------------
199 QuotaFileIO::QuotaFileIO(
202 const GURL
& file_url
,
203 PP_FileSystemType type
)
204 : delegate_(delegate
),
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)
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(),
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.
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());
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
);
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());
333 pending_callbacks_
.pop_front();
334 op
->WillRunCallback();
336 // If we have no more pending writes, notify the browser that we did
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