Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / fileapi / external_file_url_request_job.cc
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.
4
5 #include "chrome/browser/chromeos/fileapi/external_file_url_request_job.h"
6
7 #include <algorithm>
8 #include <vector>
9
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/file_manager/fileapi_util.h"
16 #include "chrome/browser/chromeos/fileapi/external_file_url_util.h"
17 #include "chrome/browser/extensions/api/file_handlers/mime_util.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/storage_partition.h"
21 #include "content/public/common/url_constants.h"
22 #include "net/base/net_errors.h"
23 #include "net/http/http_byte_range.h"
24 #include "net/http/http_request_headers.h"
25 #include "net/http/http_response_info.h"
26 #include "net/http/http_util.h"
27 #include "net/url_request/url_request.h"
28 #include "net/url_request/url_request_status.h"
29 #include "storage/browser/fileapi/external_mount_points.h"
30 #include "storage/browser/fileapi/file_system_backend.h"
31 #include "storage/browser/fileapi/file_system_context.h"
32 #include "storage/browser/fileapi/file_system_operation_runner.h"
33 #include "storage/browser/fileapi/isolated_context.h"
34
35 using content::BrowserThread;
36
37 namespace chromeos {
38 namespace {
39
40 const char kMimeTypeForRFC822[] = "message/rfc822";
41 const char kMimeTypeForMHTML[] = "multipart/related";
42
43 // Helper for obtaining FileSystemContext, FileSystemURL, and mime type on the
44 // UI thread.
45 class URLHelper {
46  public:
47   // The scoped pointer to control lifetime of the instance itself. The pointer
48   // is passed to callback functions and binds the lifetime of the instance to
49   // the callback's lifetime.
50   typedef scoped_ptr<URLHelper> Lifetime;
51
52   URLHelper(void* profile_id,
53             const GURL& url,
54             const ExternalFileURLRequestJob::HelperCallback& callback)
55       : profile_id_(profile_id), url_(url), callback_(callback) {
56     DCHECK_CURRENTLY_ON(BrowserThread::IO);
57     Lifetime lifetime(this);
58     BrowserThread::PostTask(BrowserThread::UI,
59                             FROM_HERE,
60                             base::Bind(&URLHelper::RunOnUIThread,
61                                        base::Unretained(this),
62                                        base::Passed(&lifetime)));
63   }
64
65  private:
66   void RunOnUIThread(Lifetime lifetime) {
67     DCHECK_CURRENTLY_ON(BrowserThread::UI);
68     Profile* const profile = reinterpret_cast<Profile*>(profile_id_);
69     if (!g_browser_process->profile_manager()->IsValidProfile(profile)) {
70       ReplyResult(net::ERR_FAILED);
71       return;
72     }
73     content::StoragePartition* const storage =
74         content::BrowserContext::GetStoragePartitionForSite(profile, url_);
75     DCHECK(storage);
76
77     scoped_refptr<storage::FileSystemContext> context =
78         storage->GetFileSystemContext();
79     DCHECK(context.get());
80
81     // Obtain the absolute path in the file system.
82     const base::FilePath virtual_path = ExternalFileURLToVirtualPath(url_);
83
84     // Obtain the file system URL.
85     file_system_url_ = file_manager::util::CreateIsolatedURLFromVirtualPath(
86         *context, /* empty origin */ GURL(), virtual_path);
87
88     // Check if the obtained path providing external file URL or not.
89     if (!file_system_url_.is_valid()) {
90       ReplyResult(net::ERR_INVALID_URL);
91       return;
92     }
93
94     isolated_file_system_scope_.reset(
95         new ExternalFileURLRequestJob::IsolatedFileSystemScope(
96             file_system_url_.filesystem_id()));
97
98     if (!IsExternalFileURLType(file_system_url_.type())) {
99       ReplyResult(net::ERR_FAILED);
100       return;
101     }
102
103     file_system_context_ = context;
104     extensions::app_file_handler_util::GetMimeTypeForLocalPath(
105         profile,
106         file_system_url_.path(),
107         base::Bind(&URLHelper::OnGotMimeTypeOnUIThread,
108                    base::Unretained(this),
109                    base::Passed(&lifetime)));
110   }
111
112   void OnGotMimeTypeOnUIThread(Lifetime lifetime,
113                                const std::string& mime_type) {
114     DCHECK_CURRENTLY_ON(BrowserThread::UI);
115     mime_type_ = mime_type;
116
117     if (mime_type_ == kMimeTypeForRFC822)
118       mime_type_ = kMimeTypeForMHTML;
119
120     ReplyResult(net::OK);
121   }
122
123   void ReplyResult(net::Error error) {
124     DCHECK_CURRENTLY_ON(BrowserThread::UI);
125
126     BrowserThread::PostTask(
127         BrowserThread::IO,
128         FROM_HERE,
129         base::Bind(callback_,
130                    error,
131                    file_system_context_,
132                    base::Passed(&isolated_file_system_scope_),
133                    file_system_url_,
134                    mime_type_));
135   }
136
137   void* const profile_id_;
138   const GURL url_;
139   const ExternalFileURLRequestJob::HelperCallback callback_;
140   scoped_refptr<storage::FileSystemContext> file_system_context_;
141   scoped_ptr<ExternalFileURLRequestJob::IsolatedFileSystemScope>
142       isolated_file_system_scope_;
143   storage::FileSystemURL file_system_url_;
144   std::string mime_type_;
145
146   DISALLOW_COPY_AND_ASSIGN(URLHelper);
147 };
148
149 }  // namespace
150
151 ExternalFileURLRequestJob::IsolatedFileSystemScope::IsolatedFileSystemScope(
152     const std::string& file_system_id)
153     : file_system_id_(file_system_id) {
154 }
155
156 ExternalFileURLRequestJob::IsolatedFileSystemScope::~IsolatedFileSystemScope() {
157   storage::IsolatedContext::GetInstance()->RevokeFileSystem(file_system_id_);
158 }
159
160 ExternalFileURLRequestJob::ExternalFileURLRequestJob(
161     void* profile_id,
162     net::URLRequest* request,
163     net::NetworkDelegate* network_delegate)
164     : net::URLRequestJob(request, network_delegate),
165       profile_id_(profile_id),
166       remaining_bytes_(0),
167       weak_ptr_factory_(this) {
168 }
169
170 void ExternalFileURLRequestJob::SetExtraRequestHeaders(
171     const net::HttpRequestHeaders& headers) {
172   std::string range_header;
173   if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
174     // Note: We only support single range requests.
175     std::vector<net::HttpByteRange> ranges;
176     if (net::HttpUtil::ParseRangeHeader(range_header, &ranges) &&
177         ranges.size() == 1) {
178       byte_range_ = ranges[0];
179     } else {
180       // Failed to parse Range: header, so notify the error.
181       NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
182                                        net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
183     }
184   }
185 }
186
187 void ExternalFileURLRequestJob::Start() {
188   DVLOG(1) << "Starting request";
189   DCHECK_CURRENTLY_ON(BrowserThread::IO);
190   DCHECK(!stream_reader_);
191
192   // We only support GET request.
193   if (request()->method() != "GET") {
194     LOG(WARNING) << "Failed to start request: " << request()->method()
195                  << " method is not supported";
196     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
197                                            net::ERR_METHOD_NOT_SUPPORTED));
198     return;
199   }
200
201   // Check if the scheme is correct.
202   if (!request()->url().SchemeIs(content::kExternalFileScheme)) {
203     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
204                                            net::ERR_INVALID_URL));
205     return;
206   }
207
208   // Owned by itself.
209   new URLHelper(profile_id_,
210                 request()->url(),
211                 base::Bind(&ExternalFileURLRequestJob::OnHelperResultObtained,
212                            weak_ptr_factory_.GetWeakPtr()));
213 }
214
215 void ExternalFileURLRequestJob::OnHelperResultObtained(
216     net::Error error,
217     const scoped_refptr<storage::FileSystemContext>& file_system_context,
218     scoped_ptr<IsolatedFileSystemScope> isolated_file_system_scope,
219     const storage::FileSystemURL& file_system_url,
220     const std::string& mime_type) {
221   DCHECK_CURRENTLY_ON(BrowserThread::IO);
222
223   if (error != net::OK) {
224     NotifyStartError(
225         net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
226     return;
227   }
228
229   DCHECK(file_system_context.get());
230   file_system_context_ = file_system_context;
231   isolated_file_system_scope_ = isolated_file_system_scope.Pass();
232   file_system_url_ = file_system_url;
233   mime_type_ = mime_type;
234
235   // Check if the entry has a redirect URL.
236   file_system_context_->external_backend()->GetRedirectURLForContents(
237       file_system_url_,
238       base::Bind(&ExternalFileURLRequestJob::OnRedirectURLObtained,
239                  weak_ptr_factory_.GetWeakPtr()));
240 }
241
242 void ExternalFileURLRequestJob::OnRedirectURLObtained(
243     const GURL& redirect_url) {
244   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
245   redirect_url_ = redirect_url;
246   if (!redirect_url_.is_empty()) {
247     NotifyHeadersComplete();
248     return;
249   }
250
251   // Obtain file system context.
252   file_system_context_->operation_runner()->GetMetadata(
253       file_system_url_,
254       base::Bind(&ExternalFileURLRequestJob::OnFileInfoObtained,
255                  weak_ptr_factory_.GetWeakPtr()));
256 }
257
258 void ExternalFileURLRequestJob::OnFileInfoObtained(
259     base::File::Error result,
260     const base::File::Info& file_info) {
261   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
262
263   if (result == base::File::FILE_ERROR_NOT_FOUND) {
264     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
265                                            net::ERR_FILE_NOT_FOUND));
266     return;
267   }
268
269   if (result != base::File::FILE_OK || file_info.is_directory ||
270       file_info.size < 0) {
271     NotifyStartError(
272         net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED));
273     return;
274   }
275
276   // Compute content size.
277   if (!byte_range_.ComputeBounds(file_info.size)) {
278     NotifyStartError(net::URLRequestStatus(
279         net::URLRequestStatus::FAILED, net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
280     return;
281   }
282   const int64 offset = byte_range_.first_byte_position();
283   const int64 size =
284       byte_range_.last_byte_position() + 1 - byte_range_.first_byte_position();
285   set_expected_content_size(size);
286   remaining_bytes_ = size;
287
288   // Create file stream reader.
289   stream_reader_ = file_system_context_->CreateFileStreamReader(
290       file_system_url_, offset, size, base::Time());
291   if (!stream_reader_) {
292     NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
293                                            net::ERR_FILE_NOT_FOUND));
294     return;
295   }
296
297   NotifyHeadersComplete();
298 }
299
300 void ExternalFileURLRequestJob::Kill() {
301   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
302
303   stream_reader_.reset();
304   isolated_file_system_scope_.reset();
305   file_system_context_ = NULL;
306   net::URLRequestJob::Kill();
307   weak_ptr_factory_.InvalidateWeakPtrs();
308 }
309
310 bool ExternalFileURLRequestJob::GetMimeType(std::string* mime_type) const {
311   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
312   mime_type->assign(mime_type_);
313   return !mime_type->empty();
314 }
315
316 bool ExternalFileURLRequestJob::IsRedirectResponse(GURL* location,
317                                                    int* http_status_code) {
318   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
319   if (redirect_url_.is_empty())
320     return false;
321
322   // Redirect a hosted document.
323   *location = redirect_url_;
324   const int kHttpFound = 302;
325   *http_status_code = kHttpFound;
326   return true;
327 }
328
329 bool ExternalFileURLRequestJob::ReadRawData(net::IOBuffer* buf,
330                                             int buf_size,
331                                             int* bytes_read) {
332   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
333   DCHECK(stream_reader_);
334
335   if (remaining_bytes_ == 0) {
336     *bytes_read = 0;
337     return true;
338   }
339
340   const int result = stream_reader_->Read(
341       buf,
342       std::min<int64>(buf_size, remaining_bytes_),
343       base::Bind(&ExternalFileURLRequestJob::OnReadCompleted,
344                  weak_ptr_factory_.GetWeakPtr()));
345
346   if (result == net::ERR_IO_PENDING) {
347     // The data is not yet available.
348     SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
349     return false;
350   }
351   if (result < 0) {
352     // An error occurs.
353     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
354     return false;
355   }
356
357   // Reading has been finished immediately.
358   *bytes_read = result;
359   remaining_bytes_ -= result;
360   return true;
361 }
362
363 ExternalFileURLRequestJob::~ExternalFileURLRequestJob() {
364 }
365
366 void ExternalFileURLRequestJob::OnReadCompleted(int read_result) {
367   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
368
369   if (read_result < 0) {
370     DCHECK_NE(read_result, net::ERR_IO_PENDING);
371     NotifyDone(
372         net::URLRequestStatus(net::URLRequestStatus::FAILED, read_result));
373   }
374
375   remaining_bytes_ -= read_result;
376   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status.
377   NotifyReadComplete(read_result);
378 }
379
380 }  // namespace chromeos