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 "webkit/plugins/ppapi/quota_file_io.h"
10 #include "base/location.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/message_loop_proxy.h"
13 #include "base/stl_util.h"
14 #include "base/task_runner_util.h"
15 #include "webkit/plugins/ppapi/host_globals.h"
16 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
17 #include "webkit/plugins/ppapi/resource_helper.h"
19 using base::PlatformFile
;
20 using base::PlatformFileError
;
21 using quota::StorageType
;
27 StorageType
PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type
) {
29 case PP_FILESYSTEMTYPE_LOCALPERSISTENT
:
30 return quota::kStorageTypePersistent
;
31 case PP_FILESYSTEMTYPE_LOCALTEMPORARY
:
32 return quota::kStorageTypeTemporary
;
34 return quota::kStorageTypeUnknown
;
37 return quota::kStorageTypeUnknown
;
40 int WriteAdapter(PlatformFile file
, int64 offset
,
41 scoped_ptr
<char[]> data
, int size
) {
42 return base::WritePlatformFile(file
, offset
, data
.get(), size
);
47 class QuotaFileIO::PendingOperationBase
{
49 virtual ~PendingOperationBase() {}
51 // Either one of Run() or DidFail() is called (the latter is called when
52 // there was more than one error during quota queries).
53 virtual void Run() = 0;
54 virtual void DidFail(PlatformFileError error
) = 0;
57 PendingOperationBase(QuotaFileIO
* quota_io
, bool is_will_operation
)
58 : quota_io_(quota_io
), is_will_operation_(is_will_operation
) {
60 quota_io_
->WillUpdate();
63 QuotaFileIO
* quota_io_
;
64 const bool is_will_operation_
;
67 class QuotaFileIO::WriteOperation
: public PendingOperationBase
{
69 WriteOperation(QuotaFileIO
* quota_io
,
70 bool is_will_operation
,
73 int32_t bytes_to_write
,
74 const WriteCallback
& callback
)
75 : PendingOperationBase(quota_io
, is_will_operation
),
77 bytes_to_write_(bytes_to_write
),
80 status_(base::PLATFORM_FILE_OK
),
82 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
83 if (!is_will_operation
) {
84 // TODO(kinuko): Check the API convention if we really need to keep a copy
85 // of the buffer during the async write operations.
86 buffer_
.reset(new char[bytes_to_write
]);
87 memcpy(buffer_
.get(), buffer
, bytes_to_write
);
90 virtual ~WriteOperation() {}
91 virtual void Run() OVERRIDE
{
93 if (quota_io_
->CheckIfExceedsQuota(offset_
+ bytes_to_write_
)) {
94 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE
);
97 if (is_will_operation_
) {
98 // Assuming the write will succeed.
99 DidFinish(base::PLATFORM_FILE_OK
, bytes_to_write_
);
102 DCHECK(buffer_
.get());
104 PluginDelegate
* plugin_delegate
= quota_io_
->GetPluginDelegate();
105 if (!plugin_delegate
) {
106 DidFail(base::PLATFORM_FILE_ERROR_FAILED
);
110 if (!base::PostTaskAndReplyWithResult(
111 plugin_delegate
->GetFileThreadMessageLoopProxy(), FROM_HERE
,
112 base::Bind(&WriteAdapter
,
113 quota_io_
->file_
, offset_
,
114 base::Passed(&buffer_
), bytes_to_write_
),
115 base::Bind(&WriteOperation::DidWrite
,
116 weak_factory_
.GetWeakPtr()))) {
117 DidFail(base::PLATFORM_FILE_ERROR_FAILED
);
122 virtual void DidFail(PlatformFileError error
) OVERRIDE
{
126 bool finished() const { return finished_
; }
128 virtual void WillRunCallback() {
129 base::MessageLoopProxy::current()->PostTask(
131 base::Bind(&WriteOperation::RunCallback
, weak_factory_
.GetWeakPtr()));
135 void DidWrite(int bytes_written
) {
136 base::PlatformFileError error
= bytes_written
> 0 ?
137 base::PLATFORM_FILE_OK
: base::PLATFORM_FILE_ERROR_FAILED
;
138 DidFinish(error
, bytes_written
);
141 void DidFinish(PlatformFileError status
, int bytes_written
) {
144 bytes_written_
= bytes_written
;
146 (status
!= base::PLATFORM_FILE_OK
) ? 0 : offset_
+ bytes_written
;
147 // This may delete itself by calling RunCallback.
148 quota_io_
->DidWrite(this, max_offset
);
151 virtual void RunCallback() {
152 DCHECK_EQ(false, callback_
.is_null());
153 callback_
.Run(status_
, bytes_written_
);
157 const int64_t offset_
;
158 scoped_ptr
<char[]> buffer_
;
159 const int32_t bytes_to_write_
;
160 WriteCallback callback_
;
162 PlatformFileError status_
;
163 int64_t bytes_written_
;
164 base::WeakPtrFactory
<WriteOperation
> weak_factory_
;
167 class QuotaFileIO::SetLengthOperation
: public PendingOperationBase
{
169 SetLengthOperation(QuotaFileIO
* quota_io
,
170 bool is_will_operation
,
172 const StatusCallback
& callback
)
173 : PendingOperationBase(quota_io
, is_will_operation
),
176 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {}
178 virtual ~SetLengthOperation() {}
180 virtual void Run() OVERRIDE
{
182 if (quota_io_
->CheckIfExceedsQuota(length_
)) {
183 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE
);
186 if (is_will_operation_
) {
187 DidFinish(base::PLATFORM_FILE_OK
);
191 PluginDelegate
* plugin_delegate
= quota_io_
->GetPluginDelegate();
192 if (!plugin_delegate
) {
193 DidFail(base::PLATFORM_FILE_ERROR_FAILED
);
197 if (!base::FileUtilProxy::Truncate(
198 plugin_delegate
->GetFileThreadMessageLoopProxy(),
199 quota_io_
->file_
, length_
,
200 base::Bind(&SetLengthOperation::DidFinish
,
201 weak_factory_
.GetWeakPtr()))) {
202 DidFail(base::PLATFORM_FILE_ERROR_FAILED
);
207 virtual void DidFail(PlatformFileError error
) OVERRIDE
{
212 void DidFinish(PlatformFileError status
) {
213 quota_io_
->DidSetLength(status
, length_
);
214 DCHECK_EQ(false, callback_
.is_null());
215 callback_
.Run(status
);
220 StatusCallback callback_
;
221 base::WeakPtrFactory
<SetLengthOperation
> weak_factory_
;
224 // QuotaFileIO --------------------------------------------------------------
226 QuotaFileIO::QuotaFileIO(
227 PP_Instance instance
,
229 const GURL
& file_url
,
230 PP_FileSystemType type
)
231 : pp_instance_(instance
),
234 storage_type_(PPFileSystemTypeToQuotaStorageType(type
)),
235 cached_file_size_(0),
236 cached_available_space_(0),
237 outstanding_quota_queries_(0),
238 outstanding_errors_(0),
239 max_written_offset_(0),
240 inflight_operations_(0),
241 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
242 DCHECK_NE(base::kInvalidPlatformFileValue
, file_
);
243 DCHECK_NE(quota::kStorageTypeUnknown
, storage_type_
);
246 QuotaFileIO::~QuotaFileIO() {
247 // Note that this doesn't dispatch pending callbacks.
248 STLDeleteContainerPointers(pending_operations_
.begin(),
249 pending_operations_
.end());
250 STLDeleteContainerPointers(pending_callbacks_
.begin(),
251 pending_callbacks_
.end());
254 bool QuotaFileIO::Write(
255 int64_t offset
, const char* buffer
, int32_t bytes_to_write
,
256 const WriteCallback
& callback
) {
257 if (bytes_to_write
<= 0)
260 WriteOperation
* op
= new WriteOperation(
261 this, false, offset
, buffer
, bytes_to_write
, callback
);
262 return RegisterOperationForQuotaChecks(op
);
265 bool QuotaFileIO::SetLength(int64_t length
, const StatusCallback
& callback
) {
266 DCHECK(pending_operations_
.empty());
267 SetLengthOperation
* op
= new SetLengthOperation(
268 this, false, length
, callback
);
269 return RegisterOperationForQuotaChecks(op
);
272 bool QuotaFileIO::WillWrite(
273 int64_t offset
, int32_t bytes_to_write
, const WriteCallback
& callback
) {
274 WriteOperation
* op
= new WriteOperation(
275 this, true, offset
, NULL
, bytes_to_write
, callback
);
276 return RegisterOperationForQuotaChecks(op
);
279 bool QuotaFileIO::WillSetLength(int64_t length
,
280 const StatusCallback
& callback
) {
281 DCHECK(pending_operations_
.empty());
282 SetLengthOperation
* op
= new SetLengthOperation(this, true, length
, callback
);
283 return RegisterOperationForQuotaChecks(op
);
286 PluginDelegate
* QuotaFileIO::GetPluginDelegate() const {
287 PluginInstance
* instance
= HostGlobals::Get()->GetInstance(pp_instance_
);
289 return instance
->delegate();
293 bool QuotaFileIO::RegisterOperationForQuotaChecks(
294 PendingOperationBase
* op_ptr
) {
295 scoped_ptr
<PendingOperationBase
> op(op_ptr
);
296 if (pending_operations_
.empty()) {
297 // This is the first pending quota check. Run querying the file size
298 // and available space.
299 outstanding_quota_queries_
= 0;
300 outstanding_errors_
= 0;
302 PluginDelegate
* plugin_delegate
= GetPluginDelegate();
303 if (!plugin_delegate
)
306 // Query the file size.
307 ++outstanding_quota_queries_
;
308 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile(
309 plugin_delegate
->GetFileThreadMessageLoopProxy(), file_
,
310 base::Bind(&QuotaFileIO::DidQueryInfoForQuota
,
311 weak_factory_
.GetWeakPtr()))) {
312 // This makes the call fail synchronously; we do not fire the callback
313 // here but just delete the operation and return false.
317 // Query the current available space.
318 ++outstanding_quota_queries_
;
319 plugin_delegate
->QueryAvailableSpace(
320 file_url_
.GetOrigin(), storage_type_
,
321 base::Bind(&QuotaFileIO::DidQueryAvailableSpace
,
322 weak_factory_
.GetWeakPtr()));
324 pending_operations_
.push_back(op
.release());
328 void QuotaFileIO::DidQueryInfoForQuota(
329 base::PlatformFileError error_code
,
330 const base::PlatformFileInfo
& file_info
) {
331 if (error_code
!= base::PLATFORM_FILE_OK
)
332 ++outstanding_errors_
;
333 cached_file_size_
= file_info
.size
;
334 DCHECK_GT(outstanding_quota_queries_
, 0);
335 if (--outstanding_quota_queries_
== 0)
336 DidQueryForQuotaCheck();
339 void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space
) {
340 cached_available_space_
= avail_space
;
341 DCHECK_GT(outstanding_quota_queries_
, 0);
342 if (--outstanding_quota_queries_
== 0)
343 DidQueryForQuotaCheck();
346 void QuotaFileIO::DidQueryForQuotaCheck() {
347 DCHECK(!pending_operations_
.empty());
348 DCHECK_GT(inflight_operations_
, 0);
349 while (!pending_operations_
.empty()) {
350 PendingOperationBase
* op
= pending_operations_
.front();
351 pending_operations_
.pop_front();
352 pending_callbacks_
.push_back(op
);
353 if (outstanding_errors_
> 0) {
354 op
->DidFail(base::PLATFORM_FILE_ERROR_FAILED
);
361 bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size
) const {
362 DCHECK_GE(cached_file_size_
, 0);
363 DCHECK_GE(cached_available_space_
, 0);
364 return new_file_size
- cached_file_size_
> cached_available_space_
;
367 void QuotaFileIO::WillUpdate() {
368 if (inflight_operations_
++ == 0) {
369 PluginDelegate
* plugin_delegate
= GetPluginDelegate();
371 plugin_delegate
->WillUpdateFile(file_url_
);
372 DCHECK_EQ(0, max_written_offset_
);
376 void QuotaFileIO::DidWrite(WriteOperation
* op
,
377 int64_t written_offset_end
) {
378 max_written_offset_
= std::max(max_written_offset_
, written_offset_end
);
379 DCHECK_GT(inflight_operations_
, 0);
380 DCHECK(!pending_callbacks_
.empty());
381 // Fire callbacks for finished operations.
382 while (!pending_callbacks_
.empty()) {
383 WriteOperation
* op
= static_cast<WriteOperation
*>(
384 pending_callbacks_
.front());
387 pending_callbacks_
.pop_front();
388 op
->WillRunCallback();
390 // If we have no more pending writes, notify the browser that we did
392 if (--inflight_operations_
== 0) {
393 DCHECK(pending_operations_
.empty());
394 int64_t growth
= max_written_offset_
- cached_file_size_
;
395 growth
= growth
< 0 ? 0 : growth
;
397 PluginDelegate
* plugin_delegate
= GetPluginDelegate();
399 plugin_delegate
->DidUpdateFile(file_url_
, growth
);
400 max_written_offset_
= 0;
404 void QuotaFileIO::DidSetLength(PlatformFileError error
, int64_t new_file_size
) {
405 DCHECK_EQ(1, inflight_operations_
);
406 pending_callbacks_
.pop_front();
407 DCHECK(pending_callbacks_
.empty());
408 int64_t delta
= (error
!= base::PLATFORM_FILE_OK
) ? 0 :
409 new_file_size
- cached_file_size_
;
412 PluginDelegate
* plugin_delegate
= GetPluginDelegate();
414 plugin_delegate
->DidUpdateFile(file_url_
, delta
);
415 inflight_operations_
= 0;
419 } // namespace webkit