blob: 28b6cd80113518c7086a85834fae639334636bac [file] [log] [blame]
Ken Rockot314714c2017-11-05 23:36:241// Copyright 2017 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 "content/browser/file_url_loader_factory.h"
6
7#include <memory>
8#include <string>
9#include <utility>
10
11#include "base/bind.h"
12#include "base/files/file.h"
13#include "base/files/file_path.h"
14#include "base/files/file_util.h"
15#include "base/macros.h"
16#include "base/numerics/safe_conversions.h"
17#include "base/strings/string16.h"
18#include "base/strings/string_util.h"
19#include "base/strings/sys_string_conversions.h"
20#include "base/strings/utf_string_conversions.h"
Ken Rockot6414c4d92017-11-08 19:58:3221#include "base/task_scheduler/post_task.h"
Ken Rockot314714c2017-11-05 23:36:2422#include "base/time/time.h"
23#include "build/build_config.h"
24#include "content/public/browser/content_browser_client.h"
Ken Rockot6414c4d92017-11-08 19:58:3225#include "content/public/browser/file_url_loader.h"
Ken Rockot314714c2017-11-05 23:36:2426#include "content/public/common/resource_request.h"
27#include "content/public/common/url_loader.mojom.h"
28#include "mojo/public/cpp/bindings/strong_binding.h"
29#include "mojo/public/cpp/system/data_pipe.h"
30#include "mojo/public/cpp/system/file_data_pipe_producer.h"
31#include "mojo/public/cpp/system/string_data_pipe_producer.h"
32#include "net/base/directory_lister.h"
33#include "net/base/directory_listing.h"
34#include "net/base/filename_util.h"
35#include "net/base/mime_sniffer.h"
36#include "net/base/mime_util.h"
37#include "net/base/net_errors.h"
38#include "net/http/http_byte_range.h"
39#include "net/http/http_util.h"
40#include "net/url_request/redirect_info.h"
41#include "url/gurl.h"
42
43#if defined(OS_WIN)
44#include "base/win/shortcut.h"
45#endif
46
47namespace content {
48namespace {
49
50constexpr size_t kDefaultFileUrlPipeSize = 65536;
51
52// Because this makes things simpler.
53static_assert(kDefaultFileUrlPipeSize >= net::kMaxBytesToSniff,
54 "Default file data pipe size must be at least as large as a MIME-"
55 "type sniffing buffer.");
56
Ken Rockot6414c4d92017-11-08 19:58:3257// Policy to control how a FileURLLoader will handle directory URLs.
58enum class DirectoryLoadingPolicy {
59 // File paths which refer to directories are allowed and will load as an
60 // HTML directory listing.
61 kRespondWithListing,
62
63 // File paths which refer to directories are treated as non-existent and
64 // will result in FILE_NOT_FOUND errors.
65 kFail,
66};
67
68// Policy to control whether or not file access constraints imposed by content
69// or its embedder should be honored by a FileURLLoader.
70enum class FileAccessPolicy {
71 // Enforces file acccess policy defined by content and/or its embedder.
72 kRestricted,
73
74 // Ignores file access policy, allowing contents to be loaded from any
75 // resolvable file path given.
76 kUnrestricted,
77};
78
79// Policy to control whether or not a FileURLLoader should follow filesystem
80// links (e.g. Windows shortcuts) where applicable.
81enum class LinkFollowingPolicy {
82 kFollow,
83 kDoNotFollow,
84};
85
Ken Rockot314714c2017-11-05 23:36:2486class FileURLDirectoryLoader
87 : public mojom::URLLoader,
88 public net::DirectoryLister::DirectoryListerDelegate {
89 public:
90 static void CreateAndStart(const base::FilePath& profile_path,
91 const ResourceRequest& request,
92 mojom::URLLoaderRequest loader,
93 mojom::URLLoaderClientPtrInfo client_info) {
94 // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr
95 // bindings are alive - essentially until either the client gives up or all
96 // file data has been sent to it.
97 auto* file_url_loader = new FileURLDirectoryLoader;
98 file_url_loader->Start(profile_path, request, std::move(loader),
99 std::move(client_info));
100 }
101
102 // mojom::URLLoader:
103 void FollowRedirect() override {}
104 void SetPriority(net::RequestPriority priority,
105 int32_t intra_priority_value) override {}
106 void PauseReadingBodyFromNet() override {}
107 void ResumeReadingBodyFromNet() override {}
108
109 private:
110 FileURLDirectoryLoader() : binding_(this) {}
111 ~FileURLDirectoryLoader() override = default;
112
113 void Start(const base::FilePath& profile_path,
114 const ResourceRequest& request,
115 mojom::URLLoaderRequest loader,
116 mojom::URLLoaderClientPtrInfo client_info) {
117 binding_.Bind(std::move(loader));
118 binding_.set_connection_error_handler(base::BindOnce(
119 &FileURLDirectoryLoader::OnConnectionError, base::Unretained(this)));
120
121 mojom::URLLoaderClientPtr client;
122 client.Bind(std::move(client_info));
123
124 if (!net::FileURLToFilePath(request.url, &path_)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26125 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24126 return;
127 }
128
129 base::File::Info info;
130 if (!base::GetFileInfo(path_, &info) || !info.is_directory) {
Takashi Toyoshimaaa278662017-11-20 11:11:26131 client->OnComplete(
132 network::URLLoaderCompletionStatus(net::ERR_FILE_NOT_FOUND));
Ken Rockot314714c2017-11-05 23:36:24133 return;
134 }
135
136 if (!GetContentClient()->browser()->IsFileAccessAllowed(
137 path_, base::MakeAbsoluteFilePath(path_), profile_path)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26138 client->OnComplete(
139 network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED));
Ken Rockot314714c2017-11-05 23:36:24140 return;
141 }
142
143 mojo::DataPipe pipe(kDefaultFileUrlPipeSize);
144 if (!pipe.consumer_handle.is_valid()) {
Takashi Toyoshimaaa278662017-11-20 11:11:26145 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24146 return;
147 }
148
149 ResourceResponseHead head;
150 head.mime_type = "text/html";
151 head.charset = "utf-8";
152 client->OnReceiveResponse(head, base::nullopt, nullptr);
153 client->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
154 client_ = std::move(client);
155
156 lister_ = std::make_unique<net::DirectoryLister>(path_, this);
157 lister_->Start();
158
159 data_producer_ = std::make_unique<mojo::StringDataPipeProducer>(
160 std::move(pipe.producer_handle));
161 }
162
163 void OnConnectionError() {
164 binding_.Close();
165 MaybeDeleteSelf();
166 }
167
168 void MaybeDeleteSelf() {
169 if (!binding_.is_bound() && !client_.is_bound() && !lister_)
170 delete this;
171 }
172
173 // net::DirectoryLister::DirectoryListerDelegate:
174 void OnListFile(
175 const net::DirectoryLister::DirectoryListerData& data) override {
176 if (!wrote_header_) {
177 wrote_header_ = true;
178
179#if defined(OS_WIN)
180 const base::string16& title = path_.value();
181#elif defined(OS_POSIX)
182 const base::string16& title =
183 base::WideToUTF16(base::SysNativeMBToWide(path_.value()));
184#endif
185 pending_data_.append(net::GetDirectoryListingHeader(title));
186
187 // If not a top-level directory, add a link to the parent directory. To
188 // figure this out, first normalize |path_| by stripping trailing
189 // separators. Then compare the result to its DirName(). For the top-level
190 // directory, e.g. "/" or "c:\\", the normalized path is equal to its own
191 // DirName().
192 base::FilePath stripped_path = path_.StripTrailingSeparators();
193 if (stripped_path != stripped_path.DirName())
194 pending_data_.append(net::GetParentDirectoryLink());
195 }
196
197 // Skip current / parent links from the listing.
198 base::FilePath filename = data.info.GetName();
199 if (filename.value() != base::FilePath::kCurrentDirectory &&
200 filename.value() != base::FilePath::kParentDirectory) {
201#if defined(OS_WIN)
202 std::string raw_bytes; // Empty on Windows means UTF-8 encoded name.
203#elif defined(OS_POSIX)
204 const std::string& raw_bytes = filename.value();
205#endif
206 pending_data_.append(net::GetDirectoryListingEntry(
207 filename.LossyDisplayName(), raw_bytes, data.info.IsDirectory(),
208 data.info.GetSize(), data.info.GetLastModifiedTime()));
209 }
210
211 MaybeTransferPendingData();
212 }
213
214 void OnListDone(int error) override {
215 listing_result_ = error;
216 lister_.reset();
217 MaybeDeleteSelf();
218 }
219
220 void MaybeTransferPendingData() {
221 if (transfer_in_progress_)
222 return;
223
224 transfer_in_progress_ = true;
225 data_producer_->Write(pending_data_,
226 base::BindOnce(&FileURLDirectoryLoader::OnDataWritten,
227 base::Unretained(this)));
228 // The producer above will have already copied any parts of |pending_data_|
229 // that couldn't be written immediately, so we can wipe it out here to begin
230 // accumulating more data.
231 pending_data_.clear();
232 }
233
234 void OnDataWritten(MojoResult result) {
235 transfer_in_progress_ = false;
236
237 int completion_status;
238 if (result == MOJO_RESULT_OK) {
239 if (!pending_data_.empty()) {
240 // Keep flushing the data buffer as long as it's non-empty and pipe
241 // writes are succeeding.
242 MaybeTransferPendingData();
243 return;
244 }
245
246 // If there's no pending data but the lister is still active, we simply
247 // wait for more listing results.
248 if (lister_)
249 return;
250
251 // At this point we know the listing is complete and all available data
252 // has been transferred. We inherit the status of the listing operation.
253 completion_status = listing_result_;
254 } else {
255 completion_status = net::ERR_FAILED;
256 }
257
Takashi Toyoshimaaa278662017-11-20 11:11:26258 client_->OnComplete(network::URLLoaderCompletionStatus(completion_status));
Ken Rockot314714c2017-11-05 23:36:24259 client_.reset();
260 MaybeDeleteSelf();
261 }
262
263 base::FilePath path_;
264 std::unique_ptr<net::DirectoryLister> lister_;
265 bool wrote_header_ = false;
266 int listing_result_;
267
268 mojo::Binding<mojom::URLLoader> binding_;
269 mojom::URLLoaderClientPtr client_;
270
271 std::unique_ptr<mojo::StringDataPipeProducer> data_producer_;
272 std::string pending_data_;
273 bool transfer_in_progress_ = false;
274
275 DISALLOW_COPY_AND_ASSIGN(FileURLDirectoryLoader);
276};
277
278class FileURLLoader : public mojom::URLLoader {
279 public:
280 static void CreateAndStart(const base::FilePath& profile_path,
281 const ResourceRequest& request,
282 mojom::URLLoaderRequest loader,
Ken Rockot6414c4d92017-11-08 19:58:32283 mojom::URLLoaderClientPtrInfo client_info,
284 DirectoryLoadingPolicy directory_loading_policy,
285 FileAccessPolicy file_access_policy,
286 LinkFollowingPolicy link_following_policy) {
Ken Rockot314714c2017-11-05 23:36:24287 // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr
288 // bindings are alive - essentially until either the client gives up or all
289 // file data has been sent to it.
290 auto* file_url_loader = new FileURLLoader;
291 file_url_loader->Start(profile_path, request, std::move(loader),
Ken Rockot6414c4d92017-11-08 19:58:32292 std::move(client_info), directory_loading_policy,
293 file_access_policy, link_following_policy);
Ken Rockot314714c2017-11-05 23:36:24294 }
295
296 // mojom::URLLoader:
297 void FollowRedirect() override {}
298 void SetPriority(net::RequestPriority priority,
299 int32_t intra_priority_value) override {}
300 void PauseReadingBodyFromNet() override {}
301 void ResumeReadingBodyFromNet() override {}
302
303 private:
304 FileURLLoader() : binding_(this) {}
305 ~FileURLLoader() override = default;
306
307 void Start(const base::FilePath& profile_path,
308 const ResourceRequest& request,
309 mojom::URLLoaderRequest loader,
Ken Rockot6414c4d92017-11-08 19:58:32310 mojom::URLLoaderClientPtrInfo client_info,
311 DirectoryLoadingPolicy directory_loading_policy,
312 FileAccessPolicy file_access_policy,
313 LinkFollowingPolicy link_following_policy) {
Ken Rockot314714c2017-11-05 23:36:24314 ResourceResponseHead head;
315 head.request_start = base::TimeTicks::Now();
316 head.response_start = base::TimeTicks::Now();
317 binding_.Bind(std::move(loader));
318 binding_.set_connection_error_handler(base::BindOnce(
319 &FileURLLoader::OnConnectionError, base::Unretained(this)));
320
321 mojom::URLLoaderClientPtr client;
322 client.Bind(std::move(client_info));
323
324 base::FilePath path;
325 if (!net::FileURLToFilePath(request.url, &path)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26326 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24327 return;
328 }
329
330 base::File::Info info;
331 if (!base::GetFileInfo(path, &info)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26332 client->OnComplete(
333 network::URLLoaderCompletionStatus(net::ERR_FILE_NOT_FOUND));
Ken Rockot314714c2017-11-05 23:36:24334 return;
335 }
336
Ken Rockot6414c4d92017-11-08 19:58:32337 if (info.is_directory) {
338 if (directory_loading_policy == DirectoryLoadingPolicy::kFail) {
Takashi Toyoshimaaa278662017-11-20 11:11:26339 client->OnComplete(
340 network::URLLoaderCompletionStatus(net::ERR_FILE_NOT_FOUND));
Ken Rockot6414c4d92017-11-08 19:58:32341 return;
342 }
Ken Rockot314714c2017-11-05 23:36:24343
Ken Rockot6414c4d92017-11-08 19:58:32344 DCHECK_EQ(directory_loading_policy,
345 DirectoryLoadingPolicy::kRespondWithListing);
346
347 GURL directory_url = request.url;
348 if (!path.EndsWithSeparator()) {
349 // If the named path is a directory with no trailing slash, redirect to
350 // the same path, but with a trailing slash.
351 std::string new_path = directory_url.path() + '/';
352 GURL::Replacements replacements;
353 replacements.SetPathStr(new_path);
354 directory_url = directory_url.ReplaceComponents(replacements);
355
356 net::RedirectInfo redirect_info;
357 redirect_info.new_method = "GET";
358 redirect_info.status_code = 301;
359 redirect_info.new_url = directory_url;
360 head.encoded_data_length = 0;
361 client->OnReceiveRedirect(redirect_info, head);
362 }
Ken Rockot314714c2017-11-05 23:36:24363
364 // Restart the request with a directory loader.
365 ResourceRequest new_request = request;
Ken Rockot6414c4d92017-11-08 19:58:32366 new_request.url = directory_url;
Ken Rockot314714c2017-11-05 23:36:24367 FileURLDirectoryLoader::CreateAndStart(
368 profile_path, new_request, binding_.Unbind(), client.PassInterface());
369 MaybeDeleteSelf();
370 return;
371 }
372
373#if defined(OS_WIN)
374 base::FilePath shortcut_target;
Ken Rockot6414c4d92017-11-08 19:58:32375 if (link_following_policy == LinkFollowingPolicy::kFollow &&
376 base::LowerCaseEqualsASCII(path.Extension(), ".lnk") &&
Ken Rockot314714c2017-11-05 23:36:24377 base::win::ResolveShortcut(path, &shortcut_target, nullptr)) {
378 // Follow Windows shortcuts
379 GURL new_url = net::FilePathToFileURL(shortcut_target);
380
381 net::RedirectInfo redirect_info;
382 redirect_info.new_method = "GET";
383 redirect_info.status_code = 301;
384 redirect_info.new_url = new_url;
385 head.encoded_data_length = 0;
386 client->OnReceiveRedirect(redirect_info, head);
387
388 // Restart the request with the new URL.
389 ResourceRequest new_request = request;
390 new_request.url = redirect_info.new_url;
391 return Start(profile_path, request, binding_.Unbind(),
Ken Rockot6414c4d92017-11-08 19:58:32392 client.PassInterface(), directory_loading_policy,
393 file_access_policy, link_following_policy);
Ken Rockot314714c2017-11-05 23:36:24394 }
395#endif // defined(OS_WIN)
396
Ken Rockot6414c4d92017-11-08 19:58:32397 if (file_access_policy == FileAccessPolicy::kRestricted &&
398 !GetContentClient()->browser()->IsFileAccessAllowed(
Ken Rockot314714c2017-11-05 23:36:24399 path, base::MakeAbsoluteFilePath(path), profile_path)) {
Takashi Toyoshimaaa278662017-11-20 11:11:26400 client->OnComplete(
401 network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED));
Ken Rockot314714c2017-11-05 23:36:24402 return;
403 }
404
405 mojo::DataPipe pipe(kDefaultFileUrlPipeSize);
406 if (!pipe.consumer_handle.is_valid()) {
Takashi Toyoshimaaa278662017-11-20 11:11:26407 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24408 return;
409 }
410
411 // Should never be possible for this to be a directory. If FileURLLoader
412 // is used to respond to a directory request, it must be because the URL
413 // path didn't have a trailing path separator. In that case we finish with
414 // a redirect above which will in turn be handled by FileURLDirectoryLoader.
415 DCHECK(!info.is_directory);
416
417 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
418 char initial_read_buffer[net::kMaxBytesToSniff];
419 int initial_read_result =
420 file.ReadAtCurrentPos(initial_read_buffer, net::kMaxBytesToSniff);
421 if (initial_read_result < 0) {
Takashi Toyoshimaaa278662017-11-20 11:11:26422 client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24423 return;
424 }
425 size_t initial_read_size = static_cast<size_t>(initial_read_result);
426
427 std::string range_header;
428 net::HttpByteRange byte_range;
429 if (request.headers.GetHeader(net::HttpRequestHeaders::kRange,
430 &range_header)) {
431 // Handle a simple Range header for a single range.
432 std::vector<net::HttpByteRange> ranges;
433 bool fail = false;
434 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges) &&
435 ranges.size() == 1) {
436 byte_range = ranges[0];
437 if (!byte_range.ComputeBounds(info.size))
438 fail = true;
439 } else {
440 fail = true;
441 }
442
443 if (fail) {
Takashi Toyoshimaaa278662017-11-20 11:11:26444 client->OnComplete(network::URLLoaderCompletionStatus(
445 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
Ken Rockot314714c2017-11-05 23:36:24446 return;
447 }
448 }
449
450 size_t first_byte_to_send = 0;
451 size_t total_bytes_to_send = static_cast<size_t>(info.size);
452 if (byte_range.IsValid()) {
453 first_byte_to_send =
454 static_cast<size_t>(byte_range.first_byte_position());
455 total_bytes_to_send =
456 static_cast<size_t>(byte_range.last_byte_position()) -
457 first_byte_to_send + 1;
458 }
459
460 head.content_length = base::saturated_cast<int64_t>(total_bytes_to_send);
461
462 if (first_byte_to_send < initial_read_size) {
463 // Write any data we read for MIME sniffing, constraining by range where
464 // applicable. This will always fit in the pipe (see assertion near
465 // |kDefaultFileUrlPipeSize| definition).
466 uint32_t write_size = std::min(
467 static_cast<uint32_t>(initial_read_size - first_byte_to_send),
468 static_cast<uint32_t>(total_bytes_to_send));
469 const uint32_t expected_write_size = write_size;
470 MojoResult result = pipe.producer_handle->WriteData(
471 &initial_read_buffer[first_byte_to_send], &write_size,
472 MOJO_WRITE_DATA_FLAG_NONE);
473 if (result != MOJO_RESULT_OK || write_size != expected_write_size) {
474 OnFileWritten(result);
475 return;
476 }
477
478 // Discount the bytes we just sent from the total range.
479 first_byte_to_send = initial_read_size;
480 total_bytes_to_send -= write_size;
481 }
482
483 if (!net::GetMimeTypeFromFile(path, &head.mime_type)) {
484 net::SniffMimeType(initial_read_buffer, initial_read_result, request.url,
485 head.mime_type, &head.mime_type);
486 }
487 client->OnReceiveResponse(head, base::nullopt, nullptr);
488 client->OnStartLoadingResponseBody(std::move(pipe.consumer_handle));
489 client_ = std::move(client);
490
491 if (total_bytes_to_send == 0) {
492 // There's definitely no more data, so we're already done.
493 OnFileWritten(MOJO_RESULT_OK);
494 return;
495 }
496
497 // In case of a range request, seek to the appropriate position before
498 // sending the remaining bytes asynchronously. Under normal conditions
499 // (i.e., no range request) this Seek is effectively a no-op.
500 file.Seek(base::File::FROM_BEGIN, static_cast<int64_t>(first_byte_to_send));
501
502 data_producer_ = std::make_unique<mojo::FileDataPipeProducer>(
503 std::move(pipe.producer_handle));
504 data_producer_->WriteFromFile(
505 std::move(file), total_bytes_to_send,
506 base::BindOnce(&FileURLLoader::OnFileWritten, base::Unretained(this)));
507 }
508
509 void OnConnectionError() {
510 binding_.Close();
511 MaybeDeleteSelf();
512 }
513
514 void MaybeDeleteSelf() {
515 if (!binding_.is_bound() && !client_.is_bound())
516 delete this;
517 }
518
519 void OnFileWritten(MojoResult result) {
520 if (result == MOJO_RESULT_OK)
Takashi Toyoshimaaa278662017-11-20 11:11:26521 client_->OnComplete(network::URLLoaderCompletionStatus(net::OK));
Ken Rockot314714c2017-11-05 23:36:24522 else
Takashi Toyoshimaaa278662017-11-20 11:11:26523 client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
Ken Rockot314714c2017-11-05 23:36:24524 client_.reset();
525 MaybeDeleteSelf();
526 }
527
528 std::unique_ptr<mojo::FileDataPipeProducer> data_producer_;
529 mojo::Binding<mojom::URLLoader> binding_;
530 mojom::URLLoaderClientPtr client_;
531
532 DISALLOW_COPY_AND_ASSIGN(FileURLLoader);
533};
534
535} // namespace
536
537FileURLLoaderFactory::FileURLLoaderFactory(
538 const base::FilePath& profile_path,
539 scoped_refptr<base::SequencedTaskRunner> task_runner)
540 : profile_path_(profile_path), task_runner_(std::move(task_runner)) {}
541
542FileURLLoaderFactory::~FileURLLoaderFactory() = default;
543
Ken Rockot314714c2017-11-05 23:36:24544void FileURLLoaderFactory::CreateLoaderAndStart(
545 mojom::URLLoaderRequest loader,
546 int32_t routing_id,
547 int32_t request_id,
548 uint32_t options,
549 const ResourceRequest& request,
550 mojom::URLLoaderClientPtr client,
551 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
552 base::FilePath file_path;
553 const bool is_file = net::FileURLToFilePath(request.url, &file_path);
554 if (is_file && file_path.EndsWithSeparator() && file_path.IsAbsolute()) {
555 task_runner_->PostTask(
556 FROM_HERE,
557 base::BindOnce(&FileURLDirectoryLoader::CreateAndStart, profile_path_,
558 request, std::move(loader), client.PassInterface()));
559 } else {
560 task_runner_->PostTask(
561 FROM_HERE,
562 base::BindOnce(&FileURLLoader::CreateAndStart, profile_path_, request,
Ken Rockot6414c4d92017-11-08 19:58:32563 std::move(loader), client.PassInterface(),
564 DirectoryLoadingPolicy::kRespondWithListing,
565 FileAccessPolicy::kRestricted,
566 LinkFollowingPolicy::kFollow));
Ken Rockot314714c2017-11-05 23:36:24567 }
568}
569
570void FileURLLoaderFactory::Clone(mojom::URLLoaderFactoryRequest loader) {
Ken Rockot6414c4d92017-11-08 19:58:32571 bindings_.AddBinding(this, std::move(loader));
572}
573
574void CreateFileURLLoader(const ResourceRequest& request,
575 mojom::URLLoaderRequest loader,
576 mojom::URLLoaderClientPtr client) {
577 auto task_runner = base::CreateSequencedTaskRunnerWithTraits(
578 {base::MayBlock(), base::TaskPriority::BACKGROUND,
579 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
580 task_runner->PostTask(
581 FROM_HERE,
582 base::BindOnce(&FileURLLoader::CreateAndStart, base::FilePath(), request,
583 std::move(loader), client.PassInterface(),
584 DirectoryLoadingPolicy::kFail,
585 FileAccessPolicy::kUnrestricted,
586 LinkFollowingPolicy::kDoNotFollow));
Ken Rockot314714c2017-11-05 23:36:24587}
588
589} // namespace content