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.
4
5#include "webkit/browser/fileapi/local_file_stream_writer.h"
6
7#include "base/callback.h"
8#include "base/message_loop/message_loop.h"
9#include "net/base/file_stream.h"
10#include "net/base/io_buffer.h"
11#include "net/base/net_errors.h"
12
13namespace fileapi {
14
15namespace {
16
17const int kOpenFlagsForWrite = base::PLATFORM_FILE_OPEN |
18                               base::PLATFORM_FILE_WRITE |
19                               base::PLATFORM_FILE_ASYNC;
20
21}  // namespace
22
23FileStreamWriter* FileStreamWriter::CreateForLocalFile(
24    base::TaskRunner* task_runner,
25    const base::FilePath& file_path,
26    int64 initial_offset) {
27  return new LocalFileStreamWriter(task_runner, file_path, initial_offset);
28}
29
30LocalFileStreamWriter::~LocalFileStreamWriter() {
31  // Invalidate weak pointers so that we won't receive any callbacks from
32  // in-flight stream operations, which might be triggered during the file close
33  // in the FileStream destructor.
34  weak_factory_.InvalidateWeakPtrs();
35
36  // FileStream's destructor closes the file safely, since we opened the file
37  // by its Open() method.
38}
39
40int LocalFileStreamWriter::Write(net::IOBuffer* buf, int buf_len,
41                                 const net::CompletionCallback& callback) {
42  DCHECK(!has_pending_operation_);
43  DCHECK(cancel_callback_.is_null());
44
45  has_pending_operation_ = true;
46  if (stream_impl_) {
47    int result = InitiateWrite(buf, buf_len, callback);
48    if (result != net::ERR_IO_PENDING)
49      has_pending_operation_ = false;
50    return result;
51  }
52  return InitiateOpen(callback,
53                      base::Bind(&LocalFileStreamWriter::ReadyToWrite,
54                                 weak_factory_.GetWeakPtr(),
55                                 make_scoped_refptr(buf), buf_len, callback));
56}
57
58int LocalFileStreamWriter::Cancel(const net::CompletionCallback& callback) {
59  if (!has_pending_operation_)
60    return net::ERR_UNEXPECTED;
61
62  DCHECK(!callback.is_null());
63  cancel_callback_ = callback;
64  return net::ERR_IO_PENDING;
65}
66
67int LocalFileStreamWriter::Flush(const net::CompletionCallback& callback) {
68  DCHECK(!has_pending_operation_);
69  DCHECK(cancel_callback_.is_null());
70
71  // Write() is not called yet, so there's nothing to flush.
72  if (!stream_impl_)
73    return net::OK;
74
75  has_pending_operation_ = true;
76  int result = InitiateFlush(callback);
77  if (result != net::ERR_IO_PENDING)
78    has_pending_operation_ = false;
79  return result;
80}
81
82LocalFileStreamWriter::LocalFileStreamWriter(base::TaskRunner* task_runner,
83                                             const base::FilePath& file_path,
84                                             int64 initial_offset)
85    : file_path_(file_path),
86      initial_offset_(initial_offset),
87      task_runner_(task_runner),
88      has_pending_operation_(false),
89      weak_factory_(this) {}
90
91int LocalFileStreamWriter::InitiateOpen(
92    const net::CompletionCallback& error_callback,
93    const base::Closure& main_operation) {
94  DCHECK(has_pending_operation_);
95  DCHECK(!stream_impl_.get());
96
97  stream_impl_.reset(new net::FileStream(NULL, task_runner_));
98
99  return stream_impl_->Open(file_path_,
100                            kOpenFlagsForWrite,
101                            base::Bind(&LocalFileStreamWriter::DidOpen,
102                                       weak_factory_.GetWeakPtr(),
103                                       error_callback,
104                                       main_operation));
105}
106
107void LocalFileStreamWriter::DidOpen(
108    const net::CompletionCallback& error_callback,
109    const base::Closure& main_operation,
110    int result) {
111  DCHECK(has_pending_operation_);
112  DCHECK(stream_impl_.get());
113
114  if (CancelIfRequested())
115    return;
116
117  if (result != net::OK) {
118    has_pending_operation_ = false;
119    stream_impl_.reset(NULL);
120    error_callback.Run(result);
121    return;
122  }
123
124  InitiateSeek(error_callback, main_operation);
125}
126
127void LocalFileStreamWriter::InitiateSeek(
128    const net::CompletionCallback& error_callback,
129    const base::Closure& main_operation) {
130  DCHECK(has_pending_operation_);
131  DCHECK(stream_impl_.get());
132
133  if (initial_offset_ == 0) {
134    // No need to seek.
135    main_operation.Run();
136    return;
137  }
138
139  int result = stream_impl_->Seek(net::FROM_BEGIN, initial_offset_,
140                                  base::Bind(&LocalFileStreamWriter::DidSeek,
141                                             weak_factory_.GetWeakPtr(),
142                                             error_callback,
143                                             main_operation));
144  if (result != net::ERR_IO_PENDING) {
145    has_pending_operation_ = false;
146    error_callback.Run(result);
147  }
148}
149
150void LocalFileStreamWriter::DidSeek(
151    const net::CompletionCallback& error_callback,
152    const base::Closure& main_operation,
153    int64 result) {
154  DCHECK(has_pending_operation_);
155
156  if (CancelIfRequested())
157    return;
158
159  if (result != initial_offset_) {
160    // TODO(kinaba) add a more specific error code.
161    result = net::ERR_FAILED;
162  }
163
164  if (result < 0) {
165    has_pending_operation_ = false;
166    error_callback.Run(static_cast<int>(result));
167    return;
168  }
169
170  main_operation.Run();
171}
172
173void LocalFileStreamWriter::ReadyToWrite(
174    net::IOBuffer* buf, int buf_len,
175    const net::CompletionCallback& callback) {
176  DCHECK(has_pending_operation_);
177
178  int result = InitiateWrite(buf, buf_len, callback);
179  if (result != net::ERR_IO_PENDING) {
180    has_pending_operation_ = false;
181    callback.Run(result);
182  }
183}
184
185int LocalFileStreamWriter::InitiateWrite(
186    net::IOBuffer* buf, int buf_len,
187    const net::CompletionCallback& callback) {
188  DCHECK(has_pending_operation_);
189  DCHECK(stream_impl_.get());
190
191  return stream_impl_->Write(buf, buf_len,
192                             base::Bind(&LocalFileStreamWriter::DidWrite,
193                                        weak_factory_.GetWeakPtr(),
194                                        callback));
195}
196
197void LocalFileStreamWriter::DidWrite(const net::CompletionCallback& callback,
198                                     int result) {
199  DCHECK(has_pending_operation_);
200
201  if (CancelIfRequested())
202    return;
203  has_pending_operation_ = false;
204  callback.Run(result);
205}
206
207int LocalFileStreamWriter::InitiateFlush(
208    const net::CompletionCallback& callback) {
209  DCHECK(has_pending_operation_);
210  DCHECK(stream_impl_.get());
211
212  return stream_impl_->Flush(base::Bind(&LocalFileStreamWriter::DidFlush,
213                                        weak_factory_.GetWeakPtr(),
214                                        callback));
215}
216
217void LocalFileStreamWriter::DidFlush(const net::CompletionCallback& callback,
218                                     int result) {
219  DCHECK(has_pending_operation_);
220
221  if (CancelIfRequested())
222    return;
223  has_pending_operation_ = false;
224  callback.Run(result);
225}
226
227bool LocalFileStreamWriter::CancelIfRequested() {
228  DCHECK(has_pending_operation_);
229
230  if (cancel_callback_.is_null())
231    return false;
232
233  net::CompletionCallback pending_cancel = cancel_callback_;
234  has_pending_operation_ = false;
235  cancel_callback_.Reset();
236  pending_cancel.Run(net::OK);
237  return true;
238}
239
240}  // namespace fileapi
241