summaryrefslogtreecommitdiffstats
path: root/webkit/fileapi
diff options
context:
space:
mode:
authoradamk@chromium.org <adamk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-08 03:29:35 +0000
committeradamk@chromium.org <adamk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-08 03:29:35 +0000
commit8fbe7994047c3ec60182b4b2540d825c95e9f6ef (patch)
treeaf6dfd9cb03fa40c22ffad2b8a739f425f6d8626 /webkit/fileapi
parent6aa800f9d7af07c758cacd2c7d0425c706e4f057 (diff)
downloadchromium_src-8fbe7994047c3ec60182b4b2540d825c95e9f6ef.zip
chromium_src-8fbe7994047c3ec60182b4b2540d825c95e9f6ef.tar.gz
chromium_src-8fbe7994047c3ec60182b4b2540d825c95e9f6ef.tar.bz2
First crack at FileSystemURLRequestJob for handling filesystem: URLs.
Disabled behind a switch, "--enable-filesystem-url-scheme". Review URL: http://codereview.chromium.org/6262015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74082 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/fileapi')
-rw-r--r--webkit/fileapi/file_system_dir_url_request_job.cc146
-rw-r--r--webkit/fileapi/file_system_dir_url_request_job.h65
-rw-r--r--webkit/fileapi/file_system_dir_url_request_job_unittest.cc169
-rw-r--r--webkit/fileapi/file_system_url_request_job.cc258
-rw-r--r--webkit/fileapi/file_system_url_request_job.h81
-rw-r--r--webkit/fileapi/file_system_url_request_job_unittest.cc276
-rw-r--r--webkit/fileapi/file_system_util.cc62
-rw-r--r--webkit/fileapi/file_system_util.h21
-rw-r--r--webkit/fileapi/file_system_util_unittest.cc75
-rw-r--r--webkit/fileapi/webkit_fileapi.gypi6
10 files changed, 1159 insertions, 0 deletions
diff --git a/webkit/fileapi/file_system_dir_url_request_job.cc b/webkit/fileapi/file_system_dir_url_request_job.cc
new file mode 100644
index 0000000..95ba462
--- /dev/null
+++ b/webkit/fileapi/file_system_dir_url_request_job.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/fileapi/file_system_dir_url_request_job.h"
+
+#include <algorithm>
+
+#include "base/compiler_specific.h"
+#include "base/file_util_proxy.h"
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "base/sys_string_conversions.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/url_request/url_request.h"
+#include "webkit/fileapi/file_system_path_manager.h"
+#include "webkit/fileapi/file_system_util.h"
+
+using net::URLRequest;
+using net::URLRequestJob;
+using net::URLRequestStatus;
+
+namespace fileapi {
+
+FileSystemDirURLRequestJob::FileSystemDirURLRequestJob(
+ URLRequest* request, FileSystemPathManager* path_manager,
+ scoped_refptr<base::MessageLoopProxy> file_thread_proxy)
+ : URLRequestJob(request),
+ path_manager_(path_manager),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)),
+ file_thread_proxy_(file_thread_proxy) {
+}
+
+FileSystemDirURLRequestJob::~FileSystemDirURLRequestJob() {
+}
+
+void FileSystemDirURLRequestJob::Start() {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &FileSystemDirURLRequestJob::StartAsync));
+}
+
+void FileSystemDirURLRequestJob::Kill() {
+ URLRequestJob::Kill();
+ callback_factory_.RevokeAll();
+}
+
+bool FileSystemDirURLRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size,
+ int *bytes_read) {
+
+ int count = std::min(dest_size, static_cast<int>(data_.size()));
+ if (count > 0) {
+ memcpy(dest->data(), data_.data(), count);
+ data_.erase(0, count);
+ }
+ *bytes_read = count;
+ return true;
+}
+
+bool FileSystemDirURLRequestJob::GetMimeType(std::string* mime_type) const {
+ *mime_type = "text/html";
+ return true;
+}
+
+bool FileSystemDirURLRequestJob::GetCharset(std::string* charset) {
+ *charset = "utf-8";
+ return true;
+}
+
+void FileSystemDirURLRequestJob::StartAsync() {
+ FileSystemType type;
+ if (!CrackFileSystemURL(request_->url(), &origin_url_, &type,
+ &relative_dir_path_)) {
+ NotifyFailed(net::ERR_INVALID_URL);
+ return;
+ }
+
+ path_manager_->GetFileSystemRootPath(
+ origin_url_, type, false, // create
+ callback_factory_.NewCallback(
+ &FileSystemDirURLRequestJob::DidGetRootPath));
+}
+
+void FileSystemDirURLRequestJob::DidGetRootPath(bool success,
+ const FilePath& root_path,
+ const std::string& name) {
+ if (!success) {
+ NotifyFailed(net::ERR_FILE_NOT_FOUND);
+ return;
+ }
+
+ absolute_dir_path_ = root_path.Append(relative_dir_path_);
+
+ // We assume it's a directory if we've gotten here: either the path
+ // ends with '/', or FileSystemDirURLRequestJob already statted it and
+ // found it to be a directory.
+ base::FileUtilProxy::ReadDirectory(file_thread_proxy_, absolute_dir_path_,
+ callback_factory_.NewCallback(
+ &FileSystemDirURLRequestJob::DidReadDirectory));
+}
+
+void FileSystemDirURLRequestJob::DidReadDirectory(
+ base::PlatformFileError error_code,
+ const std::vector<base::FileUtilProxy::Entry>& entries) {
+ if (error_code != base::PLATFORM_FILE_OK) {
+ NotifyFailed(error_code);
+ return;
+ }
+
+#if defined(OS_WIN)
+ const string16& title = absolute_dir_path_.value();
+#elif defined(OS_POSIX)
+ const string16& title = WideToUTF16(
+ base::SysNativeMBToWide(absolute_dir_path_.value()));
+#endif
+ data_.append(net::GetDirectoryListingHeader(title));
+
+ typedef std::vector<base::FileUtilProxy::Entry>::const_iterator EntryIterator;
+ for (EntryIterator it = entries.begin(); it != entries.end(); ++it) {
+#if defined(OS_WIN)
+ const string16& name = it->name;
+#elif defined(OS_POSIX)
+ const string16& name =
+ WideToUTF16(base::SysNativeMBToWide(it->name));
+#endif
+ // TODO(adamk): Add file size?
+ data_.append(net::GetDirectoryListingEntry(
+ name, std::string(), it->is_directory, 0, base::Time()));
+ }
+
+ set_expected_content_size(data_.size());
+ NotifyHeadersComplete();
+}
+
+void FileSystemDirURLRequestJob::NotifyFailed(int rv) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/file_system_dir_url_request_job.h b/webkit/fileapi/file_system_dir_url_request_job.h
new file mode 100644
index 0000000..e7b2184
--- /dev/null
+++ b/webkit/fileapi/file_system_dir_url_request_job.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_FILEAPI_FILE_SYSTEM_DIR_URL_REQUEST_JOB_H_
+#define WEBKIT_FILEAPI_FILE_SYSTEM_DIR_URL_REQUEST_JOB_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/file_util_proxy.h"
+#include "base/message_loop_proxy.h"
+#include "base/platform_file.h"
+#include "base/scoped_callback_factory.h"
+#include "base/task.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request_job.h"
+
+namespace fileapi {
+class FileSystemPathManager;
+
+// A request job that handles reading filesystem: URLs for directories.
+class FileSystemDirURLRequestJob : public net::URLRequestJob {
+ public:
+ FileSystemDirURLRequestJob(
+ net::URLRequest* request, FileSystemPathManager* path_manager,
+ scoped_refptr<base::MessageLoopProxy> file_thread_proxy);
+
+ // URLRequestJob methods:
+ virtual void Start();
+ virtual void Kill();
+ virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read);
+ virtual bool GetMimeType(std::string* mime_type) const;
+ virtual bool GetCharset(std::string* charset);
+ // TODO(adamk): Implement the rest of the methods required to simulate HTTP.
+
+ private:
+ virtual ~FileSystemDirURLRequestJob();
+
+ void StartAsync();
+ void DidGetRootPath(bool success, const FilePath& root_path,
+ const std::string& name);
+ void DidReadDirectory(base::PlatformFileError error_code,
+ const std::vector<base::FileUtilProxy::Entry>& entries);
+
+ void NotifyFailed(int rv);
+
+ std::string data_;
+ FilePath relative_dir_path_;
+ FilePath absolute_dir_path_;
+ GURL origin_url_;
+ FileSystemPathManager* const path_manager_;
+
+ ScopedRunnableMethodFactory<FileSystemDirURLRequestJob> method_factory_;
+ base::ScopedCallbackFactory<FileSystemDirURLRequestJob> callback_factory_;
+ scoped_refptr<base::MessageLoopProxy> file_thread_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileSystemDirURLRequestJob);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_FILEAPI_FILE_SYSTEM_DIR_URL_REQUEST_JOB_H_
diff --git a/webkit/fileapi/file_system_dir_url_request_job_unittest.cc b/webkit/fileapi/file_system_dir_url_request_job_unittest.cc
new file mode 100644
index 0000000..4d81513
--- /dev/null
+++ b/webkit/fileapi/file_system_dir_url_request_job_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// NOTE: These tests are run as part of "unit_tests" (in chrome/test/unit)
+// rather than as part of test_shell_tests because they rely on being able
+// to instantiate a MessageLoop of type TYPE_IO. test_shell_tests uses
+// TYPE_UI, which URLRequest doesn't allow.
+//
+
+#include "webkit/fileapi/file_system_dir_url_request_job.h"
+
+#include "build/build_config.h"
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/format_macros.h"
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "base/scoped_temp_dir.h"
+#include "base/string_piece.h"
+#include "base/threading/thread.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/http/http_request_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/fileapi/file_system_path_manager.h"
+
+namespace fileapi {
+namespace {
+
+// We always use the TEMPORARY FileSystem in this test.
+static const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/";
+
+class FileSystemDirURLRequestJobTest : public testing::Test {
+ protected:
+ FileSystemDirURLRequestJobTest()
+ : message_loop_(MessageLoop::TYPE_IO), // simulate an IO thread
+ ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ // We use the main thread so that we can get the root path synchronously.
+ // TODO(adamk): Run this on the FILE thread we've created as well.
+ path_manager_.reset(new FileSystemPathManager(
+ base::MessageLoopProxy::CreateForCurrentThread(),
+ temp_dir_.path(), false, false));
+
+ path_manager_->GetFileSystemRootPath(
+ GURL("http://remote/"), kFileSystemTypeTemporary, true, // create
+ callback_factory_.NewCallback(
+ &FileSystemDirURLRequestJobTest::OnGetRootPath));
+ MessageLoop::current()->RunAllPending();
+
+ file_thread_.reset(
+ new base::Thread("FileSystemDirURLRequestJobTest FILE Thread"));
+ base::Thread::Options options(MessageLoop::TYPE_IO, 0);
+ file_thread_->StartWithOptions(options);
+
+ net::URLRequest::RegisterProtocolFactory(
+ "filesystem", &FileSystemDirURLRequestJobFactory);
+ }
+
+ virtual void TearDown() {
+ // NOTE: order matters, request must die before delegate
+ request_.reset(NULL);
+ delegate_.reset(NULL);
+
+ file_thread_.reset(NULL);
+ net::URLRequest::RegisterProtocolFactory("filesystem", NULL);
+ }
+
+ void OnGetRootPath(bool success, const FilePath& root_path,
+ const std::string& name) {
+ ASSERT_TRUE(success);
+ root_path_ = root_path;
+ }
+
+ void TestRequest(const GURL& url) {
+ delegate_.reset(new TestDelegate());
+ delegate_->set_quit_on_redirect(true);
+ request_.reset(new net::URLRequest(url, delegate_.get()));
+ job_ = new FileSystemDirURLRequestJob(request_.get(), path_manager_.get(),
+ file_thread_->message_loop_proxy());
+
+ request_->Start();
+ ASSERT_TRUE(request_->is_pending()); // verify that we're starting async
+ MessageLoop::current()->Run();
+ }
+
+ void CreateDirectory(const base::StringPiece dir_name) {
+ FilePath path = root_path_.AppendASCII(dir_name);
+ ASSERT_TRUE(file_util::CreateDirectory(path));
+ }
+
+ GURL CreateFileSystemURL(const std::string path) {
+ return GURL(kFileSystemURLPrefix + path);
+ }
+
+ static net::URLRequestJob* FileSystemDirURLRequestJobFactory(
+ net::URLRequest* request,
+ const std::string& scheme) {
+ DCHECK(job_);
+ net::URLRequestJob* temp = job_;
+ job_ = NULL;
+ return temp;
+ }
+
+ ScopedTempDir temp_dir_;
+ FilePath root_path_;
+ scoped_ptr<net::URLRequest> request_;
+ scoped_ptr<TestDelegate> delegate_;
+ scoped_ptr<FileSystemPathManager> path_manager_;
+ scoped_ptr<base::Thread> file_thread_;
+
+ MessageLoop message_loop_;
+ base::ScopedCallbackFactory<FileSystemDirURLRequestJobTest> callback_factory_;
+
+ static net::URLRequestJob* job_;
+};
+
+// static
+net::URLRequestJob* FileSystemDirURLRequestJobTest::job_ = NULL;
+
+// TODO(adamk): Write tighter tests once we've decided on a format for directory
+// listing responses.
+TEST_F(FileSystemDirURLRequestJobTest, DirectoryListing) {
+ CreateDirectory("foo");
+ CreateDirectory("foo/bar");
+ CreateDirectory("foo/bar/baz");
+
+ TestRequest(CreateFileSystemURL("foo/bar/"));
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ EXPECT_GT(delegate_->bytes_received(), 0);
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, InvalidURL) {
+ TestRequest(GURL("filesystem:/foo/bar/baz"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(net::ERR_INVALID_URL, request_->status().os_error());
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, NoSuchRoot) {
+ TestRequest(GURL("filesystem:http://remote/persistent/somedir/"));
+ ASSERT_FALSE(request_->is_pending());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().os_error());
+}
+
+TEST_F(FileSystemDirURLRequestJobTest, NoSuchDirectory) {
+ TestRequest(CreateFileSystemURL("somedir/"));
+ ASSERT_FALSE(request_->is_pending());
+ ASSERT_FALSE(request_->status().is_success());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, request_->status().os_error());
+}
+
+} // namespace (anonymous)
+} // namespace fileapi
diff --git a/webkit/fileapi/file_system_url_request_job.cc b/webkit/fileapi/file_system_url_request_job.cc
new file mode 100644
index 0000000..c8b1c76
--- /dev/null
+++ b/webkit/fileapi/file_system_url_request_job.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/fileapi/file_system_url_request_job.h"
+
+#include "base/compiler_specific.h"
+#include "base/file_util_proxy.h"
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/file_stream.h"
+#include "net/base/io_buffer.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request.h"
+#include "webkit/fileapi/file_system_path_manager.h"
+#include "webkit/fileapi/file_system_util.h"
+
+using net::URLRequest;
+using net::URLRequestJob;
+using net::URLRequestStatus;
+
+namespace fileapi {
+
+static const int kFileFlags = base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_ASYNC;
+
+FileSystemURLRequestJob::FileSystemURLRequestJob(
+ URLRequest* request, FileSystemPathManager* path_manager,
+ scoped_refptr<base::MessageLoopProxy> file_thread_proxy)
+ : URLRequestJob(request),
+ path_manager_(path_manager),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ io_callback_(this, &FileSystemURLRequestJob::DidRead)),
+ stream_(NULL),
+ is_directory_(false),
+ remaining_bytes_(0),
+ startup_error_(0),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)),
+ file_thread_proxy_(file_thread_proxy) {
+}
+
+FileSystemURLRequestJob::~FileSystemURLRequestJob() {
+ // Since we use the two-arg constructor of FileStream, we need to call Close()
+ // manually: ~FileStream won't call it for us.
+ if (stream_ != NULL)
+ stream_->Close();
+}
+
+void FileSystemURLRequestJob::Start() {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &FileSystemURLRequestJob::StartAsync));
+}
+
+void FileSystemURLRequestJob::Kill() {
+ if (stream_ != NULL) {
+ stream_->Close();
+ stream_.reset(NULL);
+ }
+
+ URLRequestJob::Kill();
+ callback_factory_.RevokeAll();
+}
+
+bool FileSystemURLRequestJob::ReadRawData(net::IOBuffer* dest, int dest_size,
+ int *bytes_read) {
+ DCHECK_NE(dest_size, 0);
+ DCHECK(bytes_read);
+ DCHECK_GE(remaining_bytes_, 0);
+
+ if (stream_ == NULL)
+ return false;
+
+ if (remaining_bytes_ < dest_size)
+ dest_size = static_cast<int>(remaining_bytes_);
+
+ if (!dest_size) {
+ *bytes_read = 0;
+ return true;
+ }
+
+ int rv = stream_->Read(dest->data(), dest_size, &io_callback_);
+ if (rv >= 0) {
+ // Data is immediately available.
+ *bytes_read = rv;
+ remaining_bytes_ -= rv;
+ DCHECK_GE(remaining_bytes_, 0);
+ return true;
+ }
+
+ // Otherwise, a read error occured. We may just need to wait...
+ if (rv == net::ERR_IO_PENDING)
+ SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+ else
+ NotifyFailed(rv);
+ return false;
+}
+
+bool FileSystemURLRequestJob::GetMimeType(std::string* mime_type) const {
+ // URL requests should not block on the disk! On Windows this goes to the
+ // registry.
+ // http://code.google.com/p/chromium/issues/detail?id=59849
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ DCHECK(request_);
+ return net::GetMimeTypeFromFile(absolute_file_path_, mime_type);
+}
+
+void FileSystemURLRequestJob::SetExtraRequestHeaders(
+ const net::HttpRequestHeaders& headers) {
+ std::string range_header;
+ if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
+ std::vector<net::HttpByteRange> ranges;
+ if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
+ if (ranges.size() == 1)
+ byte_range_ = ranges[0];
+ else {
+ // We don't support multiple range requests in one single URL request.
+ // TODO(adamk): decide whether we want to support multiple range
+ // requests.
+ startup_error_ = net::ERR_REQUEST_RANGE_NOT_SATISFIABLE;
+ }
+ }
+ }
+}
+
+void FileSystemURLRequestJob::StartAsync() {
+ if (startup_error_) {
+ NotifyFailed(startup_error_);
+ return;
+ }
+
+ FileSystemType type;
+ if (!CrackFileSystemURL(request_->url(), &origin_url_, &type,
+ &relative_file_path_)) {
+ NotifyFailed(net::ERR_INVALID_URL);
+ return;
+ }
+
+ path_manager_->GetFileSystemRootPath(
+ origin_url_, type, false, // create
+ callback_factory_.NewCallback(&FileSystemURLRequestJob::DidGetRootPath));
+}
+
+void FileSystemURLRequestJob::DidGetRootPath(bool success,
+ const FilePath& root_path,
+ const std::string& name) {
+ if (!success) {
+ NotifyFailed(net::ERR_FILE_NOT_FOUND);
+ return;
+ }
+
+ absolute_file_path_ = root_path.Append(relative_file_path_);
+
+ base::FileUtilProxy::GetFileInfo(file_thread_proxy_, absolute_file_path_,
+ callback_factory_.NewCallback(&FileSystemURLRequestJob::DidResolve));
+}
+
+void FileSystemURLRequestJob::DidResolve(base::PlatformFileError error_code,
+ const base::PlatformFileInfo& file_info) {
+ // We may have been orphaned...
+ if (!request_)
+ return;
+
+ // We use FileSystemURLRequestJob to handle files as well as directories
+ // without trailing slash.
+ // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
+ // we will append trailing slash and redirect to FileDirJob.
+ if (error_code != base::PLATFORM_FILE_OK) {
+ NotifyFailed(error_code);
+ return;
+ }
+
+ is_directory_ = file_info.is_directory;
+
+ if (!byte_range_.ComputeBounds(file_info.size)) {
+ NotifyFailed(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
+ return;
+ }
+
+ if (!is_directory_) {
+ base::FileUtilProxy::CreateOrOpen(
+ file_thread_proxy_, absolute_file_path_, kFileFlags,
+ callback_factory_.NewCallback(&FileSystemURLRequestJob::DidOpen));
+ } else
+ NotifyHeadersComplete();
+}
+
+void FileSystemURLRequestJob::DidOpen(base::PlatformFileError error_code,
+ base::PassPlatformFile file,
+ bool created) {
+ if (error_code != base::PLATFORM_FILE_OK) {
+ NotifyFailed(error_code);
+ return;
+ }
+
+ stream_.reset(new net::FileStream(file.ReleaseValue(), kFileFlags));
+
+ remaining_bytes_ = byte_range_.last_byte_position() -
+ byte_range_.first_byte_position() + 1;
+ DCHECK_GE(remaining_bytes_, 0);
+
+ // Do the seek at the beginning of the request.
+ if (remaining_bytes_ > 0 &&
+ byte_range_.first_byte_position() != 0 &&
+ byte_range_.first_byte_position() !=
+ stream_->Seek(net::FROM_BEGIN, byte_range_.first_byte_position())) {
+ NotifyFailed(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
+ return;
+ }
+
+ set_expected_content_size(remaining_bytes_);
+ NotifyHeadersComplete();
+}
+
+void FileSystemURLRequestJob::DidRead(int result) {
+ if (result > 0)
+ SetStatus(URLRequestStatus()); // Clear the IO_PENDING status
+ else if (result == 0)
+ NotifyDone(URLRequestStatus());
+ else
+ NotifyFailed(result);
+
+ remaining_bytes_ -= result;
+ DCHECK_GE(remaining_bytes_, 0);
+
+ NotifyReadComplete(result);
+}
+
+bool FileSystemURLRequestJob::IsRedirectResponse(GURL* location,
+ int* http_status_code) {
+ if (is_directory_) {
+ // This happens when we discovered the file is a directory, so needs a
+ // slash at the end of the path.
+ std::string new_path = request_->url().path();
+ new_path.push_back('/');
+ GURL::Replacements replacements;
+ replacements.SetPathStr(new_path);
+ *location = request_->url().ReplaceComponents(replacements);
+ *http_status_code = 301; // simulate a permanent redirect
+ return true;
+ }
+
+ return false;
+}
+
+void FileSystemURLRequestJob::NotifyFailed(int rv) {
+ NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/file_system_url_request_job.h b/webkit/fileapi/file_system_url_request_job.h
new file mode 100644
index 0000000..c49d873
--- /dev/null
+++ b/webkit/fileapi/file_system_url_request_job.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_FILEAPI_FILE_SYSTEM_URL_REQUEST_JOB_H_
+#define WEBKIT_FILEAPI_FILE_SYSTEM_URL_REQUEST_JOB_H_
+#pragma once
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/message_loop_proxy.h"
+#include "base/platform_file.h"
+#include "base/scoped_callback_factory.h"
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/completion_callback.h"
+#include "net/http/http_byte_range.h"
+#include "net/url_request/url_request_job.h"
+
+namespace net {
+class FileStream;
+}
+
+namespace fileapi {
+class FileSystemPathManager;
+
+// A request job that handles reading filesystem: URLs
+class FileSystemURLRequestJob : public net::URLRequestJob {
+ public:
+ FileSystemURLRequestJob(
+ net::URLRequest* request, FileSystemPathManager* path_manager,
+ scoped_refptr<base::MessageLoopProxy> file_thread_proxy);
+
+ // URLRequestJob methods:
+ virtual void Start();
+ virtual void Kill();
+ virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read);
+ virtual bool IsRedirectResponse(GURL* location, int* http_status_code);
+ virtual bool GetMimeType(std::string* mime_type) const;
+ virtual void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers);
+ // TODO(adamk): Implement the rest of the methods required to simulate HTTP.
+
+ private:
+ virtual ~FileSystemURLRequestJob();
+
+ void StartAsync();
+ void DidGetRootPath(bool success, const FilePath& root_path,
+ const std::string& name);
+ void DidResolve(base::PlatformFileError error_code,
+ const base::PlatformFileInfo& file_info);
+ void DidOpen(base::PlatformFileError error_code,
+ base::PassPlatformFile file, bool created);
+ void DidRead(int result);
+
+ void NotifyFailed(int rv);
+
+ FilePath relative_file_path_;
+ FilePath absolute_file_path_;
+ GURL origin_url_;
+ FileSystemPathManager* const path_manager_;
+
+ net::CompletionCallbackImpl<FileSystemURLRequestJob> io_callback_;
+ scoped_ptr<net::FileStream> stream_;
+ bool is_directory_;
+
+ net::HttpByteRange byte_range_;
+ int64 remaining_bytes_;
+ int startup_error_;
+
+ ScopedRunnableMethodFactory<FileSystemURLRequestJob> method_factory_;
+ base::ScopedCallbackFactory<FileSystemURLRequestJob> callback_factory_;
+ scoped_refptr<base::MessageLoopProxy> file_thread_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileSystemURLRequestJob);
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_FILEAPI_FILE_SYSTEM_URL_REQUEST_JOB_H_
diff --git a/webkit/fileapi/file_system_url_request_job_unittest.cc b/webkit/fileapi/file_system_url_request_job_unittest.cc
new file mode 100644
index 0000000..3e76338
--- /dev/null
+++ b/webkit/fileapi/file_system_url_request_job_unittest.cc
@@ -0,0 +1,276 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// NOTE: These tests are run as part of "unit_tests" (in chrome/test/unit)
+// rather than as part of test_shell_tests because they rely on being able
+// to instantiate a MessageLoop of type TYPE_IO. test_shell_tests uses
+// TYPE_UI, which URLRequest doesn't allow.
+//
+
+#include "webkit/fileapi/file_system_url_request_job.h"
+
+#include "build/build_config.h"
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/format_macros.h"
+#include "base/message_loop.h"
+#include "base/platform_file.h"
+#include "base/scoped_temp_dir.h"
+#include "base/string_piece.h"
+#include "base/stringprintf.h"
+#include "base/threading/thread.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/http/http_request_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/fileapi/file_system_path_manager.h"
+
+namespace fileapi {
+namespace {
+
+// We always use the TEMPORARY FileSystem in this test.
+const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/";
+const char kTestFileData[] = "0123456789";
+
+void FillBuffer(char* buffer, size_t len) {
+ static bool called = false;
+ if (!called) {
+ called = true;
+ int seed = static_cast<int>(base::Time::Now().ToInternalValue());
+ srand(seed);
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ buffer[i] = static_cast<char>(rand());
+ if (!buffer[i])
+ buffer[i] = 'g';
+ }
+}
+
+class FileSystemURLRequestJobTest : public testing::Test {
+ protected:
+ FileSystemURLRequestJobTest()
+ : message_loop_(MessageLoop::TYPE_IO), // simulate an IO thread
+ ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)) {
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ // We use the main thread so that we can get the root path synchronously.
+ // TODO(adamk): Run this on the FILE thread we've created as well.
+ path_manager_.reset(new FileSystemPathManager(
+ base::MessageLoopProxy::CreateForCurrentThread(),
+ temp_dir_.path(), false, false));
+
+ path_manager_->GetFileSystemRootPath(
+ GURL("http://remote/"), kFileSystemTypeTemporary, true, // create
+ callback_factory_.NewCallback(
+ &FileSystemURLRequestJobTest::OnGetRootPath));
+ MessageLoop::current()->RunAllPending();
+
+ file_thread_.reset(
+ new base::Thread("FileSystemURLRequestJobTest FILE Thread"));
+ base::Thread::Options options(MessageLoop::TYPE_IO, 0);
+ file_thread_->StartWithOptions(options);
+
+ net::URLRequest::RegisterProtocolFactory("filesystem",
+ &FileSystemURLRequestJobFactory);
+ }
+
+ virtual void TearDown() {
+ // NOTE: order matters, request must die before delegate
+ request_.reset(NULL);
+ delegate_.reset(NULL);
+
+ file_thread_.reset(NULL);
+ net::URLRequest::RegisterProtocolFactory("filesystem", NULL);
+ }
+
+ void OnGetRootPath(bool success, const FilePath& root_path,
+ const std::string& name) {
+ ASSERT_TRUE(success);
+ origin_root_path_ = root_path;
+ }
+
+ void TestRequest(const GURL& url) {
+ TestRequestWithHeaders(url, NULL);
+ }
+
+ void TestRequestWithHeaders(const GURL& url,
+ const net::HttpRequestHeaders* headers) {
+ delegate_.reset(new TestDelegate());
+ // Make delegate_ exit the MessageLoop when the request is done.
+ delegate_->set_quit_on_complete(true);
+ delegate_->set_quit_on_redirect(true);
+ request_.reset(new net::URLRequest(url, delegate_.get()));
+ if (headers)
+ request_->SetExtraRequestHeaders(*headers);
+ job_ = new FileSystemURLRequestJob(request_.get(), path_manager_.get(),
+ file_thread_->message_loop_proxy());
+
+ request_->Start();
+ ASSERT_TRUE(request_->is_pending()); // verify that we're starting async
+ MessageLoop::current()->Run();
+ }
+
+ void WriteFile(const base::StringPiece file_name,
+ const char* buf, int buf_size) {
+ FilePath path = origin_root_path_.AppendASCII(file_name);
+ ASSERT_EQ(buf_size, file_util::WriteFile(path, buf, buf_size));
+ }
+
+ GURL CreateFileSystemURL(const std::string& path) {
+ return GURL(kFileSystemURLPrefix + path);
+ }
+
+ static net::URLRequestJob* FileSystemURLRequestJobFactory(
+ net::URLRequest* request,
+ const std::string& scheme) {
+ DCHECK(job_);
+ net::URLRequestJob* temp = job_;
+ job_ = NULL;
+ return temp;
+ }
+
+ ScopedTempDir temp_dir_;
+ FilePath origin_root_path_;
+ scoped_ptr<net::URLRequest> request_;
+ scoped_ptr<TestDelegate> delegate_;
+ scoped_ptr<FileSystemPathManager> path_manager_;
+ scoped_ptr<base::Thread> file_thread_;
+
+ MessageLoop message_loop_;
+ base::ScopedCallbackFactory<FileSystemURLRequestJobTest> callback_factory_;
+
+ static net::URLRequestJob* job_;
+};
+
+// static
+net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL;
+
+TEST_F(FileSystemURLRequestJobTest, FileTest) {
+ WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
+ TestRequest(CreateFileSystemURL("file1.dat"));
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ EXPECT_EQ(kTestFileData, delegate_->data_received());
+}
+
+TEST_F(FileSystemURLRequestJobTest, FileTestFullSpecifiedRange) {
+ const size_t buffer_size = 4000;
+ scoped_array<char> buffer(new char[buffer_size]);
+ FillBuffer(buffer.get(), buffer_size);
+ WriteFile("bigfile", buffer.get(), buffer_size);
+
+ const size_t first_byte_position = 500;
+ const size_t last_byte_position = buffer_size - first_byte_position;
+ std::string partial_buffer_string(buffer.get() + first_byte_position,
+ buffer.get() + last_byte_position + 1);
+
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(net::HttpRequestHeaders::kRange,
+ base::StringPrintf(
+ "bytes=%" PRIuS "-%" PRIuS,
+ first_byte_position, last_byte_position));
+ TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
+}
+
+TEST_F(FileSystemURLRequestJobTest, FileTestHalfSpecifiedRange) {
+ const size_t buffer_size = 4000;
+ scoped_array<char> buffer(new char[buffer_size]);
+ FillBuffer(buffer.get(), buffer_size);
+ WriteFile("bigfile", buffer.get(), buffer_size);
+
+ const size_t first_byte_position = 500;
+ std::string partial_buffer_string(buffer.get() + first_byte_position,
+ buffer.get() + buffer_size);
+
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(net::HttpRequestHeaders::kRange,
+ base::StringPrintf("bytes=%" PRIuS "-",
+ first_byte_position));
+ TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_EQ(1, delegate_->response_started_count());
+ EXPECT_FALSE(delegate_->received_data_before_response());
+ // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed.
+ EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
+}
+
+
+TEST_F(FileSystemURLRequestJobTest, FileTestMultipleRangesNotSupported) {
+ WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(net::HttpRequestHeaders::kRange,
+ "bytes=0-5,10-200,200-300");
+ TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
+ request_->status().os_error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, RangeOutOfBounds) {
+ WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
+ net::HttpRequestHeaders headers;
+ headers.SetHeader(net::HttpRequestHeaders::kRange, "bytes=500-1000");
+ TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);
+
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
+ request_->status().os_error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, FileDirRedirect) {
+ ASSERT_TRUE(file_util::CreateDirectory(origin_root_path_.AppendASCII("dir")));
+ TestRequest(CreateFileSystemURL("dir"));
+
+ EXPECT_EQ(1, delegate_->received_redirect_count());
+ EXPECT_TRUE(request_->status().is_success());
+ EXPECT_FALSE(delegate_->request_failed());
+
+ // We've deferred the redirect; now cancel the request to avoid following it.
+ request_->Cancel();
+ MessageLoop::current()->Run();
+}
+
+TEST_F(FileSystemURLRequestJobTest, InvalidURL) {
+ TestRequest(GURL("filesystem:/foo/bar/baz"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_INVALID_URL, request_->status().os_error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, NoSuchRoot) {
+ TestRequest(GURL("filesystem:http://remote/persistent/somefile"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().os_error());
+}
+
+TEST_F(FileSystemURLRequestJobTest, NoSuchFile) {
+ TestRequest(CreateFileSystemURL("somefile"));
+ ASSERT_FALSE(request_->is_pending());
+ EXPECT_TRUE(delegate_->request_failed());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, request_->status().os_error());
+}
+
+} // namespace (anonymous)
+} // namespace fileapi
diff --git a/webkit/fileapi/file_system_util.cc b/webkit/fileapi/file_system_util.cc
new file mode 100644
index 0000000..07aa5a6
--- /dev/null
+++ b/webkit/fileapi/file_system_util.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/fileapi/file_system_util.h"
+
+#include "build/build_config.h"
+
+#include "base/file_path.h"
+#include "base/sys_string_conversions.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/escape.h"
+#include "webkit/fileapi/file_system_types.h"
+
+namespace fileapi {
+
+static const char kPersistentDir[] = "/persistent/";
+static const char kTemporaryDir[] = "/temporary/";
+
+bool CrackFileSystemURL(const GURL& url, GURL* origin_url, FileSystemType* type,
+ FilePath* file_path) {
+ *origin_url = GURL();
+ *type = kFileSystemTypeUnknown;
+ *file_path = FilePath();
+
+ if (url.scheme() != "filesystem")
+ return false;
+
+ GURL bare_url(url.path());
+ *origin_url = bare_url.GetOrigin();
+
+ // The input URL was malformed, bail out early.
+ if (origin_url->is_empty() || bare_url.path().empty())
+ return false;
+
+ std::string path = UnescapeURLComponent(bare_url.path(),
+ UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
+ if (path.compare(0, strlen(kPersistentDir), kPersistentDir) == 0) {
+ *type = kFileSystemTypePersistent;
+ path = path.substr(strlen(kPersistentDir));
+ } else if (path.compare(0, strlen(kTemporaryDir), kTemporaryDir) == 0) {
+ *type = kFileSystemTypeTemporary;
+ path = path.substr(strlen(kTemporaryDir));
+ } else {
+ return false;
+ }
+
+ // Ensure the path is relative.
+ while (!path.empty() && path[0] == '/')
+ path.erase(0, 1);
+
+#if defined(OS_WIN)
+ const FilePath::StringType& sys_path = base::SysUTF8ToWide(path);
+#elif defined(OS_POSIX)
+ const FilePath::StringType& sys_path = path;
+#endif
+
+ *file_path = FilePath(sys_path);
+ return true;
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/file_system_util.h b/webkit/fileapi/file_system_util.h
new file mode 100644
index 0000000..4331b1a
--- /dev/null
+++ b/webkit/fileapi/file_system_util.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_FILEAPI_FILE_SYSTEM_UTIL_H_
+#define WEBKIT_FILEAPI_FILE_SYSTEM_UTIL_H_
+#pragma once
+
+#include "webkit/fileapi/file_system_types.h"
+
+class FilePath;
+class GURL;
+
+namespace fileapi {
+
+bool CrackFileSystemURL(const GURL& url, GURL* origin_url, FileSystemType* type,
+ FilePath* file_path);
+
+} // namespace fileapi
+
+#endif // WEBKIT_FILEAPI_FILE_SYSTEM_UTIL_H_
diff --git a/webkit/fileapi/file_system_util_unittest.cc b/webkit/fileapi/file_system_util_unittest.cc
new file mode 100644
index 0000000..7cb4c59
--- /dev/null
+++ b/webkit/fileapi/file_system_util_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/fileapi/file_system_util.h"
+
+#include "base/file_path.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/fileapi/file_system_types.h"
+
+namespace fileapi {
+namespace {
+
+class FileSystemUtilTest : public testing::Test {
+ protected:
+ bool CrackFileSystemURL(const char* url) {
+ return fileapi::CrackFileSystemURL(
+ GURL(url), &origin_url_, &type_, &file_path_);
+ }
+
+ GURL origin_url_;
+ FileSystemType type_;
+ FilePath file_path_;
+};
+
+TEST_F(FileSystemUtilTest, ParsePersistent) {
+ ASSERT_TRUE(CrackFileSystemURL(
+ "filesystem:http://chromium.org/persistent/directory/file"));
+ EXPECT_EQ("http://chromium.org/", origin_url_.spec());
+ EXPECT_EQ(kFileSystemTypePersistent, type_);
+ EXPECT_EQ(FILE_PATH_LITERAL("directory/file"), file_path_.value());
+}
+
+TEST_F(FileSystemUtilTest, ParseTemporary) {
+ ASSERT_TRUE(CrackFileSystemURL(
+ "filesystem:http://chromium.org/temporary/directory/file"));
+ EXPECT_EQ("http://chromium.org/", origin_url_.spec());
+ EXPECT_EQ(kFileSystemTypeTemporary, type_);
+ EXPECT_EQ(FILE_PATH_LITERAL("directory/file"), file_path_.value());
+}
+
+TEST_F(FileSystemUtilTest, EnsureFilePathIsRelative) {
+ ASSERT_TRUE(CrackFileSystemURL(
+ "filesystem:http://chromium.org/temporary/////directory/file"));
+ EXPECT_EQ("http://chromium.org/", origin_url_.spec());
+ EXPECT_EQ(kFileSystemTypeTemporary, type_);
+ EXPECT_EQ(FILE_PATH_LITERAL("directory/file"), file_path_.value());
+ EXPECT_FALSE(file_path_.IsAbsolute());
+}
+
+TEST_F(FileSystemUtilTest, RejectBadSchemes) {
+ EXPECT_FALSE(CrackFileSystemURL("http://chromium.org/"));
+ EXPECT_FALSE(CrackFileSystemURL("https://chromium.org/"));
+ EXPECT_FALSE(CrackFileSystemURL("file:///foo/bar"));
+ EXPECT_FALSE(CrackFileSystemURL("foobar:///foo/bar"));
+}
+
+TEST_F(FileSystemUtilTest, UnescapePath) {
+ ASSERT_TRUE(CrackFileSystemURL(
+ "filesystem:http://chromium.org/persistent/%7Echromium/space%20bar"));
+ EXPECT_EQ(FILE_PATH_LITERAL("~chromium/space bar"), file_path_.value());
+}
+
+TEST_F(FileSystemUtilTest, RejectBadType) {
+ EXPECT_FALSE(CrackFileSystemURL("filesystem:http://c.org/foobar/file"));
+}
+
+TEST_F(FileSystemUtilTest, RejectMalformedURL) {
+ EXPECT_FALSE(CrackFileSystemURL("filesystem:///foobar/file"));
+ EXPECT_FALSE(CrackFileSystemURL("filesystem:foobar/file"));
+}
+
+} // namespace (anonymous)
+} // namespace fileapi
diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi
index e380a2c..f5f8675 100644
--- a/webkit/fileapi/webkit_fileapi.gypi
+++ b/webkit/fileapi/webkit_fileapi.gypi
@@ -15,6 +15,8 @@
],
'sources': [
'file_system_callback_dispatcher.h',
+ 'file_system_dir_url_request_job.cc',
+ 'file_system_dir_url_request_job.h',
'file_system_operation.cc',
'file_system_operation.h',
'file_system_path_manager.cc',
@@ -22,6 +24,10 @@
'file_system_quota_manager.cc',
'file_system_quota_manager.h',
'file_system_types.h',
+ 'file_system_url_request_job.cc',
+ 'file_system_url_request_job.h',
+ 'file_system_util.cc',
+ 'file_system_util.h',
'file_writer_delegate.cc',
'file_writer_delegate.h',
'sandboxed_file_system_context.cc',