// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/child/fileapi/webfilewriter_base.h"
#include "base/logging.h"
#include "storage/common/fileapi/file_system_util.h"
#include "third_party/WebKit/public/platform/WebFileError.h"
#include "third_party/WebKit/public/platform/WebFileWriterClient.h"
#include "third_party/WebKit/public/platform/WebURL.h"
using storage::FileErrorToWebFileError;
namespace content {
WebFileWriterBase::WebFileWriterBase(const GURL& path,
blink::WebFileWriterClient* client)
: path_(path),
client_(client),
operation_(kOperationNone),
cancel_state_(kCancelNotInProgress) {}
WebFileWriterBase::~WebFileWriterBase() {}
void WebFileWriterBase::truncate(long long length) {
DCHECK(kOperationNone == operation_);
DCHECK(kCancelNotInProgress == cancel_state_);
operation_ = kOperationTruncate;
DoTruncate(path_, length);
}
void WebFileWriterBase::write(
long long position,
const blink::WebString& id) {
DCHECK_EQ(kOperationNone, operation_);
DCHECK_EQ(kCancelNotInProgress, cancel_state_);
operation_ = kOperationWrite;
DoWrite(path_, id.utf8(), position);
}
// When we cancel a write/truncate, we always get back the result of the write
// before the result of the cancel, no matter what happens.
// So we'll get back either
// success [of the write/truncate, in a DidWrite(XXX, true)/DidSucceed() call]
// followed by failure [of the cancel]; or
// failure [of the write, either from cancel or other reasons] followed by
// the result of the cancel.
// In the write case, there could also be queued up non-terminal DidWrite calls
// before any of that comes back, but there will always be a terminal write
// response [success or failure] after them, followed by the cancel result, so
// we can ignore non-terminal write responses, take the terminal write success
// or the first failure as the last write response, then know that the next
// thing to come back is the cancel response. We only notify the
// AsyncFileWriterClient when it's all over.
void WebFileWriterBase::cancel() {
// Check for the cancel passing the previous operation's return in-flight.
if (kOperationWrite != operation_ && kOperationTruncate != operation_)
return;
if (kCancelNotInProgress != cancel_state_)
return;
cancel_state_ = kCancelSent;
DoCancel();
}
void WebFileWriterBase::DidFinish(base::File::Error error_code) {
if (error_code == base::File::FILE_OK)
DidSucceed();
else
DidFail(error_code);
}
void WebFileWriterBase::DidWrite(int64 bytes, bool complete) {
DCHECK(kOperationWrite == operation_);
switch (cancel_state_) {
case kCancelNotInProgress:
if (complete)
operation_ = kOperationNone;
client_->didWrite(bytes, complete);
break;
case kCancelSent:
// This is the success call of the write, which we'll eat, even though
// it succeeded before the cancel got there. We accepted the cancel call,
// so the write will eventually return an error.
if (complete)
cancel_state_ = kCancelReceivedWriteResponse;
break;
case kCancelReceivedWriteResponse:
default:
NOTREACHED();
}
}
void WebFileWriterBase::DidSucceed() {
// Write never gets a DidSucceed call, so this is either a cancel or truncate
// response.
switch (cancel_state_) {
case kCancelNotInProgress:
// A truncate succeeded, with no complications.
DCHECK(kOperationTruncate == operation_);
operation_ = kOperationNone;
client_->didTruncate();
break;
case kCancelSent:
DCHECK(kOperationTruncate == operation_);
// This is the success call of the truncate, which we'll eat, even though
// it succeeded before the cancel got there. We accepted the cancel call,
// so the truncate will eventually return an error.
cancel_state_ = kCancelReceivedWriteResponse;
break;
case kCancelReceivedWriteResponse:
// This is the success of the cancel operation.
FinishCancel();
break;
default:
NOTREACHED();
}
}
void WebFileWriterBase::DidFail(base::File::Error error_code) {
DCHECK(kOperationNone != operation_);
switch (cancel_state_) {
case kCancelNotInProgress:
// A write or truncate failed.
operation_ = kOperationNone;
client_->didFail(FileErrorToWebFileError(error_code));
break;
case kCancelSent:
// This is the failure of a write or truncate; the next message should be
// the result of the cancel. We don't assume that it'll be a success, as
// the write/truncate could have failed for other reasons.
cancel_state_ = kCancelReceivedWriteResponse;
break;
case kCancelReceivedWriteResponse:
// The cancel reported failure, meaning that the write or truncate
// finished before the cancel got there. But we suppressed the
// write/truncate's response, and will now report that it was cancelled.
FinishCancel();
break;
default:
NOTREACHED();
}
}
void WebFileWriterBase::FinishCancel() {
DCHECK(kCancelReceivedWriteResponse == cancel_state_);
DCHECK(kOperationNone != operation_);
cancel_state_ = kCancelNotInProgress;
operation_ = kOperationNone;
client_->didFail(blink::WebFileErrorAbort);
}
} // namespace content