diff options
30 files changed, 324 insertions, 66 deletions
diff --git a/build/features_override.gypi b/build/features_override.gypi index e511957..4b289b1 100644 --- a/build/features_override.gypi +++ b/build/features_override.gypi @@ -11,6 +11,7 @@ # but not listed below, it will revert to its hardcoded webkit value. 'feature_defines': [ 'ENABLE_3D_CANVAS=1', + 'ENABLE_BLOB_SLICE=1', 'ENABLE_CHANNEL_MESSAGING=1', 'ENABLE_DATABASE=1', 'ENABLE_DATAGRID=0', diff --git a/chrome/browser/chrome_plugin_host.cc b/chrome/browser/chrome_plugin_host.cc index 50d9abc..56d8828 100644 --- a/chrome/browser/chrome_plugin_host.cc +++ b/chrome/browser/chrome_plugin_host.cc @@ -656,7 +656,8 @@ CPError STDCALL CPR_AppendFileToUpload(CPRequest* request, const char* filepath, if (!length) length = kuint64max; FilePath path(FilePath::FromWStringHack(UTF8ToWide(filepath))); - handler->request()->AppendFileRangeToUpload(path, offset, length); + handler->request()->AppendFileRangeToUpload(path, offset, length, + base::Time()); return CPERR_SUCCESS; } diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 48c1cb6..de9e836 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -265,6 +265,17 @@ class GetRawCookiesCompletion : public net::CompletionCallback { scoped_refptr<URLRequestContext> context_; }; +void WriteFileSize(IPC::Message* reply_msg, + const file_util::FileInfo& file_info) { + ViewHostMsg_GetFileSize::WriteReplyParams(reply_msg, file_info.size); +} + +void WriteFileModificationTime(IPC::Message* reply_msg, + const file_util::FileInfo& file_info) { + ViewHostMsg_GetFileModificationTime::WriteReplyParams( + reply_msg, file_info.last_modified); +} + } // namespace ResourceMessageFilter::ResourceMessageFilter( @@ -520,6 +531,8 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) { OnCloseCurrentConnections) IPC_MESSAGE_HANDLER(ViewHostMsg_SetCacheMode, OnSetCacheMode) IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetFileSize, OnGetFileSize) + IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetFileModificationTime, + OnGetFileModificationTime) IPC_MESSAGE_HANDLER(ViewHostMsg_Keygen, OnKeygen) IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetExtensionMessageBundle, OnGetExtensionMessageBundle) @@ -1318,18 +1331,41 @@ void ResourceMessageFilter::OnGetFileSize(const FilePath& path, ChromeThread::PostTask( ChromeThread::FILE, FROM_HERE, NewRunnableMethod( - this, &ResourceMessageFilter::OnGetFileSizeOnFileThread, path, - reply_msg)); + this, &ResourceMessageFilter::OnGetFileInfoOnFileThread, path, + reply_msg, &WriteFileSize)); } -void ResourceMessageFilter::OnGetFileSizeOnFileThread( - const FilePath& path, IPC::Message* reply_msg) { +void ResourceMessageFilter::OnGetFileModificationTime(const FilePath& path, + IPC::Message* reply_msg) { + // Get file modification time only when the child process has been granted + // permission to upload the file. + if (!ChildProcessSecurityPolicy::GetInstance()->CanUploadFile(id(), path)) { + ViewHostMsg_GetFileModificationTime::WriteReplyParams(reply_msg, + base::Time()); + Send(reply_msg); + return; + } + + // Getting file modification time could take a long time if it lives on a + // network share, so run it on the FILE thread. + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod( + this, &ResourceMessageFilter::OnGetFileInfoOnFileThread, + path, reply_msg, &WriteFileModificationTime)); +} + +void ResourceMessageFilter::OnGetFileInfoOnFileThread( + const FilePath& path, + IPC::Message* reply_msg, + FileInfoWriteFunc write_func) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); - int64 result; - if (!file_util::GetFileSize(path, &result)) - result = -1; - ViewHostMsg_GetFileSize::WriteReplyParams(reply_msg, result); + file_util::FileInfo file_info; + file_info.size = 0; + file_util::GetFileInfo(path, &file_info); + + (*write_func)(reply_msg, file_info); ChromeThread::PostTask( ChromeThread::IO, FROM_HERE, diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index c903f6e..2abc4c0 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -48,6 +48,10 @@ class URLRequestContextGetter; struct ViewHostMsg_CreateWorker_Params; struct WebPluginInfo; +namespace file_util { +struct FileInfo; +} + namespace printing { class PrinterQuery; class PrintJobManager; @@ -122,6 +126,8 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, private: friend class ChromeThread; friend class DeleteTask<ResourceMessageFilter>; + typedef void (*FileInfoWriteFunc)(IPC::Message* reply_msg, + const file_util::FileInfo& file_info); virtual ~ResourceMessageFilter(); @@ -306,7 +312,10 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, void OnSetCacheMode(bool enabled); void OnGetFileSize(const FilePath& path, IPC::Message* reply_msg); - void OnGetFileSizeOnFileThread(const FilePath& path, IPC::Message* reply_msg); + void OnGetFileModificationTime(const FilePath& path, IPC::Message* reply_msg); + void OnGetFileInfoOnFileThread(const FilePath& path, + IPC::Message* reply_msg, + FileInfoWriteFunc write_func); void OnKeygen(uint32 key_size_index, const std::string& challenge_string, const GURL& url, std::string* signed_public_key); void OnGetExtensionMessageBundle(const std::string& extension_id, diff --git a/chrome/common/common_param_traits.h b/chrome/common/common_param_traits.h index 72f84ee..426075a 100644 --- a/chrome/common/common_param_traits.h +++ b/chrome/common/common_param_traits.h @@ -318,6 +318,7 @@ struct ParamTraits<net::UploadData::Element> { WriteParam(m, p.file_path()); WriteParam(m, p.file_range_offset()); WriteParam(m, p.file_range_length()); + WriteParam(m, p.expected_file_modification_time()); } } static bool Read(const Message* m, void** iter, param_type* r) { @@ -334,13 +335,17 @@ struct ParamTraits<net::UploadData::Element> { DCHECK(type == net::UploadData::TYPE_FILE); FilePath file_path; uint64 offset, length; + base::Time expected_modification_time; if (!ReadParam(m, iter, &file_path)) return false; if (!ReadParam(m, iter, &offset)) return false; if (!ReadParam(m, iter, &length)) return false; - r->SetToFilePathRange(file_path, offset, length); + if (!ReadParam(m, iter, &expected_modification_time)) + return false; + r->SetToFilePathRange(file_path, offset, length, + expected_modification_time); } return true; } diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index afc9a4c..a0f5d14 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -15,6 +15,7 @@ #include "base/file_path.h" #include "base/nullable_string16.h" #include "base/sync_socket.h" +#include "base/time.h" #include "base/values.h" #include "chrome/common/content_settings.h" #include "chrome/common/extensions/update_manifest.h" @@ -2101,6 +2102,12 @@ IPC_BEGIN_MESSAGES(ViewHost) FilePath /* path */, int64 /* result */) + // Get file modification time in seconds. Set result to 0 if failed to get the + // file modification time. + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_GetFileModificationTime, + FilePath /* path */, + base::Time /* result */) + // Sent by the renderer process to acknowledge receipt of a // ViewMsg_CSSInsertRequest message and css has been inserted into the frame. IPC_MESSAGE_ROUTED0(ViewHostMsg_OnCSSInserted) diff --git a/chrome/common/resource_dispatcher.cc b/chrome/common/resource_dispatcher.cc index 9df8eaf..5c2fd28 100644 --- a/chrome/common/resource_dispatcher.cc +++ b/chrome/common/resource_dispatcher.cc @@ -52,8 +52,11 @@ class IPCResourceLoaderBridge : public ResourceLoaderBridge { // ResourceLoaderBridge virtual void AppendDataToUpload(const char* data, int data_len); - virtual void AppendFileRangeToUpload(const FilePath& path, - uint64 offset, uint64 length); + virtual void AppendFileRangeToUpload( + const FilePath& path, + uint64 offset, + uint64 length, + const base::Time& expected_modification_time); virtual void SetUploadIdentifier(int64 identifier); virtual bool Start(Peer* peer); virtual void Cancel(); @@ -152,12 +155,14 @@ void IPCResourceLoaderBridge::AppendDataToUpload(const char* data, } void IPCResourceLoaderBridge::AppendFileRangeToUpload( - const FilePath& path, uint64 offset, uint64 length) { + const FilePath& path, uint64 offset, uint64 length, + const base::Time& expected_modification_time) { DCHECK(request_id_ == -1) << "request already started"; if (!request_.upload_data) request_.upload_data = new net::UploadData(); - request_.upload_data->AppendFileRange(path, offset, length); + request_.upload_data->AppendFileRange(path, offset, length, + expected_modification_time); } void IPCResourceLoaderBridge::SetUploadIdentifier(int64 identifier) { diff --git a/chrome/plugin/chrome_plugin_host.cc b/chrome/plugin/chrome_plugin_host.cc index 4561cc4..b3ae0ec 100644 --- a/chrome/plugin/chrome_plugin_host.cc +++ b/chrome/plugin/chrome_plugin_host.cc @@ -147,7 +147,8 @@ class PluginRequestHandlerProxy void AppendFileRangeToUpload(const FilePath &filepath, uint64 offset, uint64 length) { upload_content_.push_back(net::UploadData::Element()); - upload_content_.back().SetToFilePathRange(filepath, offset, length); + upload_content_.back().SetToFilePathRange(filepath, offset, length, + base::Time()); } CPError Start(int renderer_id, int render_view_id) { @@ -186,7 +187,8 @@ class PluginRequestHandlerProxy bridge_->AppendFileRangeToUpload( upload_content_[i].file_path(), upload_content_[i].file_range_offset(), - upload_content_[i].file_range_length()); + upload_content_[i].file_range_length(), + upload_content_[i].expected_file_modification_time()); break; } default: { diff --git a/chrome/renderer/renderer_webkitclient_impl.cc b/chrome/renderer/renderer_webkitclient_impl.cc index 534cd75..1f6736f 100644 --- a/chrome/renderer/renderer_webkitclient_impl.cc +++ b/chrome/renderer/renderer_webkitclient_impl.cc @@ -98,6 +98,21 @@ bool RendererWebKitClientImpl::getFileSize(const WebString& path, return false; } +bool RendererWebKitClientImpl::getFileModificationTime( + const WebKit::WebString& path, + double& result) { + base::Time time; + if (RenderThread::current()->Send( + new ViewHostMsg_GetFileModificationTime( + webkit_glue::WebStringToFilePath(path), &time))) { + result = time.ToDoubleT(); + return true; + } + + result = 0; + return false; +} + unsigned long long RendererWebKitClientImpl::visitedLinkHash( const char* canonical_url, size_t length) { diff --git a/chrome/renderer/renderer_webkitclient_impl.h b/chrome/renderer/renderer_webkitclient_impl.h index 71154c8..f041851 100644 --- a/chrome/renderer/renderer_webkitclient_impl.h +++ b/chrome/renderer/renderer_webkitclient_impl.h @@ -35,6 +35,8 @@ class RendererWebKitClientImpl : public webkit_glue::WebKitClientImpl { virtual WebKit::WebCookieJar* cookieJar(); virtual bool sandboxEnabled(); virtual bool getFileSize(const WebKit::WebString& path, long long& result); + virtual bool getFileModificationTime(const WebKit::WebString& path, + double& result); virtual unsigned long long visitedLinkHash( const char* canonicalURL, size_t length); virtual bool isLinkVisited(unsigned long long linkHash); diff --git a/chrome/renderer/translate/page_translator_unittest.cc b/chrome/renderer/translate/page_translator_unittest.cc index 18d293a..02f1b32 100644 --- a/chrome/renderer/translate/page_translator_unittest.cc +++ b/chrome/renderer/translate/page_translator_unittest.cc @@ -73,8 +73,11 @@ class DummyResourceLoaderBridge : public webkit_glue::ResourceLoaderBridge { DummyResourceLoaderBridge() { } virtual void AppendDataToUpload(const char* data, int data_len) {} - virtual void AppendFileRangeToUpload(const FilePath& file_path, - uint64 offset, uint64 length) {} + virtual void AppendFileRangeToUpload( + const FilePath& file_path, + uint64 offset, + uint64 length, + const base::Time& expected_modification_time) {} virtual void SetUploadIdentifier(int64 identifier) {} virtual bool Start(Peer* peer) { return false; } virtual void Cancel() {} diff --git a/chrome_frame/urlmon_upload_data_stream.cc b/chrome_frame/urlmon_upload_data_stream.cc index af7a1de..2c40ca2 100644 --- a/chrome_frame/urlmon_upload_data_stream.cc +++ b/chrome_frame/urlmon_upload_data_stream.cc @@ -5,10 +5,12 @@ #include "chrome_frame/urlmon_upload_data_stream.h" #include "net/base/io_buffer.h" +#include "net/base/net_errors.h" void UrlmonUploadDataStream::Initialize(net::UploadData* upload_data) { upload_data_ = upload_data; - request_body_stream_.reset(new net::UploadDataStream(upload_data)); + request_body_stream_.reset(net::UploadDataStream::Create(upload_data, NULL)); + DCHECK(request_body_stream_.get()); } STDMETHODIMP UrlmonUploadDataStream::Read(void* pv, ULONG cb, ULONG* read) { @@ -69,7 +71,9 @@ STDMETHODIMP UrlmonUploadDataStream::Seek(LARGE_INTEGER move, DWORD origin, // STREAM_SEEK_SETs to work with a 0 offset, but fail on everything else. if (origin == STREAM_SEEK_SET && move.QuadPart == 0) { if (request_body_stream_->position() != 0) { - request_body_stream_.reset(new net::UploadDataStream(upload_data_)); + request_body_stream_.reset( + net::UploadDataStream::Create(upload_data_, NULL)); + DCHECK(request_body_stream_.get()); } if (new_pos) { new_pos->QuadPart = 0; diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index 9c83568..50d528a 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -58,6 +58,10 @@ NET_ERROR(INSUFFICIENT_RESOURCES, -12) // Memory allocation failed. NET_ERROR(OUT_OF_MEMORY, -13) +// The file upload failed because the file's modification time was different +// from the expectation. +NET_ERROR(UPLOAD_FILE_CHANGED, -14) + // A connection was closed (corresponding to a TCP FIN). NET_ERROR(CONNECTION_CLOSED, -100) diff --git a/net/base/upload_data.h b/net/base/upload_data.h index 3aab835..869ab68 100644 --- a/net/base/upload_data.h +++ b/net/base/upload_data.h @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "base/file_path.h" #include "base/ref_counted.h" +#include "base/time.h" #include "testing/gtest/include/gtest/gtest_prod.h" namespace net { @@ -35,6 +36,10 @@ class UploadData : public base::RefCounted<UploadData> { const FilePath& file_path() const { return file_path_; } uint64 file_range_offset() const { return file_range_offset_; } uint64 file_range_length() const { return file_range_length_; } + // If NULL time is returned, we do not do the check. + const base::Time& expected_file_modification_time() const { + return expected_file_modification_time_; + } void SetToBytes(const char* bytes, int bytes_len) { type_ = TYPE_BYTES; @@ -42,15 +47,20 @@ class UploadData : public base::RefCounted<UploadData> { } void SetToFilePath(const FilePath& path) { - SetToFilePathRange(path, 0, kuint64max); + SetToFilePathRange(path, 0, kuint64max, base::Time()); } + // If expected_modification_time is NULL, we do not check for the file + // change. Also note that the granularity for comparison is time_t, not + // the full precision. void SetToFilePathRange(const FilePath& path, - uint64 offset, uint64 length) { + uint64 offset, uint64 length, + const base::Time& expected_modification_time) { type_ = TYPE_FILE; file_path_ = path; file_range_offset_ = offset; file_range_length_ = length; + expected_file_modification_time_ = expected_modification_time; } // Returns the byte-length of the element. For files that do not exist, 0 @@ -69,6 +79,7 @@ class UploadData : public base::RefCounted<UploadData> { FilePath file_path_; uint64 file_range_offset_; uint64 file_range_length_; + base::Time expected_file_modification_time_; bool override_content_length_; uint64 content_length_; @@ -89,9 +100,11 @@ class UploadData : public base::RefCounted<UploadData> { } void AppendFileRange(const FilePath& file_path, - uint64 offset, uint64 length) { + uint64 offset, uint64 length, + const base::Time& expected_modification_time) { elements_.push_back(Element()); - elements_.back().SetToFilePathRange(file_path, offset, length); + elements_.back().SetToFilePathRange(file_path, offset, length, + expected_modification_time); } // Returns the total size in bytes of the data to upload. diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index 21f4ce8..a3a67fa 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -4,12 +4,25 @@ #include "net/base/upload_data_stream.h" +#include "base/file_util.h" #include "base/logging.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" namespace net { +UploadDataStream* UploadDataStream::Create(const UploadData* data, + int* error_code) { + scoped_ptr<UploadDataStream> stream(new UploadDataStream(data)); + int rv = stream->FillBuf(); + if (error_code) + *error_code = rv; + if (rv != OK) + return NULL; + + return stream.release(); +} + UploadDataStream::UploadDataStream(const UploadData* data) : data_(data), buf_(new IOBuffer(kBufSize)), @@ -20,7 +33,6 @@ UploadDataStream::UploadDataStream(const UploadData* data) total_size_(data->GetContentLength()), current_position_(0), eof_(false) { - FillBuf(); } UploadDataStream::~UploadDataStream() { @@ -39,7 +51,7 @@ void UploadDataStream::DidConsume(size_t num_bytes) { current_position_ += num_bytes; } -void UploadDataStream::FillBuf() { +int UploadDataStream::FillBuf() { std::vector<UploadData::Element>::const_iterator end = data_->elements().end(); @@ -66,6 +78,18 @@ void UploadDataStream::FillBuf() { } else { DCHECK(element.type() == UploadData::TYPE_FILE); + // If the underlying file has been changed, treat it as error. + // 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. + if (!element.expected_file_modification_time().is_null()) { + file_util::FileInfo info; + if (file_util::GetFileInfo(element.file_path(), &info) && + element.expected_file_modification_time().ToTimeT() != + info.last_modified.ToTimeT()) { + return ERR_UPLOAD_FILE_CHANGED; + } + } + if (!next_element_stream_.IsOpen()) { int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ; @@ -110,6 +134,8 @@ void UploadDataStream::FillBuf() { if (next_element_ == end && !buf_len_) eof_ = true; + + return OK; } } // namespace net diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h index 0dd7dd3..b326dae 100644 --- a/net/base/upload_data_stream.h +++ b/net/base/upload_data_stream.h @@ -14,7 +14,11 @@ class IOBuffer; class UploadDataStream { public: - explicit UploadDataStream(const UploadData* data); + // Returns a new instance of UploadDataStream if it can be created and + // initialized successfully. If not, NULL will be returned and the error + // code will be set if the output parameter error_code is not empty. + static UploadDataStream* Create(const UploadData* data, int* error_code); + ~UploadDataStream(); // Returns the stream's buffer and buffer length. @@ -38,9 +42,14 @@ class UploadDataStream { bool eof() const { return eof_; } private: + // Protects from public access since now we have a static creator function + // which will do both creation and initialization and might return an error. + explicit UploadDataStream(const UploadData* data); + // Fills the buffer with any remaining data and sets eof_ if there was nothing // left to fill the buffer with. - void FillBuf(); + // Returns OK if the operation succeeds. Otherwise error code is returned. + int FillBuf(); const UploadData* data_; diff --git a/net/base/upload_data_stream_unittest.cc b/net/base/upload_data_stream_unittest.cc index ae8f7ff..701bad9 100644 --- a/net/base/upload_data_stream_unittest.cc +++ b/net/base/upload_data_stream_unittest.cc @@ -9,6 +9,9 @@ #include "base/basictypes.h" #include "base/file_path.h" #include "base/file_util.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "net/base/net_errors.h" #include "net/base/upload_data.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -26,20 +29,28 @@ class UploadDataStreamTest : public PlatformTest { public: UploadDataStreamTest() : upload_data_(new UploadData) { } + void FileChangedHelper(const FilePath& file_path, + const base::Time& time, + bool error_expected); + scoped_refptr<UploadData> upload_data_; }; TEST_F(UploadDataStreamTest, EmptyUploadData) { upload_data_->AppendBytes("", 0); - UploadDataStream stream(upload_data_); - EXPECT_TRUE(stream.eof()); + scoped_ptr<UploadDataStream> stream( + UploadDataStream::Create(upload_data_, NULL)); + ASSERT_TRUE(stream.get()); + EXPECT_TRUE(stream->eof()); } TEST_F(UploadDataStreamTest, ConsumeAll) { upload_data_->AppendBytes(kTestData, kTestDataSize); - UploadDataStream stream(upload_data_); - while (!stream.eof()) { - stream.DidConsume(stream.buf_len()); + scoped_ptr<UploadDataStream> stream( + UploadDataStream::Create(upload_data_, NULL)); + ASSERT_TRUE(stream.get()); + while (!stream->eof()) { + stream->DidConsume(stream->buf_len()); } } @@ -58,14 +69,54 @@ TEST_F(UploadDataStreamTest, FileSmallerThanLength) { upload_data_->set_elements(elements); EXPECT_EQ(kFakeSize, upload_data_->GetContentLength()); - UploadDataStream stream(upload_data_); - EXPECT_FALSE(stream.eof()); + scoped_ptr<UploadDataStream> stream( + UploadDataStream::Create(upload_data_, NULL)); + ASSERT_TRUE(stream.get()); + EXPECT_FALSE(stream->eof()); uint64 read_counter = 0; - while (!stream.eof()) { - read_counter += stream.buf_len(); - stream.DidConsume(stream.buf_len()); + while (!stream->eof()) { + read_counter += stream->buf_len(); + stream->DidConsume(stream->buf_len()); } - EXPECT_LT(read_counter, stream.size()); + EXPECT_LT(read_counter, stream->size()); + + file_util::Delete(temp_file_path, false); +} + +void UploadDataStreamTest::FileChangedHelper(const FilePath& file_path, + const base::Time& time, + bool error_expected) { + std::vector<UploadData::Element> elements; + UploadData::Element element; + element.SetToFilePathRange(file_path, 1, 2, time); + elements.push_back(element); + upload_data_->set_elements(elements); + + int error_code; + scoped_ptr<UploadDataStream> stream( + UploadDataStream::Create(upload_data_, &error_code)); + if (error_expected) + ASSERT_TRUE(!stream.get() && error_code == net::ERR_UPLOAD_FILE_CHANGED); + else + ASSERT_TRUE(stream.get() && error_code == net::OK); +} + +TEST_F(UploadDataStreamTest, FileChanged) { + FilePath temp_file_path; + ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); + ASSERT_EQ(kTestDataSize, file_util::WriteFile(temp_file_path, + kTestData, kTestDataSize)); + + file_util::FileInfo file_info; + ASSERT_TRUE(file_util::GetFileInfo(temp_file_path, &file_info)); + + // Test file not changed. + FileChangedHelper(temp_file_path, file_info.last_modified, false); + + // Test file changed. + FileChangedHelper(temp_file_path, + file_info.last_modified - base::TimeDelta::FromSeconds(1), + true); file_util::Delete(temp_file_path, false); } diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 1a2c9a0..218f26d 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -887,8 +887,12 @@ int HttpNetworkTransaction::DoSendRequest() { next_state_ = STATE_SEND_REQUEST_COMPLETE; UploadDataStream* request_body = NULL; - if (!establishing_tunnel_ && request_->upload_data) - request_body = new UploadDataStream(request_->upload_data); + if (!establishing_tunnel_ && request_->upload_data) { + int error_code; + request_body = UploadDataStream::Create(request_->upload_data, &error_code); + if (!request_body) + return error_code; + } // This is constructed lazily (instead of within our Start method), so that // we have proxy info available. @@ -1187,8 +1191,13 @@ int HttpNetworkTransaction::DoSpdySendRequest() { CHECK(spdy_session.get()); - UploadDataStream* upload_data = request_->upload_data ? - new UploadDataStream(request_->upload_data) : NULL; + UploadDataStream* upload_data = NULL; + if (request_->upload_data) { + int error_code = OK; + upload_data = UploadDataStream::Create(request_->upload_data, &error_code); + if (!upload_data) + return error_code; + } headers_valid_ = false; spdy_stream_ = spdy_session->GetOrCreateStream( *request_, upload_data, net_log_); diff --git a/net/spdy/spdy_network_transaction.cc b/net/spdy/spdy_network_transaction.cc index d7de332..c35c934 100644 --- a/net/spdy/spdy_network_transaction.cc +++ b/net/spdy/spdy_network_transaction.cc @@ -246,8 +246,13 @@ int SpdyNetworkTransaction::DoInitConnectionComplete(int result) { int SpdyNetworkTransaction::DoSendRequest() { next_state_ = STATE_SEND_REQUEST_COMPLETE; CHECK(!stream_.get()); - UploadDataStream* upload_data = request_->upload_data ? - new UploadDataStream(request_->upload_data) : NULL; + UploadDataStream* upload_data = NULL; + if (request_->upload_data) { + int error_code; + upload_data = UploadDataStream::Create(request_->upload_data, &error_code); + if (!upload_data) + return error_code; + } stream_ = spdy_->GetOrCreateStream(*request_, upload_data, net_log_); // Release the reference to |spdy_| since we don't need it anymore. spdy_ = NULL; diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index a44792d..7ada4a9 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc @@ -86,12 +86,16 @@ void URLRequest::AppendBytesToUpload(const char* bytes, int bytes_len) { upload_->AppendBytes(bytes, bytes_len); } -void URLRequest::AppendFileRangeToUpload(const FilePath& file_path, - uint64 offset, uint64 length) { +void URLRequest::AppendFileRangeToUpload( + const FilePath& file_path, + uint64 offset, + uint64 length, + const base::Time& expected_modification_time) { DCHECK(file_path.value().length() > 0 && length > 0); if (!upload_) upload_ = new UploadData(); - upload_->AppendFileRange(file_path, offset, length); + upload_->AppendFileRange(file_path, offset, length, + expected_modification_time); } void URLRequest::set_upload(net::UploadData* upload) { diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index 81d2436..438621b 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -299,12 +299,16 @@ class URLRequest { // // When uploading data, bytes_len must be non-zero. // When uploading a file range, length must be non-zero. If length - // exceeds the end-of-file, the upload is clipped at end-of-file. + // exceeds the end-of-file, the upload is clipped at end-of-file. If the + // expected modification time is provided (non-zero), it will be used to + // check if the underlying file has been changed or not. The granularity of + // the time comparison is 1 second since time_t precision is used in WebKit. void AppendBytesToUpload(const char* bytes, int bytes_len); void AppendFileRangeToUpload(const FilePath& file_path, - uint64 offset, uint64 length); + uint64 offset, uint64 length, + const base::Time& expected_modification_time); void AppendFileToUpload(const FilePath& file_path) { - AppendFileRangeToUpload(file_path, 0, kuint64max); + AppendFileRangeToUpload(file_path, 0, kuint64max, base::Time()); } // Set the upload data directly. diff --git a/webkit/glue/glue_serialize.cc b/webkit/glue/glue_serialize.cc index 802f57d..4b4efd3 100644 --- a/webkit/glue/glue_serialize.cc +++ b/webkit/glue/glue_serialize.cc @@ -19,6 +19,7 @@ #include "webkit/glue/webkit_glue.h" using WebKit::WebData; +using WebKit::WebFileInfo; using WebKit::WebHistoryItem; using WebKit::WebHTTPBody; using WebKit::WebPoint; @@ -53,12 +54,13 @@ struct SerializeObject { // 5: Adds support for empty FormData // 6: Adds support for documentSequenceNumbers // 7: Adds support for stateObject +// 8: Adds support for file range and modification time // Should be const, but unit tests may modify it. // // NOTE: If the version is -1, then the pickle contains only a URL string. // See CreateHistoryStateForURL. // -int kVersion = 7; +int kVersion = 8; // A bunch of convenience functions to read/write to SerializeObjects. // The serializers assume the input data is in the correct format and so does @@ -230,6 +232,9 @@ static void WriteFormData(const WebHTTPBody& http_body, SerializeObject* obj) { obj); } else { WriteString(element.filePath, obj); + WriteInteger64(element.fileStart, obj); + WriteInteger64(element.fileLength, obj); + WriteReal(element.fileInfo.modificationTime, obj); } } WriteInteger64(http_body.identifier(), obj); @@ -257,7 +262,16 @@ static WebHTTPBody ReadFormData(const SerializeObject* obj) { if (length >= 0) http_body.appendData(WebData(static_cast<const char*>(data), length)); } else { - http_body.appendFile(ReadString(obj)); + WebString file_path = ReadString(obj); + long long file_start = 0; + long long file_length = -1; + WebFileInfo file_info; + if (obj->version >= 8) { + file_start = ReadInteger64(obj); + file_length = ReadInteger64(obj); + file_info.modificationTime = ReadReal(obj); + } + http_body.appendFileRange(file_path, file_start, file_length, file_info); } } if (obj->version >= 4) diff --git a/webkit/glue/glue_serialize_unittest.cc b/webkit/glue/glue_serialize_unittest.cc index e3d91bd..34ce09f 100644 --- a/webkit/glue/glue_serialize_unittest.cc +++ b/webkit/glue/glue_serialize_unittest.cc @@ -13,6 +13,7 @@ #include "webkit/glue/glue_serialize.h" using WebKit::WebData; +using WebKit::WebFileInfo; using WebKit::WebHistoryItem; using WebKit::WebHTTPBody; using WebKit::WebPoint; diff --git a/webkit/glue/mock_resource_loader_bridge.h b/webkit/glue/mock_resource_loader_bridge.h index a3c40ba..7176e04 100644 --- a/webkit/glue/mock_resource_loader_bridge.h +++ b/webkit/glue/mock_resource_loader_bridge.h @@ -21,9 +21,11 @@ class MockResourceLoaderBridge : public webkit_glue::ResourceLoaderBridge { } MOCK_METHOD2(AppendDataToUpload, void(const char* data, int data_len)); - MOCK_METHOD3(AppendFileRangeToUpload, void(const FilePath& file_path, - uint64 offset, - uint64 length)); + MOCK_METHOD4(AppendFileRangeToUpload, + void(const FilePath& file_path, + uint64 offset, + uint64 length, + const base::Time& expected_modification_time)); MOCK_METHOD1(SetUploadIdentifier, void(int64 identifier)); MOCK_METHOD1(Start, bool(ResourceLoaderBridge::Peer* peer)); MOCK_METHOD0(Cancel, void()); diff --git a/webkit/glue/resource_loader_bridge.h b/webkit/glue/resource_loader_bridge.h index a1ede6c..26253f8 100644 --- a/webkit/glue/resource_loader_bridge.h +++ b/webkit/glue/resource_loader_bridge.h @@ -208,13 +208,16 @@ class ResourceLoaderBridge { // Call this method before calling Start() to append the contents of a file // to the request body. May only be used with HTTP(S) POST requests. void AppendFileToUpload(const FilePath& file_path) { - AppendFileRangeToUpload(file_path, 0, kuint64max); + AppendFileRangeToUpload(file_path, 0, kuint64max, base::Time()); } // Call this method before calling Start() to append the contents of a file // to the request body. May only be used with HTTP(S) POST requests. - virtual void AppendFileRangeToUpload(const FilePath& file_path, - uint64 offset, uint64 length) = 0; + virtual void AppendFileRangeToUpload( + const FilePath& file_path, + uint64 offset, + uint64 length, + const base::Time& expected_modification_time) = 0; // Call this method before calling Start() to assign an upload identifier to // this request. This is used to enable caching of POST responses. A value diff --git a/webkit/glue/webkitclient_impl.cc b/webkit/glue/webkitclient_impl.cc index 4695c24..a8fcac3 100644 --- a/webkit/glue/webkitclient_impl.cc +++ b/webkit/glue/webkitclient_impl.cc @@ -454,7 +454,7 @@ bool WebKitClientImpl::getFileSize(const WebKit::WebString& path, } bool WebKitClientImpl::getFileModificationTime(const WebKit::WebString& path, - time_t& result) { + double& result) { NOTREACHED(); return false; } diff --git a/webkit/glue/webkitclient_impl.h b/webkit/glue/webkitclient_impl.h index df1f9c7..3017e94e 100644 --- a/webkit/glue/webkitclient_impl.h +++ b/webkit/glue/webkitclient_impl.h @@ -30,7 +30,8 @@ class WebKitClientImpl : public WebKit::WebKitClient { virtual bool deleteEmptyDirectory(const WebKit::WebString& path); virtual bool getFileSize(const WebKit::WebString& path, long long& result); virtual bool getFileModificationTime( - const WebKit::WebString& path, time_t& result); + const WebKit::WebString& path, + double& result); virtual WebKit::WebString directoryName(const WebKit::WebString& path); virtual WebKit::WebString pathByAppendingComponent( const WebKit::WebString& path, const WebKit::WebString& component); diff --git a/webkit/glue/weburlloader_impl.cc b/webkit/glue/weburlloader_impl.cc index 421cb8f..d1d17a4 100644 --- a/webkit/glue/weburlloader_impl.cc +++ b/webkit/glue/weburlloader_impl.cc @@ -375,7 +375,16 @@ void WebURLLoaderImpl::Context::Start( } break; case WebHTTPBody::Element::TypeFile: - bridge_->AppendFileToUpload(WebStringToFilePath(element.filePath)); + if (element.fileLength == -1) { + bridge_->AppendFileToUpload( + WebStringToFilePath(element.filePath)); + } else { + bridge_->AppendFileRangeToUpload( + WebStringToFilePath(element.filePath), + static_cast<uint64>(element.fileStart), + static_cast<uint64>(element.fileLength), + base::Time::FromDoubleT(element.fileInfo.modificationTime)); + } break; default: NOTREACHED(); diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.cc b/webkit/tools/test_shell/simple_resource_loader_bridge.cc index 7aac167..baf8355 100644 --- a/webkit/tools/test_shell/simple_resource_loader_bridge.cc +++ b/webkit/tools/test_shell/simple_resource_loader_bridge.cc @@ -515,12 +515,16 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { params_->upload->AppendBytes(data, data_len); } - virtual void AppendFileRangeToUpload(const FilePath& file_path, - uint64 offset, uint64 length) { + virtual void AppendFileRangeToUpload( + const FilePath& file_path, + uint64 offset, + uint64 length, + const base::Time& expected_modification_time) { DCHECK(params_.get()); if (!params_->upload) params_->upload = new net::UploadData(); - params_->upload->AppendFileRange(file_path, offset, length); + params_->upload->AppendFileRange(file_path, offset, length, + expected_modification_time); } virtual void SetUploadIdentifier(int64 identifier) { diff --git a/webkit/tools/test_shell/test_shell_webkit_init.h b/webkit/tools/test_shell/test_shell_webkit_init.h index c182269..f2f314e 100644 --- a/webkit/tools/test_shell/test_shell_webkit_init.h +++ b/webkit/tools/test_shell/test_shell_webkit_init.h @@ -152,6 +152,15 @@ class TestShellWebKitInit : public webkit_glue::WebKitClientImpl { reinterpret_cast<int64*>(&result)); } + virtual bool getFileModificationTime(const WebKit::WebString& path, + double& result) { + file_util::FileInfo info; + if (!file_util::GetFileInfo(webkit_glue::WebStringToFilePath(path), &info)) + return false; + result = info.last_modified.ToDoubleT(); + return true; + } + virtual unsigned long long visitedLinkHash(const char* canonicalURL, size_t length) { return 0; |