blob: 5e01937d035fa1a7f4e43ecf2054599c3f3c41dd [file] [log] [blame]
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/modules/filesystem/file_writer_base.h"
#include <memory>
#include "third_party/blink/renderer/core/events/progress_event.h"
#include "third_party/blink/renderer/core/fileapi/blob.h"
#include "third_party/blink/renderer/core/fileapi/file_error.h"
namespace blink {
FileWriterBase::~FileWriterBase() = default;
void FileWriterBase::Initialize(const KURL& path, long long length) {
DCHECK_GE(length, 0);
length_ = length;
path_ = path;
}
FileWriterBase::FileWriterBase()
: position_(0),
operation_(kOperationNone),
cancel_state_(kCancelNotInProgress) {}
void FileWriterBase::SeekInternal(long long position) {
if (position > length_)
position = length_;
else if (position < 0)
position = length_ + position;
if (position < 0)
position = 0;
position_ = position;
}
void FileWriterBase::Truncate(long long length) {
DCHECK_EQ(kOperationNone, operation_);
DCHECK_EQ(kCancelNotInProgress, cancel_state_);
operation_ = kOperationTruncate;
DoTruncate(path_, length);
}
void FileWriterBase::Write(long long position, const String& id) {
DCHECK_EQ(kOperationNone, operation_);
DCHECK_EQ(kCancelNotInProgress, cancel_state_);
operation_ = kOperationWrite;
DoWrite(path_, id, 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 FileWriterBase::Cancel() {
// Check for the cancel passing the previous operation's return in-flight.
if (operation_ != kOperationWrite && operation_ != kOperationTruncate)
return;
if (cancel_state_ != kCancelNotInProgress)
return;
cancel_state_ = kCancelSent;
DoCancel();
}
void FileWriterBase::DidFinish(base::File::Error error_code) {
if (error_code == base::File::FILE_OK)
DidSucceed();
else
DidFail(error_code);
}
void FileWriterBase::DidWrite(int64_t bytes, bool complete) {
DCHECK_EQ(kOperationWrite, operation_);
switch (cancel_state_) {
case kCancelNotInProgress:
if (complete)
operation_ = kOperationNone;
DidWriteImpl(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 FileWriterBase::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_EQ(kOperationTruncate, operation_);
operation_ = kOperationNone;
DidTruncateImpl();
break;
case kCancelSent:
DCHECK_EQ(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 FileWriterBase::DidFail(base::File::Error error_code) {
DCHECK_NE(kOperationNone, operation_);
switch (cancel_state_) {
case kCancelNotInProgress:
// A write or truncate failed.
operation_ = kOperationNone;
DidFailImpl(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 FileWriterBase::FinishCancel() {
DCHECK_EQ(kCancelReceivedWriteResponse, cancel_state_);
DCHECK_NE(kOperationNone, operation_);
cancel_state_ = kCancelNotInProgress;
operation_ = kOperationNone;
DidFailImpl(base::File::FILE_ERROR_ABORT);
}
} // namespace blink