diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-14 11:23:23 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-14 11:23:23 +0000 |
commit | 192bd691ed6eedb3e08b5b619019cb7cb4300b84 (patch) | |
tree | a9164dc70a18a2e008d28103d582a83841e7bc8a /webkit | |
parent | a91ef5e4ab532cc708ad90a1ddb890bb1c839300 (diff) | |
download | chromium_src-192bd691ed6eedb3e08b5b619019cb7cb4300b84.zip chromium_src-192bd691ed6eedb3e08b5b619019cb7cb4300b84.tar.gz chromium_src-192bd691ed6eedb3e08b5b619019cb7cb4300b84.tar.bz2 |
Separate FileStream related code out of BlobURLRequestJob
This patch separates FileStream related code out of BlobURLRequestJob to:
- make BlobURLRequesetJob's responsibility simpler and clearer by moving file-related details out of the class
- remove blocking file operation on IO thread
Long-term goal includes using the same reader class/interface to FileSystemURLRequestJob and other fileapi streaming related code.
(Simplified the patch from the previous version: was: "Add FileStreamInterface for fileapi/blob stream related operations")
BUG=114999,75548
TEST=blob tests
Review URL: https://chromiumcodereview.appspot.com/9651032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126623 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/blob/blob_url_request_job.cc | 166 | ||||
-rw-r--r-- | webkit/blob/blob_url_request_job.h | 25 | ||||
-rw-r--r-- | webkit/blob/blob_url_request_job_unittest.cc | 10 | ||||
-rw-r--r-- | webkit/blob/local_file_reader.cc | 174 | ||||
-rw-r--r-- | webkit/blob/local_file_reader.h | 82 | ||||
-rw-r--r-- | webkit/blob/webkit_blob.gypi | 2 |
6 files changed, 337 insertions, 122 deletions
diff --git a/webkit/blob/blob_url_request_job.cc b/webkit/blob/blob_url_request_job.cc index ccc229c..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_.Seek() 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_->Seek(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..3de9427 --- /dev/null +++ b/webkit/blob/local_file_reader.cc @@ -0,0 +1,174 @@ +// 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); +} + +} // namespace + +class LocalFileReader::OpenFileStreamHelper { + public: + OpenFileStreamHelper() : result_(net::OK) {} + + void OpenAndSeekOnFileThread(const FilePath& file_path, + int64 initial_offset, + const base::Time& expected_modification_time) { + base::PlatformFileInfo file_info; + const bool result = file_util::GetFileInfo(file_path, &file_info); + if (!result) { + result_ = net::ERR_FAILED; + return; + } + + if (!VerifySnapshotTime(expected_modification_time, file_info)) { + result_ = net::ERR_UPLOAD_FILE_CHANGED; + return; + } + + stream_impl_.reset(new net::FileStream(NULL)); + result_ = stream_impl_->OpenSync(file_path, kOpenFlagsForRead); + if (result_ != net::OK) { + stream_impl_.reset(); + return; + } + + const int64 returned_offset = stream_impl_->Seek( + net::FROM_BEGIN, initial_offset); + if (returned_offset != initial_offset) { + result_ = net::ERR_FAILED; + return; + } + + result_ = net::OK; + } + + void Reply(const OpenFileStreamCallback& callback) { + callback.Run(result_, stream_impl_.Pass()); + } + + private: + base::WeakPtr<LocalFileReader> stream_; + scoped_ptr<net::FileStream> stream_impl_; + int result_; +}; + +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() {} + +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; + const bool posted = file_thread_proxy_->PostTaskAndReply( + FROM_HERE, + base::Bind(&OpenFileStreamHelper::OpenAndSeekOnFileThread, + base::Unretained(helper), file_path_, initial_offset_, + expected_modification_time_), + base::Bind(&OpenFileStreamHelper::Reply, base::Owned(helper), callback)); + DCHECK(posted); + return net::ERR_IO_PENDING; +} + +void LocalFileReader::DidOpen(net::IOBuffer* buf, + int buf_len, + const net::CompletionCallback& callback, + int error, + scoped_ptr<net::FileStream> stream_impl) { + DCHECK(has_pending_open_); + DCHECK(!stream_impl_.get()); + stream_impl_ = stream_impl.Pass(); + has_pending_open_ = false; + error = stream_impl_->Read(buf, buf_len, callback); + if (error != net::ERR_IO_PENDING) + callback.Run(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..59bc356 --- /dev/null +++ b/webkit/blob/local_file_reader.h @@ -0,0 +1,82 @@ +// 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" + +namespace base { +class MessageLoopProxy; +} + +namespace net { +class FileStream; +class IOBuffer; +} + +namespace webkit_blob { + +// A thin wrapper of net::FileStream for sequential access to the file. +class 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 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..3ffc7ea 100644 --- a/webkit/blob/webkit_blob.gypi +++ b/webkit/blob/webkit_blob.gypi @@ -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', |