summaryrefslogtreecommitdiffstats
path: root/webkit
diff options
context:
space:
mode:
authorkinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-11 17:28:40 +0000
committerkinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-11 17:28:40 +0000
commit5b47ac32b7c1a6b0fd95b0622163fc4546eee3c1 (patch)
tree04c69466437d9954cca0a07b249560af4647e2bd /webkit
parent08b274f76c1474ece6128e55a30e1eba49fe37cf (diff)
downloadchromium_src-5b47ac32b7c1a6b0fd95b0622163fc4546eee3c1.zip
chromium_src-5b47ac32b7c1a6b0fd95b0622163fc4546eee3c1.tar.gz
chromium_src-5b47ac32b7c1a6b0fd95b0622163fc4546eee3c1.tar.bz2
2nd try: separate FileStream related code out of BlobURLRequest
The original change I had tried to submit was: http://codereview.chromium.org/9651032 The change was reverted because FileStream cannot be created on other threads than on IO thread on Windows, and therefore broke the ExtensionPageCaptureApiTest.SaveAsMHTML browser test. BUG=114999 TEST=test_shell_tests:BlobURLRequestJobTest.*, browser_tests:ExtensionPageCaptureApiTest.SaveAsMHTML Review URL: http://codereview.chromium.org/10029008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@131791 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r--webkit/blob/blob_url_request_job.cc166
-rw-r--r--webkit/blob/blob_url_request_job.h25
-rw-r--r--webkit/blob/blob_url_request_job_unittest.cc10
-rw-r--r--webkit/blob/local_file_reader.cc216
-rw-r--r--webkit/blob/local_file_reader.h83
-rw-r--r--webkit/blob/webkit_blob.gypi4
6 files changed, 381 insertions, 123 deletions
diff --git a/webkit/blob/blob_url_request_job.cc b/webkit/blob/blob_url_request_job.cc
index d6be8a5..1a360f1 100644
--- a/webkit/blob/blob_url_request_job.cc
+++ b/webkit/blob/blob_url_request_job.cc
@@ -6,14 +6,11 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
-#include "base/file_path.h"
-#include "base/file_util.h"
#include "base/file_util_proxy.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
+#include "base/stl_util.h"
#include "base/string_number_conversions.h"
-#include "base/threading/thread_restrictions.h"
-#include "net/base/file_stream.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_headers.h"
@@ -23,6 +20,7 @@
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_status.h"
+#include "webkit/blob/local_file_reader.h"
namespace webkit_blob {
@@ -71,9 +69,7 @@ BlobURLRequestJob::BlobURLRequestJob(
}
BlobURLRequestJob::~BlobURLRequestJob() {
- // FileStream's destructor won't close it for us because we passed in our own
- // file handle.
- CloseFileStream();
+ STLDeleteValues(&index_to_reader_);
}
void BlobURLRequestJob::Start() {
@@ -84,7 +80,7 @@ void BlobURLRequestJob::Start() {
}
void BlobURLRequestJob::Kill() {
- CloseFileStream();
+ DeleteCurrentFileReader();
net::URLRequestJob::Kill();
weak_factory_.InvalidateWeakPtrs();
@@ -185,9 +181,8 @@ void BlobURLRequestJob::CountSize() {
const BlobData::Item& item = blob_data_->items().at(i);
if (item.type == BlobData::TYPE_FILE) {
++pending_get_file_info_count_;
- base::FileUtilProxy::GetFileInfo(
- file_thread_proxy_, item.file_path,
- base::Bind(&BlobURLRequestJob::DidGetFileItemInfo,
+ GetFileReader(i)->GetLength(
+ base::Bind(&BlobURLRequestJob::DidGetFileItemLength,
weak_factory_.GetWeakPtr(), i));
continue;
}
@@ -227,41 +222,28 @@ void BlobURLRequestJob::DidCountSize(int error) {
NotifySuccess();
}
-void BlobURLRequestJob::DidGetFileItemInfo(
- size_t index,
- base::PlatformFileError rv,
- const base::PlatformFileInfo& file_info) {
+void BlobURLRequestJob::DidGetFileItemLength(size_t index, int result) {
// Do nothing if we have encountered an error.
if (error_)
return;
- if (rv == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
+ if (result == net::ERR_UPLOAD_FILE_CHANGED) {
NotifyFailure(net::ERR_FILE_NOT_FOUND);
return;
- } else if (rv != base::PLATFORM_FILE_OK) {
- NotifyFailure(net::ERR_FAILED);
+ } else if (result < 0) {
+ NotifyFailure(result);
return;
}
- // Validate the expected modification time.
- // Note that the expected modification time from WebKit is based on
- // time_t precision. So we have to convert both to time_t to compare.
DCHECK_LT(index, blob_data_->items().size());
const BlobData::Item& item = blob_data_->items().at(index);
DCHECK(item.type == BlobData::TYPE_FILE);
- if (!item.expected_modification_time.is_null() &&
- item.expected_modification_time.ToTimeT() !=
- file_info.last_modified.ToTimeT()) {
- NotifyFailure(net::ERR_FILE_NOT_FOUND);
- return;
- }
-
// If item length is -1, we need to use the file size being resolved
// in the real time.
int64 item_length = static_cast<int64>(item.length);
if (item_length == -1)
- item_length = file_info.size;
+ item_length = result;
// Cache the size and add it to the total size.
DCHECK_LT(index, item_length_list_.size());
@@ -283,6 +265,20 @@ void BlobURLRequestJob::Seek(int64 offset) {
// Set the offset that need to jump to for the first item in the range.
current_item_offset_ = offset;
+
+ if (offset == 0)
+ return;
+
+ // Adjust the offset of the first stream if it is of file type.
+ const BlobData::Item& item = blob_data_->items().at(current_item_index_);
+ if (item.type == BlobData::TYPE_FILE) {
+ DeleteCurrentFileReader();
+ index_to_reader_[current_item_index_] = new LocalFileReader(
+ file_thread_proxy_,
+ item.file_path,
+ item.offset + offset,
+ item.expected_modification_time);
+ }
}
bool BlobURLRequestJob::ReadItem() {
@@ -312,7 +308,7 @@ bool BlobURLRequestJob::ReadItem() {
case BlobData::TYPE_DATA:
return ReadBytesItem(item, bytes_to_read);
case BlobData::TYPE_FILE:
- return ReadFileItem(item, bytes_to_read);
+ return ReadFileItem(GetFileReader(current_item_index_), bytes_to_read);
default:
DCHECK(false);
return false;
@@ -320,8 +316,8 @@ bool BlobURLRequestJob::ReadItem() {
}
void BlobURLRequestJob::AdvanceItem() {
- // Close the stream if the current item is a file.
- CloseFileStream();
+ // Close the file if the current item is a file.
+ DeleteCurrentFileReader();
// Advance to the next item.
current_item_index_++;
@@ -357,79 +353,24 @@ bool BlobURLRequestJob::ReadBytesItem(const BlobData::Item& item,
return true;
}
-bool BlobURLRequestJob::ReadFileItem(const BlobData::Item& item,
+bool BlobURLRequestJob::ReadFileItem(LocalFileReader* reader,
int bytes_to_read) {
- // If the stream already exists, keep reading from it.
- if (stream_ != NULL)
- return ReadFileStream(bytes_to_read);
-
- base::FileUtilProxy::CreateOrOpen(
- file_thread_proxy_, item.file_path, kFileOpenFlags,
- base::Bind(&BlobURLRequestJob::DidOpenFile,
- weak_factory_.GetWeakPtr(), bytes_to_read));
- SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
- return false;
-}
-
-void BlobURLRequestJob::DidOpenFile(int bytes_to_read,
- base::PlatformFileError rv,
- base::PassPlatformFile file,
- bool created) {
- if (rv != base::PLATFORM_FILE_OK) {
- NotifyFailure(net::ERR_FAILED);
- return;
- }
-
- DCHECK(!stream_.get());
- stream_.reset(new net::FileStream(file.ReleaseValue(), kFileOpenFlags, NULL));
-
- const BlobData::Item& item = blob_data_->items().at(current_item_index_);
- {
- // stream_.SeekSync() blocks the IO thread, see http://crbug.com/75548.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- int64 offset = current_item_offset_ + static_cast<int64>(item.offset);
- if (offset > 0 && offset != stream_->SeekSync(net::FROM_BEGIN, offset)) {
- NotifyFailure(net::ERR_FAILED);
- return;
- }
- }
-
- ReadFileStream(bytes_to_read);
-}
-
-bool BlobURLRequestJob::ReadFileStream(int bytes_to_read) {
- DCHECK(stream_.get());
- DCHECK(stream_->IsOpen());
DCHECK_GE(read_buf_->BytesRemaining(), bytes_to_read);
-
- // Start the asynchronous reading.
- int rv = stream_->Read(read_buf_,
- bytes_to_read,
- base::Bind(&BlobURLRequestJob::DidReadFileStream,
- base::Unretained(this)));
-
- // If I/O pending error is returned, we just need to wait.
- if (rv == net::ERR_IO_PENDING) {
- SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
- return false;
- }
-
- // For all other errors (and for unexpected EOF case), bail out.
- if (rv <= 0) {
- NotifyFailure(net::ERR_FAILED);
+ DCHECK(reader);
+ const int result = reader->Read(
+ read_buf_, bytes_to_read,
+ base::Bind(&BlobURLRequestJob::DidReadFile,
+ base::Unretained(this)));
+ if (result != net::ERR_IO_PENDING) {
+ DCHECK(result != net::OK);
+ NotifyFailure(result);
return false;
}
-
- // Otherwise, data is immediately available.
- if (GetStatus().is_io_pending())
- DidReadFileStream(rv);
- else
- AdvanceBytesRead(rv);
-
- return true;
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
+ return false;
}
-void BlobURLRequestJob::DidReadFileStream(int result) {
+void BlobURLRequestJob::DidReadFile(int result) {
if (result <= 0) {
NotifyFailure(net::ERR_FAILED);
return;
@@ -451,12 +392,11 @@ void BlobURLRequestJob::DidReadFileStream(int result) {
NotifyReadComplete(bytes_read);
}
-void BlobURLRequestJob::CloseFileStream() {
- if (stream_ != NULL) {
- // stream_.Close() blocks the IO thread, see http://crbug.com/75548.
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- stream_->CloseSync();
- stream_.reset(NULL);
+void BlobURLRequestJob::DeleteCurrentFileReader() {
+ IndexToReaderMap::iterator found = index_to_reader_.find(current_item_index_);
+ if (found != index_to_reader_.end() && found->second) {
+ delete found->second;
+ index_to_reader_.erase(found);
}
}
@@ -578,4 +518,20 @@ void BlobURLRequestJob::HeadersCompleted(int status_code,
NotifyHeadersComplete();
}
+LocalFileReader* BlobURLRequestJob::GetFileReader(size_t index) {
+ DCHECK_LT(index, blob_data_->items().size());
+ const BlobData::Item& item = blob_data_->items().at(index);
+ if (item.type != BlobData::TYPE_FILE)
+ return NULL;
+ if (index_to_reader_.find(index) == index_to_reader_.end()) {
+ index_to_reader_[index] = new LocalFileReader(
+ file_thread_proxy_,
+ item.file_path,
+ item.offset,
+ item.expected_modification_time);
+ }
+ DCHECK(index_to_reader_[index]);
+ return index_to_reader_[index];
+}
+
} // namespace webkit_blob
diff --git a/webkit/blob/blob_url_request_job.h b/webkit/blob/blob_url_request_job.h
index e242aff..86a5f31 100644
--- a/webkit/blob/blob_url_request_job.h
+++ b/webkit/blob/blob_url_request_job.h
@@ -26,6 +26,8 @@ class IOBuffer;
namespace webkit_blob {
+class LocalFileReader;
+
// A request job that handles reading blob URLs.
class BLOB_EXPORT BlobURLRequestJob : public net::URLRequestJob {
public:
@@ -47,13 +49,13 @@ class BLOB_EXPORT BlobURLRequestJob : public net::URLRequestJob {
const net::HttpRequestHeaders& headers) OVERRIDE;
private:
+ typedef std::map<size_t, LocalFileReader*> IndexToReaderMap;
+
// For preparing for read: get the size, apply the range and perform seek.
void DidStart();
void CountSize();
void DidCountSize(int error);
- void DidGetFileItemInfo(size_t index,
- base::PlatformFileError error,
- const base::PlatformFileInfo& file_info);
+ void DidGetFileItemLength(size_t index, int result);
void Seek(int64 offset);
// For reading the blob.
@@ -62,15 +64,10 @@ class BLOB_EXPORT BlobURLRequestJob : public net::URLRequestJob {
void AdvanceItem();
void AdvanceBytesRead(int result);
bool ReadBytesItem(const BlobData::Item& item, int bytes_to_read);
- bool ReadFileItem(const BlobData::Item& item, int bytes_to_read);
+ bool ReadFileItem(LocalFileReader* reader, int bytes_to_read);
- void DidOpenFile(int bytes_to_read,
- base::PlatformFileError rv,
- base::PassPlatformFile file,
- bool created);
- bool ReadFileStream(int bytes_to_read);
- void DidReadFileStream(int result);
- void CloseFileStream();
+ void DidReadFile(int result);
+ void DeleteCurrentFileReader();
int ComputeBytesToRead() const;
int BytesReadCompleted();
@@ -79,6 +76,10 @@ class BLOB_EXPORT BlobURLRequestJob : public net::URLRequestJob {
void NotifyFailure(int);
void HeadersCompleted(int status_code, const std::string& status_txt);
+ // Returns a LocalFileReader for a blob item at |index|.
+ // If the item at |index| is not of TYPE_FILE this returns NULL.
+ LocalFileReader* GetFileReader(size_t index);
+
base::WeakPtrFactory<BlobURLRequestJob> weak_factory_;
scoped_refptr<BlobData> blob_data_;
scoped_refptr<base::MessageLoopProxy> file_thread_proxy_;
@@ -86,7 +87,7 @@ class BLOB_EXPORT BlobURLRequestJob : public net::URLRequestJob {
int64 total_size_;
int64 remaining_bytes_;
int pending_get_file_info_count_;
- scoped_ptr<net::FileStream> stream_;
+ IndexToReaderMap index_to_reader_;
size_t current_item_index_;
int64 current_item_offset_;
scoped_refptr<net::DrainableIOBuffer> read_buf_;
diff --git a/webkit/blob/blob_url_request_job_unittest.cc b/webkit/blob/blob_url_request_job_unittest.cc
index 12a0cb7..515574a 100644
--- a/webkit/blob/blob_url_request_job_unittest.cc
+++ b/webkit/blob/blob_url_request_job_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -259,10 +259,10 @@ class BlobURLRequestJobTest : public testing::Test {
void VerifyResponse() {
EXPECT_TRUE(request_->status().is_success());
- EXPECT_EQ(request_->response_headers()->response_code(),
- expected_status_code_);
- EXPECT_STREQ(url_request_delegate_->response_data().c_str(),
- expected_response_.c_str());
+ EXPECT_EQ(expected_status_code_,
+ request_->response_headers()->response_code());
+ EXPECT_STREQ(expected_response_.c_str(),
+ url_request_delegate_->response_data().c_str());
TestFinished();
}
diff --git a/webkit/blob/local_file_reader.cc b/webkit/blob/local_file_reader.cc
new file mode 100644
index 0000000..54493c1
--- /dev/null
+++ b/webkit/blob/local_file_reader.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2012 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/blob/local_file_reader.h"
+
+#include "base/file_util.h"
+#include "base/file_util_proxy.h"
+#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "base/platform_file.h"
+#include "net/base/file_stream.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace webkit_blob {
+
+namespace {
+
+const int kOpenFlagsForRead = base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_ASYNC;
+
+int PlatformFileErrorToNetError(base::PlatformFileError file_error) {
+ switch (file_error) {
+ case base::PLATFORM_FILE_OK:
+ return net::OK;
+ case base::PLATFORM_FILE_ERROR_NOT_FOUND:
+ return net::ERR_FILE_NOT_FOUND;
+ case base::PLATFORM_FILE_ERROR_ACCESS_DENIED:
+ return net::ERR_ACCESS_DENIED;
+ default:
+ return net::ERR_FAILED;
+ }
+}
+
+// Verify if the underlying file has not been modified.
+bool VerifySnapshotTime(const base::Time& expected_modification_time,
+ const base::PlatformFileInfo& file_info) {
+ return expected_modification_time.is_null() ||
+ expected_modification_time.ToTimeT() ==
+ file_info.last_modified.ToTimeT();
+}
+
+void DidGetFileInfoForGetLength(const net::CompletionCallback& callback,
+ const base::Time& expected_modification_time,
+ int64 initial_offset,
+ base::PlatformFileError error,
+ const base::PlatformFileInfo& file_info) {
+ if (file_info.is_directory) {
+ callback.Run(net::ERR_FILE_NOT_FOUND);
+ return;
+ }
+ if (error != base::PLATFORM_FILE_OK) {
+ callback.Run(PlatformFileErrorToNetError(error));
+ return;
+ }
+ if (!VerifySnapshotTime(expected_modification_time, file_info)) {
+ callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
+ return;
+ }
+ callback.Run(file_info.size - initial_offset);
+}
+
+void DidSeekFile(const LocalFileReader::OpenFileStreamCallback& callback,
+ scoped_ptr<net::FileStream> stream_impl,
+ int64 initial_offset,
+ int64 new_offset) {
+ int result = net::OK;
+ if (new_offset < 0)
+ result = static_cast<int>(new_offset);
+ else if (new_offset != initial_offset)
+ result = net::ERR_FAILED;
+ callback.Run(result, stream_impl.Pass());
+}
+
+void EmptyCompletionCallback(int) {}
+
+} // namespace
+
+// A helper class to open, verify and seek a file stream for a given path.
+class LocalFileReader::OpenFileStreamHelper {
+ public:
+ OpenFileStreamHelper(base::MessageLoopProxy* file_thread_proxy)
+ : file_thread_proxy_(file_thread_proxy),
+ file_handle_(base::kInvalidPlatformFileValue),
+ result_(net::OK) {}
+ ~OpenFileStreamHelper() {
+ if (file_handle_ != base::kInvalidPlatformFileValue) {
+ file_thread_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&base::ClosePlatformFile),
+ file_handle_));
+ }
+ }
+
+ void OpenAndVerifyOnFileThread(const FilePath& file_path,
+ const base::Time& expected_modification_time) {
+ base::PlatformFileError file_error = base::PLATFORM_FILE_OK;
+ file_handle_ = base::CreatePlatformFile(
+ file_path, kOpenFlagsForRead, NULL, &file_error);
+ if (file_error != base::PLATFORM_FILE_OK) {
+ result_ = PlatformFileErrorToNetError(file_error);
+ return;
+ }
+ DCHECK_NE(base::kInvalidPlatformFileValue, file_handle_);
+ base::PlatformFileInfo file_info;
+ if (!base::GetPlatformFileInfo(file_handle_, &file_info)) {
+ result_ = net::ERR_FAILED;
+ return;
+ }
+ if (!VerifySnapshotTime(expected_modification_time, file_info)) {
+ result_ = net::ERR_UPLOAD_FILE_CHANGED;
+ return;
+ }
+ result_ = net::OK;
+ }
+
+ void OpenStreamOnCallingThread(int64 initial_offset,
+ const OpenFileStreamCallback& callback) {
+ DCHECK(!callback.is_null());
+ scoped_ptr<net::FileStream> stream_impl;
+ if (result_ != net::OK) {
+ callback.Run(result_, stream_impl.Pass());
+ return;
+ }
+ stream_impl.reset(
+ new net::FileStream(file_handle_, kOpenFlagsForRead, NULL));
+ file_handle_ = base::kInvalidPlatformFileValue;
+ result_ = stream_impl->Seek(net::FROM_BEGIN, initial_offset,
+ base::Bind(&DidSeekFile, callback,
+ base::Passed(&stream_impl),
+ initial_offset));
+ if (result_ != net::ERR_IO_PENDING)
+ callback.Run(result_, stream_impl.Pass());
+ }
+
+ private:
+ scoped_refptr<base::MessageLoopProxy> file_thread_proxy_;
+ base::PlatformFile file_handle_;
+ int result_;
+ DISALLOW_COPY_AND_ASSIGN(OpenFileStreamHelper);
+};
+
+LocalFileReader::LocalFileReader(
+ base::MessageLoopProxy* file_thread_proxy,
+ const FilePath& file_path,
+ int64 initial_offset,
+ const base::Time& expected_modification_time)
+ : file_thread_proxy_(file_thread_proxy),
+ file_path_(file_path),
+ initial_offset_(initial_offset),
+ expected_modification_time_(expected_modification_time),
+ has_pending_open_(false),
+ weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
+
+LocalFileReader::~LocalFileReader() {
+ if (!stream_impl_.get())
+ return;
+ stream_impl_->Close(base::Bind(&EmptyCompletionCallback));
+}
+
+int LocalFileReader::Read(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback) {
+ DCHECK(!has_pending_open_);
+ if (stream_impl_.get())
+ return stream_impl_->Read(buf, buf_len, callback);
+ return Open(base::Bind(&LocalFileReader::DidOpen, weak_factory_.GetWeakPtr(),
+ make_scoped_refptr(buf), buf_len, callback));
+}
+
+int LocalFileReader::GetLength(const net::CompletionCallback& callback) {
+ const bool posted = base::FileUtilProxy::GetFileInfo(
+ file_thread_proxy_, file_path_,
+ base::Bind(&DidGetFileInfoForGetLength, callback,
+ expected_modification_time_, initial_offset_));
+ DCHECK(posted);
+ return net::ERR_IO_PENDING;
+}
+
+int LocalFileReader::Open(const OpenFileStreamCallback& callback) {
+ DCHECK(!has_pending_open_);
+ DCHECK(!stream_impl_.get());
+ has_pending_open_ = true;
+ OpenFileStreamHelper* helper = new OpenFileStreamHelper(file_thread_proxy_);
+ const bool posted = file_thread_proxy_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&OpenFileStreamHelper::OpenAndVerifyOnFileThread,
+ base::Unretained(helper), file_path_,
+ expected_modification_time_),
+ base::Bind(&OpenFileStreamHelper::OpenStreamOnCallingThread,
+ base::Owned(helper), initial_offset_, callback));
+ DCHECK(posted);
+ return net::ERR_IO_PENDING;
+}
+
+void LocalFileReader::DidOpen(net::IOBuffer* buf,
+ int buf_len,
+ const net::CompletionCallback& callback,
+ int open_error,
+ scoped_ptr<net::FileStream> stream_impl) {
+ DCHECK(has_pending_open_);
+ DCHECK(!stream_impl_.get());
+ has_pending_open_ = false;
+ if (open_error != net::OK) {
+ callback.Run(open_error);
+ return;
+ }
+ DCHECK(stream_impl.get());
+ stream_impl_ = stream_impl.Pass();
+ const int read_error = stream_impl_->Read(buf, buf_len, callback);
+ if (read_error != net::ERR_IO_PENDING)
+ callback.Run(read_error);
+}
+
+} // namespace webkit_blob
diff --git a/webkit/blob/local_file_reader.h b/webkit/blob/local_file_reader.h
new file mode 100644
index 0000000..fd87dd4
--- /dev/null
+++ b/webkit/blob/local_file_reader.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 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_BLOB_LOCAL_FILE_READER_H_
+#define WEBKIT_BLOB_LOCAL_FILE_READER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time.h"
+#include "net/base/completion_callback.h"
+#include "webkit/blob/blob_export.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace net {
+class FileStream;
+class IOBuffer;
+}
+
+namespace webkit_blob {
+
+// A thin wrapper of net::FileStream with range support for sliced file
+// handling.
+class BLOB_EXPORT LocalFileReader {
+ public:
+ typedef base::Callback<void(int error, scoped_ptr<net::FileStream> stream)>
+ OpenFileStreamCallback;
+
+ // Creates a new FileReader for a local file |file_path|.
+ // |initial_offset| specifies the offset in the file where the first read
+ // should start.
+ // |expected_modification_time| specifies the expected last modification
+ // If the value is non-null, the reader will check the underlying file's
+ // actual modification time to see if the file has been modified, and if
+ // it does any succeeding read operations should fail with
+ // ERR_UPLOAD_FILE_CHANGED error.
+ LocalFileReader(base::MessageLoopProxy* file_thread_proxy,
+ const FilePath& file_path,
+ int64 initial_offset,
+ const base::Time& expected_modification_time);
+
+ ~LocalFileReader();
+
+ // Reads from the current cursor position asynchronously.
+ // This works mostly same as how net::FileStream::Read() works except that
+ // it internally opens (and seeks) the file if it is not opened yet.
+ // It is invalid to call Read while there is an in-flight Read operation.
+ int Read(net::IOBuffer* buf, int buf_len,
+ const net::CompletionCallback& callback);
+
+ // Returns the number of bytes available to read from the beginning of
+ // the file (or initial_offset) until the end of the file (rv >= 0 cases).
+ // Otherwise, a negative error code is returned (rv < 0 cases).
+ int GetLength(const net::CompletionCallback& callback);
+
+ private:
+ class OpenFileStreamHelper;
+
+ int Open(const OpenFileStreamCallback& callback);
+ void DidOpen(net::IOBuffer* buf,
+ int buf_len,
+ const net::CompletionCallback& callback,
+ int open_error,
+ scoped_ptr<net::FileStream> stream);
+
+ scoped_refptr<base::MessageLoopProxy> file_thread_proxy_;
+ scoped_ptr<net::FileStream> stream_impl_;
+ const FilePath file_path_;
+ const int64 initial_offset_;
+ const base::Time expected_modification_time_;
+ bool has_pending_open_;
+ base::WeakPtrFactory<LocalFileReader> weak_factory_;
+};
+
+} // namespace webkit_blob
+
+#endif // WEBKIT_BLOB_LOCAL_FILE_READER_H_
diff --git a/webkit/blob/webkit_blob.gypi b/webkit/blob/webkit_blob.gypi
index bb07019..196c3fb 100644
--- a/webkit/blob/webkit_blob.gypi
+++ b/webkit/blob/webkit_blob.gypi
@@ -1,4 +1,4 @@
-# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Copyright (c) 2012 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.
@@ -37,6 +37,8 @@
'blob_url_request_job.h',
'blob_url_request_job_factory.cc',
'blob_url_request_job_factory.h',
+ 'local_file_reader.cc',
+ 'local_file_reader.h',
'shareable_file_reference.cc',
'shareable_file_reference.h',
'view_blob_internals_job.cc',