Rename the drive: scheme with the externalfile: scheme.
[chromium-blink-merge.git] / chrome / browser / chromeos / fileapi / external_file_url_request_job.cc
blobff54aff2d083e46c3d8922bda72a1a8df7e561b6
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/chromeos/fileapi/external_file_url_request_job.h"
7 #include <algorithm>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/chromeos/drive/file_system_util.h"
15 #include "chrome/browser/chromeos/fileapi/external_file_url_util.h"
16 #include "chrome/browser/extensions/api/file_handlers/mime_util.h"
17 #include "chrome/browser/profiles/profile_manager.h"
18 #include "chrome/common/url_constants.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/storage_partition.h"
21 #include "net/base/net_errors.h"
22 #include "net/http/http_byte_range.h"
23 #include "net/http/http_request_headers.h"
24 #include "net/http/http_response_info.h"
25 #include "net/http/http_util.h"
26 #include "net/url_request/url_request.h"
27 #include "net/url_request/url_request_status.h"
28 #include "storage/browser/fileapi/file_system_backend.h"
29 #include "storage/browser/fileapi/file_system_context.h"
30 #include "storage/browser/fileapi/file_system_operation_runner.h"
32 using content::BrowserThread;
34 namespace chromeos {
35 namespace {
37 const char kMimeTypeForRFC822[] = "message/rfc822";
38 const char kMimeTypeForMHTML[] = "multipart/related";
40 // Check if the |url| points a valid location or not.
41 bool IsValidURL(const storage::FileSystemURL& url) {
42 switch (url.type()) {
43 case storage::kFileSystemTypeDrive: {
44 const base::FilePath my_drive_path =
45 drive::util::GetDriveMyDriveRootPath();
46 const base::FilePath drive_other_path =
47 drive::util::GetDriveGrandRootPath().Append(
48 drive::util::kDriveOtherDirName);
49 const base::FilePath url_drive_path =
50 drive::util::ExtractDrivePathFromFileSystemUrl(url);
51 return my_drive_path == url_drive_path ||
52 my_drive_path.IsParent(url_drive_path) ||
53 drive_other_path.IsParent(url_drive_path);
55 default:
56 return false;
60 // Helper for obtaining FileSystemContext, FileSystemURL, and mime type on the
61 // UI thread.
62 class URLHelper {
63 public:
64 // The scoped pointer to control lifetime of the instance itself. The pointer
65 // is passed to callback functions and binds the lifetime of the instance to
66 // the callback's lifetime.
67 typedef scoped_ptr<URLHelper> Lifetime;
69 URLHelper(void* profile_id,
70 const GURL& url,
71 const ExternalFileURLRequestJob::HelperCallback& callback)
72 : profile_id_(profile_id), url_(url), callback_(callback) {
73 DCHECK_CURRENTLY_ON(BrowserThread::IO);
74 Lifetime lifetime(this);
75 BrowserThread::PostTask(BrowserThread::UI,
76 FROM_HERE,
77 base::Bind(&URLHelper::RunOnUIThread,
78 base::Unretained(this),
79 base::Passed(&lifetime)));
82 private:
83 void RunOnUIThread(Lifetime lifetime) {
84 DCHECK_CURRENTLY_ON(BrowserThread::UI);
85 Profile* const profile = reinterpret_cast<Profile*>(profile_id_);
86 if (!g_browser_process->profile_manager()->IsValidProfile(profile)) {
87 ReplyResult(net::ERR_FAILED);
88 return;
90 content::StoragePartition* const storage =
91 content::BrowserContext::GetStoragePartitionForSite(profile, url_);
92 DCHECK(storage);
94 scoped_refptr<storage::FileSystemContext> context =
95 storage->GetFileSystemContext();
96 DCHECK(context.get());
98 // Obtain the absolute path in the file system.
99 base::FilePath path = drive::util::GetDriveMountPointPath(profile);
100 drive::util::GetDriveGrandRootPath().AppendRelativePath(
101 ExternalFileURLToFilePath(url_), &path);
103 storage::ExternalFileSystemBackend* const backend =
104 context->external_backend();
105 DCHECK(backend);
107 // Obtain the virtual path.
108 base::FilePath virtual_path;
109 if (!backend->GetVirtualPath(path, &virtual_path)) {
110 ReplyResult(net::ERR_FILE_NOT_FOUND);
111 return;
114 // Obtain the file system URL.
115 // TODO(hirono): After removing MHTML support, stop to use the special
117 // drive: scheme and use filesystem: URL directly. crbug.com/415455
118 file_system_url_ = context->CreateCrackedFileSystemURL(
119 GURL(std::string(chrome::kExternalFileScheme) + ":"),
120 storage::kFileSystemTypeExternal,
121 virtual_path);
122 if (!IsValidURL(file_system_url_)) {
123 ReplyResult(net::ERR_INVALID_URL);
124 return;
127 file_system_context_ = context;
129 extensions::app_file_handler_util::GetMimeTypeForLocalPath(
130 profile,
131 file_system_url_.path(),
132 base::Bind(&URLHelper::OnGotMimeTypeOnUIThread,
133 base::Unretained(this),
134 base::Passed(&lifetime)));
137 void OnGotMimeTypeOnUIThread(Lifetime lifetime,
138 const std::string& mime_type) {
139 DCHECK_CURRENTLY_ON(BrowserThread::UI);
140 mime_type_ = mime_type;
142 if (mime_type_ == kMimeTypeForRFC822)
143 mime_type_ = kMimeTypeForMHTML;
145 ReplyResult(net::OK);
148 void ReplyResult(net::Error error) {
149 DCHECK_CURRENTLY_ON(BrowserThread::UI);
151 BrowserThread::PostTask(BrowserThread::IO,
152 FROM_HERE,
153 base::Bind(callback_,
154 error,
155 file_system_context_,
156 file_system_url_,
157 mime_type_));
160 void* const profile_id_;
161 const GURL url_;
162 const ExternalFileURLRequestJob::HelperCallback callback_;
163 scoped_refptr<storage::FileSystemContext> file_system_context_;
164 storage::FileSystemURL file_system_url_;
165 std::string mime_type_;
167 DISALLOW_COPY_AND_ASSIGN(URLHelper);
170 } // namespace
172 ExternalFileURLRequestJob::ExternalFileURLRequestJob(
173 void* profile_id,
174 net::URLRequest* request,
175 net::NetworkDelegate* network_delegate)
176 : net::URLRequestJob(request, network_delegate),
177 profile_id_(profile_id),
178 remaining_bytes_(0),
179 weak_ptr_factory_(this) {
182 void ExternalFileURLRequestJob::SetExtraRequestHeaders(
183 const net::HttpRequestHeaders& headers) {
184 std::string range_header;
185 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
186 // Note: We only support single range requests.
187 std::vector<net::HttpByteRange> ranges;
188 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges) &&
189 ranges.size() == 1) {
190 byte_range_ = ranges[0];
191 } else {
192 // Failed to parse Range: header, so notify the error.
193 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
194 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
199 void ExternalFileURLRequestJob::Start() {
200 DVLOG(1) << "Starting request";
201 DCHECK_CURRENTLY_ON(BrowserThread::IO);
202 DCHECK(!stream_reader_);
204 // We only support GET request.
205 if (request()->method() != "GET") {
206 LOG(WARNING) << "Failed to start request: " << request()->method()
207 << " method is not supported";
208 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
209 net::ERR_METHOD_NOT_SUPPORTED));
210 return;
213 // Check if the scheme is correct.
214 if (!request()->url().SchemeIs(chrome::kExternalFileScheme)) {
215 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
216 net::ERR_INVALID_URL));
217 return;
220 // Owned by itself.
221 new URLHelper(profile_id_,
222 request()->url(),
223 base::Bind(&ExternalFileURLRequestJob::OnHelperResultObtained,
224 weak_ptr_factory_.GetWeakPtr()));
227 void ExternalFileURLRequestJob::OnHelperResultObtained(
228 net::Error error,
229 const scoped_refptr<storage::FileSystemContext>& file_system_context,
230 const storage::FileSystemURL& file_system_url,
231 const std::string& mime_type) {
232 DCHECK_CURRENTLY_ON(BrowserThread::IO);
234 if (error != net::OK) {
235 NotifyStartError(
236 net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
237 return;
240 DCHECK(file_system_context.get());
241 file_system_context_ = file_system_context;
242 file_system_url_ = file_system_url;
243 mime_type_ = mime_type;
245 // Check if the entry has a redirect URL.
246 file_system_context_->external_backend()->GetRedirectURLForContents(
247 file_system_url_,
248 base::Bind(&ExternalFileURLRequestJob::OnRedirectURLObtained,
249 weak_ptr_factory_.GetWeakPtr()));
252 void ExternalFileURLRequestJob::OnRedirectURLObtained(
253 const GURL& redirect_url) {
254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
255 redirect_url_ = redirect_url;
256 if (!redirect_url_.is_empty()) {
257 NotifyHeadersComplete();
258 return;
261 // Obtain file system context.
262 file_system_context_->operation_runner()->GetMetadata(
263 file_system_url_,
264 base::Bind(&ExternalFileURLRequestJob::OnFileInfoObtained,
265 weak_ptr_factory_.GetWeakPtr()));
268 void ExternalFileURLRequestJob::OnFileInfoObtained(
269 base::File::Error result,
270 const base::File::Info& file_info) {
271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
273 if (result == base::File::FILE_ERROR_NOT_FOUND) {
274 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
275 net::ERR_FILE_NOT_FOUND));
276 return;
279 if (result != base::File::FILE_OK || file_info.is_directory ||
280 file_info.size < 0) {
281 NotifyStartError(
282 net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED));
283 return;
286 // Compute content size.
287 if (!byte_range_.ComputeBounds(file_info.size)) {
288 NotifyStartError(net::URLRequestStatus(
289 net::URLRequestStatus::FAILED, net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
290 return;
292 const int64 offset = byte_range_.first_byte_position();
293 const int64 size =
294 byte_range_.last_byte_position() + 1 - byte_range_.first_byte_position();
295 set_expected_content_size(size);
296 remaining_bytes_ = size;
298 // Create file stream reader.
299 stream_reader_ = file_system_context_->CreateFileStreamReader(
300 file_system_url_, offset, size, base::Time());
301 if (!stream_reader_) {
302 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
303 net::ERR_FILE_NOT_FOUND));
304 return;
307 NotifyHeadersComplete();
310 void ExternalFileURLRequestJob::Kill() {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
313 stream_reader_.reset();
314 file_system_context_ = NULL;
315 net::URLRequestJob::Kill();
316 weak_ptr_factory_.InvalidateWeakPtrs();
319 bool ExternalFileURLRequestJob::GetMimeType(std::string* mime_type) const {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
321 mime_type->assign(mime_type_);
322 return !mime_type->empty();
325 bool ExternalFileURLRequestJob::IsRedirectResponse(GURL* location,
326 int* http_status_code) {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
328 if (redirect_url_.is_empty())
329 return false;
331 // Redirect a hosted document.
332 *location = redirect_url_;
333 const int kHttpFound = 302;
334 *http_status_code = kHttpFound;
335 return true;
338 bool ExternalFileURLRequestJob::ReadRawData(net::IOBuffer* buf,
339 int buf_size,
340 int* bytes_read) {
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
342 DCHECK(stream_reader_);
344 if (remaining_bytes_ == 0) {
345 *bytes_read = 0;
346 return true;
349 const int result = stream_reader_->Read(
350 buf,
351 std::min<int64>(buf_size, remaining_bytes_),
352 base::Bind(&ExternalFileURLRequestJob::OnReadCompleted,
353 weak_ptr_factory_.GetWeakPtr()));
355 if (result == net::ERR_IO_PENDING) {
356 // The data is not yet available.
357 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
358 return false;
360 if (result < 0) {
361 // An error occurs.
362 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
363 return false;
366 // Reading has been finished immediately.
367 *bytes_read = result;
368 remaining_bytes_ -= result;
369 return true;
372 ExternalFileURLRequestJob::~ExternalFileURLRequestJob() {
375 void ExternalFileURLRequestJob::OnReadCompleted(int read_result) {
376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
378 if (read_result < 0) {
379 DCHECK_NE(read_result, net::ERR_IO_PENDING);
380 NotifyDone(
381 net::URLRequestStatus(net::URLRequestStatus::FAILED, read_result));
384 remaining_bytes_ -= read_result;
385 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status.
386 NotifyReadComplete(read_result);
389 } // namespace chromeos