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
13 namespace fileapi {
14
15 namespace {
16
17 const int kOpenFlagsForWrite = base::PLATFORM_FILE_OPEN |
18 base::PLATFORM_FILE_WRITE |
19 base::PLATFORM_FILE_ASYNC;
20
21 } // namespace
22
CreateForLocalFile(base::TaskRunner * task_runner,const base::FilePath & file_path,int64 initial_offset)23 FileStreamWriter* 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
~LocalFileStreamWriter()30 LocalFileStreamWriter::~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
Write(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)40 int 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
Cancel(const net::CompletionCallback & callback)58 int 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
Flush(const net::CompletionCallback & callback)67 int 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
LocalFileStreamWriter(base::TaskRunner * task_runner,const base::FilePath & file_path,int64 initial_offset)82 LocalFileStreamWriter::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
InitiateOpen(const net::CompletionCallback & error_callback,const base::Closure & main_operation)91 int 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
DidOpen(const net::CompletionCallback & error_callback,const base::Closure & main_operation,int result)107 void 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
InitiateSeek(const net::CompletionCallback & error_callback,const base::Closure & main_operation)127 void 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
DidSeek(const net::CompletionCallback & error_callback,const base::Closure & main_operation,int64 result)150 void 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
ReadyToWrite(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)173 void 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
InitiateWrite(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)185 int 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
DidWrite(const net::CompletionCallback & callback,int result)197 void 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
InitiateFlush(const net::CompletionCallback & callback)207 int 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
DidFlush(const net::CompletionCallback & callback,int result)217 void 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
CancelIfRequested()227 bool 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