Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / media_galleries / fileapi / mtp_file_stream_reader.cc
blobd1fa2c101129439bcda8d3e8cfeae0dc21d37b36
1 // Copyright 2014 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.
5 #include "chrome/browser/media_galleries/fileapi/mtp_file_stream_reader.h"
7 #include <algorithm>
9 #include "base/numerics/safe_conversions.h"
10 #include "chrome/browser/media_galleries/fileapi/mtp_device_async_delegate.h"
11 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
12 #include "chrome/browser/media_galleries/fileapi/native_media_file_util.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/mime_sniffer.h"
16 #include "net/base/net_errors.h"
17 #include "storage/browser/fileapi/file_system_context.h"
19 using storage::FileStreamReader;
21 namespace {
23 // Called on the IO thread.
24 MTPDeviceAsyncDelegate* GetMTPDeviceDelegate(
25 const storage::FileSystemURL& url) {
26 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
27 return MTPDeviceMapService::GetInstance()->GetMTPDeviceAsyncDelegate(
28 url.filesystem_id());
31 void CallCompletionCallbackWithPlatformFileError(
32 const net::CompletionCallback& callback,
33 base::File::Error file_error) {
34 callback.Run(net::FileErrorToNetError(file_error));
37 void CallInt64CompletionCallbackWithPlatformFileError(
38 const net::Int64CompletionCallback& callback,
39 base::File::Error file_error) {
40 callback.Run(net::FileErrorToNetError(file_error));
43 void ReadBytes(
44 const storage::FileSystemURL& url,
45 const scoped_refptr<net::IOBuffer>& buf,
46 int64 offset,
47 int buf_len,
48 const MTPDeviceAsyncDelegate::ReadBytesSuccessCallback& success_callback,
49 const net::CompletionCallback& error_callback) {
50 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url);
51 if (!delegate) {
52 error_callback.Run(net::ERR_FAILED);
53 return;
56 delegate->ReadBytes(
57 url.path(),
58 buf,
59 offset,
60 buf_len,
61 success_callback,
62 base::Bind(&CallCompletionCallbackWithPlatformFileError, error_callback));
65 } // namespace
67 MTPFileStreamReader::MTPFileStreamReader(
68 storage::FileSystemContext* file_system_context,
69 const storage::FileSystemURL& url,
70 int64 initial_offset,
71 const base::Time& expected_modification_time,
72 bool do_media_header_validation)
73 : file_system_context_(file_system_context),
74 url_(url),
75 current_offset_(initial_offset),
76 expected_modification_time_(expected_modification_time),
77 media_header_validated_(!do_media_header_validation),
78 weak_factory_(this) {
81 MTPFileStreamReader::~MTPFileStreamReader() {
84 int MTPFileStreamReader::Read(net::IOBuffer* buf, int buf_len,
85 const net::CompletionCallback& callback) {
86 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
88 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url_);
89 if (!delegate)
90 return net::ERR_FAILED;
92 if (!media_header_validated_) {
93 scoped_refptr<net::IOBuffer> header_buf;
94 int header_buf_len = 0;
96 if (current_offset_ == 0 && buf_len >= net::kMaxBytesToSniff) {
97 // If requested read includes all the header bytes, we read directly to
98 // the original buffer, and validate the header bytes within that.
99 header_buf = buf;
100 header_buf_len = buf_len;
101 } else {
102 // Otherwise, make a special request for the header.
103 header_buf = new net::IOBuffer(net::kMaxBytesToSniff);
104 header_buf_len = net::kMaxBytesToSniff;
107 ReadBytes(url_,
108 header_buf.get(),
110 header_buf_len,
111 base::Bind(&MTPFileStreamReader::FinishValidateMediaHeader,
112 weak_factory_.GetWeakPtr(),
113 header_buf,
114 make_scoped_refptr(buf),
115 buf_len,
116 callback),
117 callback);
118 return net::ERR_IO_PENDING;
121 ReadBytes(url_, buf, current_offset_, buf_len,
122 base::Bind(&MTPFileStreamReader::FinishRead,
123 weak_factory_.GetWeakPtr(), callback),
124 callback);
126 return net::ERR_IO_PENDING;
129 int64 MTPFileStreamReader::GetLength(
130 const net::Int64CompletionCallback& callback) {
131 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
133 MTPDeviceAsyncDelegate* delegate = GetMTPDeviceDelegate(url_);
134 if (!delegate)
135 return net::ERR_FAILED;
137 delegate->GetFileInfo(
138 url_.path(),
139 base::Bind(&MTPFileStreamReader::FinishGetLength,
140 weak_factory_.GetWeakPtr(), callback),
141 base::Bind(&CallInt64CompletionCallbackWithPlatformFileError,
142 callback));
144 return net::ERR_IO_PENDING;
147 void MTPFileStreamReader::FinishValidateMediaHeader(
148 net::IOBuffer* header_buf,
149 net::IOBuffer* buf, int buf_len,
150 const net::CompletionCallback& callback,
151 const base::File::Info& file_info,
152 int header_bytes_read) {
153 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
154 DCHECK_GE(header_bytes_read, 0);
155 base::File::Error error = NativeMediaFileUtil::BufferIsMediaHeader(
156 header_buf, header_bytes_read);
157 if (error != base::File::FILE_OK) {
158 CallCompletionCallbackWithPlatformFileError(callback, error);
159 return;
162 media_header_validated_ = true;
164 // Finish the read immediately if we've already finished reading into the
165 // originally requested buffer.
166 if (header_buf == buf)
167 return FinishRead(callback, file_info, header_bytes_read);
169 // Header buffer isn't the same as the original read buffer. Make a separate
170 // request for that.
171 ReadBytes(url_, buf, current_offset_, buf_len,
172 base::Bind(&MTPFileStreamReader::FinishRead,
173 weak_factory_.GetWeakPtr(), callback),
174 callback);
177 void MTPFileStreamReader::FinishRead(const net::CompletionCallback& callback,
178 const base::File::Info& file_info,
179 int bytes_read) {
180 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
182 if (!VerifySnapshotTime(expected_modification_time_, file_info)) {
183 callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
184 return;
187 DCHECK_GE(bytes_read, 0);
188 current_offset_ += bytes_read;
189 callback.Run(bytes_read);
192 void MTPFileStreamReader::FinishGetLength(
193 const net::Int64CompletionCallback& callback,
194 const base::File::Info& file_info) {
195 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
197 if (!VerifySnapshotTime(expected_modification_time_, file_info)) {
198 callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
199 return;
202 callback.Run(file_info.size);