summaryrefslogtreecommitdiffstats
path: root/webkit/fileapi/webfilewriter_base.cc
blob: ca0239f57ed5c8ac1074997a48ef9ebd405ffb4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright (c) 2012 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 "webkit/fileapi/webfilewriter_base.h"

#include "base/logging.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileError.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileWriterClient.h"
#include "webkit/fileapi/file_system_util.h"

namespace fileapi {

WebFileWriterBase::WebFileWriterBase(
     const GURL& path, WebKit::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 WebKit::WebURL& blob_url) {
  DCHECK(kOperationNone == operation_);
  DCHECK(kCancelNotInProgress == cancel_state_);
  operation_ = kOperationWrite;
  DoWrite(path_, blob_url, 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::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::PlatformFileError error_code) {
  DCHECK(kOperationNone != operation_);
  switch (cancel_state_) {
    case kCancelNotInProgress:
      // A write or truncate failed.
      operation_ = kOperationNone;
      client_->didFail(
          PlatformFileErrorToWebFileError(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::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::FinishCancel() {
  DCHECK(kCancelReceivedWriteResponse == cancel_state_);
  DCHECK(kOperationNone != operation_);
  cancel_state_ = kCancelNotInProgress;
  operation_ = kOperationNone;
  client_->didFail(WebKit::WebFileErrorAbort);
}

}  // namespace fileapi