diff options
author | stoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-08 16:53:57 +0000 |
---|---|---|
committer | stoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-08 16:53:57 +0000 |
commit | 0ce46404deb06c137b2e2c253004c43cf42e8a70 (patch) | |
tree | 5a4026ada47051c2af1ccffe105a2d68a38ce77e /chrome_frame | |
parent | 3e59bacae6bff857dfb5e3d3e8359dbde60ccb04 (diff) | |
download | chromium_src-0ce46404deb06c137b2e2c253004c43cf42e8a70.zip chromium_src-0ce46404deb06c137b2e2c253004c43cf42e8a70.tar.gz chromium_src-0ce46404deb06c137b2e2c253004c43cf42e8a70.tar.bz2 |
Move Urlmon network requests to UI thread, thus allowing reusing the bind context (and already fetched data) via INET_E_TERMINATED_BIND error code.
Make caching of data more sensible, so the UI does not freeze (the reason why url request have been moved to a background thread).
Always return INET_E_TERMINATED_BIND on OnDataAvailable when BSCF_LASTDATANOTIFICATION flag is detected, so if a subsequent DownloadToHost request comes from Chrome, bind context and already fetched data can be reused.
BUG=36694
Review URL: http://codereview.chromium.org/1523014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43953 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame')
-rw-r--r-- | chrome_frame/chrome_active_document.cc | 4 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_activex_base.h | 5 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.cc | 3 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.h | 2 | ||||
-rw-r--r-- | chrome_frame/npapi_url_request.cc | 13 | ||||
-rw-r--r-- | chrome_frame/npapi_url_request.h | 4 | ||||
-rw-r--r-- | chrome_frame/plugin_url_request.h | 61 | ||||
-rw-r--r-- | chrome_frame/test/automation_client_mock.h | 2 | ||||
-rw-r--r-- | chrome_frame/test/url_request_test.cc | 43 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request.cc | 414 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request.h | 73 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request_private.h | 114 |
12 files changed, 303 insertions, 435 deletions
diff --git a/chrome_frame/chrome_active_document.cc b/chrome_frame/chrome_active_document.cc index 80ccd23..cec1b46 100644 --- a/chrome_frame/chrome_active_document.cc +++ b/chrome_frame/chrome_active_document.cc @@ -266,8 +266,8 @@ STDMETHODIMP ChromeActiveDocument::Load(BOOL fully_avalable, return E_INVALIDARG; } - if (!is_chrome_protocol && data) { - url_fetcher_.UseRequestDataForUrl(data, url); + if (!is_chrome_protocol) { + url_fetcher_.SetInfoForUrl(url, moniker_name, bind_context); } THREAD_SAFE_UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.FullTabLaunchType", diff --git a/chrome_frame/chrome_frame_activex_base.h b/chrome_frame/chrome_frame_activex_base.h index bb516af..d43c4ea 100644 --- a/chrome_frame/chrome_frame_activex_base.h +++ b/chrome_frame/chrome_frame_activex_base.h @@ -426,14 +426,13 @@ END_MSG_MAP() BOOL& handled) { ScopedComPtr<IMoniker> moniker(reinterpret_cast<IMoniker*>(lparam)); DCHECK(moniker); + ScopedComPtr<IBindCtx> bind_context(reinterpret_cast<IBindCtx*>(wparam)); + // TODO(tommi): It looks like we might have to switch the request object // into a pass-through request object and serve up any thus far received // content and headers to IE in order to prevent what can currently happen // which is reissuing requests and turning POST into GET. if (moniker) { - ScopedComPtr<IBindCtx> bind_context; - ::CreateBindCtx(0, bind_context.Receive()); - DCHECK(bind_context); NavigateBrowserToMoniker(doc_site_, moniker, NULL, bind_context, NULL); } diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc index eed34e5..aa88def 100644 --- a/chrome_frame/chrome_frame_automation.cc +++ b/chrome_frame/chrome_frame_automation.cc @@ -1298,8 +1298,7 @@ void ChromeFrameAutomationClient::OnResponseStarted(int request_id, } void ChromeFrameAutomationClient::OnReadComplete(int request_id, - const void* buffer, int len) { - std::string data(reinterpret_cast<const char*>(buffer), len); + const std::string& data) { automation_server_->Send(new AutomationMsg_RequestData(0, tab_->handle(), request_id, data)); } diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h index c995883..2387a12 100644 --- a/chrome_frame/chrome_frame_automation.h +++ b/chrome_frame/chrome_frame_automation.h @@ -357,7 +357,7 @@ class ChromeFrameAutomationClient virtual void OnResponseStarted(int request_id, const char* mime_type, const char* headers, int size, base::Time last_modified, const std::string& redirect_url, int redirect_status); - virtual void OnReadComplete(int request_id, const void* buffer, int len); + virtual void OnReadComplete(int request_id, const std::string& data); virtual void OnResponseEnd(int request_id, const URLRequestStatus& status); virtual bool SendIPCMessage(IPC::Message* msg); diff --git a/chrome_frame/npapi_url_request.cc b/chrome_frame/npapi_url_request.cc index f928327..5306fb9 100644 --- a/chrome_frame/npapi_url_request.cc +++ b/chrome_frame/npapi_url_request.cc @@ -166,7 +166,8 @@ int NPAPIUrlRequest::OnWriteReady() { int NPAPIUrlRequest::OnWrite(void* buffer, int len) { pending_read_size_ = 0; - delegate_->OnReadComplete(id(), buffer, len); + std::string data(reinterpret_cast<char*>(buffer), len); + delegate_->OnReadComplete(id(), data); return len; } @@ -198,13 +199,13 @@ bool NPAPIUrlRequestManager::IsThreadSafe() { } void NPAPIUrlRequestManager::StartRequest(int request_id, - const ThreadSafeAutomationUrlRequest& request_info) { + const IPC::AutomationURLRequest& request_info) { scoped_refptr<NPAPIUrlRequest> new_request(new NPAPIUrlRequest(instance_)); DCHECK(new_request); if (new_request->Initialize(this, request_id, request_info.url, request_info.method, request_info.referrer, request_info.extra_request_headers, - request_info.upload_data.get()->get_data(), + request_info.upload_data, enable_frame_busting_)) { // Add to map. DCHECK(request_map_.find(request_id) == request_map_.end()); @@ -256,9 +257,9 @@ void NPAPIUrlRequestManager::OnResponseStarted(int request_id, last_modified, redirect_url, redirect_status); } -void NPAPIUrlRequestManager::OnReadComplete(int request_id, const void* buffer, - int len) { - delegate_->OnReadComplete(request_id, buffer, len); +void NPAPIUrlRequestManager::OnReadComplete(int request_id, + const std::string& data) { + delegate_->OnReadComplete(request_id, data); } void NPAPIUrlRequestManager::OnResponseEnd(int request_id, diff --git a/chrome_frame/npapi_url_request.h b/chrome_frame/npapi_url_request.h index 64817eb..a059176 100644 --- a/chrome_frame/npapi_url_request.h +++ b/chrome_frame/npapi_url_request.h @@ -36,7 +36,7 @@ class NPAPIUrlRequestManager : public PluginUrlRequestManager, // PluginUrlRequestManager implementation. Called from AutomationClient. virtual bool IsThreadSafe(); virtual void StartRequest(int request_id, - const ThreadSafeAutomationUrlRequest& request_info); + const IPC::AutomationURLRequest& request_info); virtual void ReadRequest(int request_id, int bytes_to_read); virtual void EndRequest(int request_id); virtual void DownloadRequestInHost(int request_id) { @@ -54,7 +54,7 @@ class NPAPIUrlRequestManager : public PluginUrlRequestManager, virtual void OnResponseStarted(int request_id, const char* mime_type, const char* headers, int size, base::Time last_modified, const std::string& redirect_url, int redirect_status); - virtual void OnReadComplete(int request_id, const void* buffer, int len); + virtual void OnReadComplete(int request_id, const std::string& data); virtual void OnResponseEnd(int request_id, const URLRequestStatus& status); static inline NPAPIUrlRequest* RequestFromNotifyData(void* notify_data) { diff --git a/chrome_frame/plugin_url_request.h b/chrome_frame/plugin_url_request.h index dbec76f..aea4fa8 100644 --- a/chrome_frame/plugin_url_request.h +++ b/chrome_frame/plugin_url_request.h @@ -21,67 +21,12 @@ class PluginUrlRequest; class PluginUrlRequestDelegate; class PluginUrlRequestManager; -// A thread-safe ref-counted wrapper for the non-thread-safe ref-counted -// net::UploadData. I'm trying to avoid making net::UploadData thread-safe -// as it is a widely used data structure whose performance characteristics -// I do not wish to change. -class UploadDataThreadSafe - : public base::RefCountedThreadSafe<UploadDataThreadSafe> { - public: - explicit UploadDataThreadSafe(net::UploadData* upload_data) { - DCHECK(upload_data == NULL || upload_data->HasOneRef()); - upload_data_ = upload_data; - } - - net::UploadData* get_data() { - DCHECK(upload_data_.get() == NULL || upload_data_->HasOneRef()); - return upload_data_.get(); - } - - private: - scoped_refptr<net::UploadData> upload_data_; -}; - -// A class that can be used in place of an IPC::AutomationUrlRequest but can be -// safely passed across threads. Note that this takes ownership of the -// net::UploadData instance and that the instance must have a ref_count of -// exactly 1 when this is called. -class ThreadSafeAutomationUrlRequest { - public: - // Note that the constructor mutates the "const" ipc_request parameter. - explicit ThreadSafeAutomationUrlRequest( - const IPC::AutomationURLRequest& ipc_request) - : url(ipc_request.url), method(ipc_request.method), - referrer(ipc_request.referrer), - extra_request_headers(ipc_request.extra_request_headers) { - // Make sure that we have exactly one reference when taking ownership. - net::UploadData* temp_data = const_cast<IPC::AutomationURLRequest&>( - ipc_request).upload_data.release(); - DCHECK(temp_data == NULL || temp_data->HasOneRef()); - - upload_data = new UploadDataThreadSafe(temp_data); - - if (temp_data) { - temp_data->Release(); - } - // Make sure that we have exactly one reference after taking ownership. - DCHECK(temp_data == NULL || temp_data->HasOneRef()); - } - - std::string url; - std::string method; - std::string referrer; - std::string extra_request_headers; - scoped_refptr<UploadDataThreadSafe> upload_data; -}; - - class DECLSPEC_NOVTABLE PluginUrlRequestDelegate { // NOLINT public: virtual void OnResponseStarted(int request_id, const char* mime_type, const char* headers, int size, base::Time last_modified, const std::string& redirect_url, int redirect_status) = 0; - virtual void OnReadComplete(int request_id, const void* buffer, int len) = 0; + virtual void OnReadComplete(int request_id, const std::string& data) = 0; virtual void OnResponseEnd(int request_id, const URLRequestStatus& status) = 0; virtual void AddPrivacyDataForUrl(const std::string& url, @@ -116,7 +61,7 @@ class DECLSPEC_NOVTABLE PluginUrlRequestManager { // NOLINT // derived classes. void StartUrlRequest(int tab, int request_id, const IPC::AutomationURLRequest& request_info) { - StartRequest(request_id, ThreadSafeAutomationUrlRequest(request_info)); + StartRequest(request_id, request_info); } void ReadUrlRequest(int tab, int request_id, int bytes_to_read) { @@ -151,7 +96,7 @@ class DECLSPEC_NOVTABLE PluginUrlRequestManager { // NOLINT private: virtual void StartRequest(int request_id, - const ThreadSafeAutomationUrlRequest& request_info) = 0; + const IPC::AutomationURLRequest& request_info) = 0; virtual void ReadRequest(int request_id, int bytes_to_read) = 0; virtual void EndRequest(int request_id) = 0; virtual void DownloadRequestInHost(int request_id) = 0; diff --git a/chrome_frame/test/automation_client_mock.h b/chrome_frame/test/automation_client_mock.h index 54f9d5e..d08d62d3 100644 --- a/chrome_frame/test/automation_client_mock.h +++ b/chrome_frame/test/automation_client_mock.h @@ -72,7 +72,7 @@ struct MockCFDelegate : public ChromeFrameDelegateImpl { } void ReplyData(int request_id, const std::string* data) { - request_delegate_->OnReadComplete(request_id, data->c_str(), data->size()); + request_delegate_->OnReadComplete(request_id, *data); } void Reply(const URLRequestStatus& status, int request_id) { diff --git a/chrome_frame/test/url_request_test.cc b/chrome_frame/test/url_request_test.cc index 971f239c..efdebc2 100644 --- a/chrome_frame/test/url_request_test.cc +++ b/chrome_frame/test/url_request_test.cc @@ -21,8 +21,7 @@ class MockUrlDelegate : public PluginUrlRequestDelegate { MOCK_METHOD7(OnResponseStarted, void(int request_id, const char* mime_type, const char* headers, int size, base::Time last_modified, const std::string& redirect_url, int redirect_status)); - MOCK_METHOD3(OnReadComplete, void(int request_id, const void* buffer, - int len)); + MOCK_METHOD2(OnReadComplete, void(int request_id, const std::string& data)); MOCK_METHOD2(OnResponseEnd, void(int request_id, const URLRequestStatus& status)); @@ -35,11 +34,11 @@ class MockUrlDelegate : public PluginUrlRequestDelegate { void PostponeReadRequest(chrome_frame_test::TimedMsgLoop* loop, UrlmonUrlRequest* request, int bytes_to_read) { loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(this, - &MockUrlDelegate::Read, request, bytes_to_read), 0); + &MockUrlDelegate::RequestRead, request, bytes_to_read), 0); } private: - void Read(UrlmonUrlRequest* request, int bytes_to_read) { + void RequestRead(UrlmonUrlRequest* request, int bytes_to_read) { request->Read(bytes_to_read); } }; @@ -70,7 +69,8 @@ TEST(UrlmonUrlRequestTest, Simple1) { &request, &UrlmonUrlRequest::Read, 512)))); - EXPECT_CALL(mock, OnReadComplete(1, testing::_, testing::Gt(0))) + EXPECT_CALL(mock, OnReadComplete(1, testing::Property(&std::string::size, + testing::Gt(0u)))) .Times(testing::AtLeast(1)) .WillRepeatedly(testing::InvokeWithoutArgs(CreateFunctor(&mock, &MockUrlDelegate::PostponeReadRequest, &loop, &request, 64))); @@ -113,7 +113,7 @@ TEST(UrlmonUrlRequestTest, Head) { // For HEAD requests we don't expect content reads. - EXPECT_CALL(mock, OnReadComplete(1, testing::_, testing::_)).Times(0); + EXPECT_CALL(mock, OnReadComplete(1, testing::_)).Times(0); EXPECT_CALL(mock, OnResponseEnd(1, testing::_)) .Times(1) @@ -193,6 +193,17 @@ TEST(UrlmonUrlRequestTest, ZeroLengthResponse) { server.TearDown(); } +ACTION_P4(ManagerRead, loop, mgr, request_id, bytes_to_read) { + loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(mgr, + &UrlmonUrlRequestManager::ReadUrlRequest, 0, request_id, + bytes_to_read), 0); +} +ACTION_P3(ManagerEndRequest, loop, mgr, request_id) { + loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(mgr, + &UrlmonUrlRequestManager::EndUrlRequest, 0, request_id, + URLRequestStatus()), 0); +} + // Simplest test - retrieve file from local web server. TEST(UrlmonUrlRequestManagerTest, Simple1) { MockUrlDelegate mock; @@ -207,13 +218,12 @@ TEST(UrlmonUrlRequestManagerTest, Simple1) { EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_, testing::_, testing::_, testing::_)) .Times(1) - .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(mgr.get(), - &PluginUrlRequestManager::ReadUrlRequest, 0, 1, 512))); + .WillOnce(ManagerRead(&loop, mgr.get(), 1, 512)); - EXPECT_CALL(mock, OnReadComplete(1, testing::_, testing::Gt(0))) + EXPECT_CALL(mock, OnReadComplete(1, testing::Property(&std::string::size, + testing::Gt(0u)))) .Times(testing::AtLeast(1)) - .WillRepeatedly(testing::InvokeWithoutArgs(CreateFunctor(mgr.get(), - &PluginUrlRequestManager::ReadUrlRequest, 0, 1, 2))); + .WillRepeatedly(ManagerRead(&loop, mgr.get(), 1, 2)); EXPECT_CALL(mock, OnResponseEnd(1, testing::_)) .Times(1) @@ -239,17 +249,14 @@ TEST(UrlmonUrlRequestManagerTest, Abort1) { testing::_, testing::_, testing::_)) .Times(1) .WillOnce(testing::DoAll( - testing::InvokeWithoutArgs(CreateFunctor(mgr.get(), - &PluginUrlRequestManager::EndUrlRequest, 0, 1, URLRequestStatus())), - testing::InvokeWithoutArgs(CreateFunctor(mgr.get(), - &PluginUrlRequestManager::ReadUrlRequest, 0, 1, 2)))); + ManagerEndRequest(&loop, mgr.get(), 1), + QUIT_LOOP_SOON(loop, 3))); - EXPECT_CALL(mock, OnReadComplete(1, testing::_, testing::Gt(0))) + EXPECT_CALL(mock, OnReadComplete(1, testing::_)) .Times(0); EXPECT_CALL(mock, OnResponseEnd(1, testing::_)) - .Times(1) - .WillOnce(QUIT_LOOP_SOON(loop, 2)); + .Times(0); mgr->StartUrlRequest(0, 1, r1); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc index b0641a5..783fcf0 100644 --- a/chrome_frame/urlmon_url_request.cc +++ b/chrome_frame/urlmon_url_request.cc @@ -20,25 +20,10 @@ #include "net/http/http_util.h" #include "net/http/http_response_headers.h" -static const LARGE_INTEGER kZero = {0}; -static const ULARGE_INTEGER kUnsignedZero = {0}; - -STDMETHODIMP UrlmonUrlRequest::SendStream::Write(const void * buffer, - ULONG size, - ULONG* size_written) { - DCHECK(request_); - int size_to_write = static_cast<int>( - std::min(static_cast<ULONG>(MAXINT), size)); - request_->delegate_->OnReadComplete(request_->id(), buffer, - size_to_write); - if (size_written) - *size_written = size_to_write; - return S_OK; -} - UrlmonUrlRequest::UrlmonUrlRequest() : pending_read_size_(0), headers_received_(false), + calling_delegate_(0), thread_(NULL), parent_window_(NULL), privileged_mode_(false) { @@ -59,7 +44,7 @@ bool UrlmonUrlRequest::Start() { HRESULT hr = E_UNEXPECTED; if (request_data_) { DCHECK(bind_context_ == NULL); - hr = CreateAsyncBindCtx(0, this, NULL, bind_context_.Receive()); + hr = CreateAsyncBindCtxEx(NULL, 0, this, NULL, bind_context_.Receive(), 0); DCHECK(SUCCEEDED(hr)); CComObject<SimpleBindingImpl>* binding = NULL; CComObject<SimpleBindingImpl>::CreateInstance(&binding); @@ -83,6 +68,7 @@ void UrlmonUrlRequest::Stop() { DCHECK_EQ(thread_, PlatformThread::CurrentId()); DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL)); Status::State state = status_.get_state(); + delegate_ = NULL; switch (state) { case Status::WORKING: status_.Cancel(); @@ -104,7 +90,9 @@ void UrlmonUrlRequest::Stop() { bool UrlmonUrlRequest::Read(int bytes_to_read) { DCHECK_EQ(thread_, PlatformThread::CurrentId()); - // Re-entrancy check. Thou shall not call Read() while processOnReadComplete!! + DCHECK_GE(bytes_to_read, 0); + DCHECK_EQ(0, calling_delegate_); + // Re-entrancy check. Thou shall not call Read() while process OnReadComplete! DCHECK_EQ(0, pending_read_size_); if (pending_read_size_ != 0) return false; @@ -115,12 +103,8 @@ bool UrlmonUrlRequest::Read(int bytes_to_read) { } // Send cached data if available. - CComObjectStackEx<SendStream> send_stream; - send_stream.Initialize(this); - - size_t bytes_copied = 0; - if (delegate_ && cached_data_.is_valid() && - cached_data_.Read(&send_stream, bytes_to_read, &bytes_copied)) { + if (delegate_ && cached_data_.is_valid()) { + size_t bytes_copied = SendDataToDelegate(bytes_to_read); DLOG(INFO) << StringPrintf("URL: %s Obj: %X - bytes read from cache: %d", url().c_str(), this, bytes_copied); return true; @@ -139,17 +123,35 @@ bool UrlmonUrlRequest::Read(int bytes_to_read) { return true; } -HRESULT UrlmonUrlRequest::SetRequestData(RequestData* data) { - request_data_ = data; +HRESULT UrlmonUrlRequest::UseBindCtx(IMoniker* moniker, LPBC bc) { + DCHECK(bind_context_ == NULL); + DCHECK(moniker_ == NULL); + bind_context_ = bc; + moniker_ = moniker; return S_OK; } -void UrlmonUrlRequest::StealMoniker(IMoniker** moniker) { +void UrlmonUrlRequest::StealMoniker(IMoniker** moniker, IBindCtx** bctx) { // Could be called in any thread. There should be no race // since moniker_ is not released while we are in manager's request map. DLOG(INFO) << __FUNCTION__ << " id: " << id(); DLOG_IF(WARNING, moniker == NULL) << __FUNCTION__ << " no moniker"; *moniker = moniker_.Detach(); + *bctx = bind_context_.Detach(); +} + +size_t UrlmonUrlRequest::SendDataToDelegate(size_t bytes_to_read) { + // We can optimize a bit by setting this string as a class member + // and avoid frequent memory reallocations. + std::string data; + size_t bytes_copied; + + size_t bytes = std::min(size_t(bytes_to_read), cached_data_.size()); + cached_data_.Read(WriteInto(&data, 1 + bytes), bytes, &bytes_copied); + ++calling_delegate_; + delegate_->OnReadComplete(id(), data); + --calling_delegate_; + return bytes_copied; } STDMETHODIMP UrlmonUrlRequest::OnStartBinding(DWORD reserved, @@ -239,6 +241,10 @@ STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) { // Mark we a are done. status_.Done(); + // We always return INET_E_TERMINATED_BIND from OnDataAvailable + if (result == INET_E_TERMINATED_BIND) + result = S_OK; + if (state == Status::WORKING) { status_.set_result(result); @@ -330,8 +336,7 @@ STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags, if (upload_data) { // Bypass caching proxies on POSTs and PUTs and avoid writing responses to // these requests to the browser's cache - *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE | - BINDF_PRAGMA_NO_CACHE; + *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_PRAGMA_NO_CACHE; // Initialize the STGMEDIUM. memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM)); @@ -378,17 +383,15 @@ STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size, // time or it won't be available later. Since the size of the data could // be more than pending read size, it's not straightforward (or might even // be impossible) to implement a true data pull model. - size_t bytes_available = 0; - cached_data_.Append(read_stream, &bytes_available); + size_t cached = cached_data_.size(); + cached_data_.Append(read_stream); DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << - " - Bytes read into cache: " << bytes_available; + " - Bytes read into cache: " << cached_data_.size() - cached; if (pending_read_size_ && cached_data_.is_valid()) { - CComObjectStackEx<SendStream> send_stream; - send_stream.Initialize(this); - cached_data_.Read(&send_stream, pending_read_size_, &pending_read_size_); + size_t bytes_copied = SendDataToDelegate(pending_read_size_); DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << - " - size read: " << pending_read_size_; + " - size read: " << bytes_copied; pending_read_size_ = 0; } else { DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << @@ -398,6 +401,10 @@ STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size, if (BSCF_LASTDATANOTIFICATION & flags) { DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) << " - end of data."; + + // Always return INET_E_TERMINATED_BIND to allow bind context reuse + // if DownloadToHost is suddenly requested. + return INET_E_TERMINATED_BIND; } return S_OK; @@ -629,20 +636,27 @@ HRESULT UrlmonUrlRequest::StartAsyncDownload() { DLOG(INFO) << __FUNCTION__ << StringPrintf(" this=0x%08X, tid=%i", this, ::GetCurrentThreadId()); HRESULT hr = E_FAIL; - if (moniker_.get() == NULL) { - DLOG(INFO) << "Creating a new moniker for " << url(); + DCHECK((moniker_ && bind_context_) || (!moniker_ && !bind_context_)); + + + if (!moniker_.get()) { std::wstring wide_url = UTF8ToWide(url()); hr = CreateURLMonikerEx(NULL, wide_url.c_str(), moniker_.Receive(), URL_MK_UNIFORM); if (FAILED(hr)) { NOTREACHED() << "CreateURLMonikerEx failed. Error: " << hr; - } else { - hr = CreateAsyncBindCtx(0, this, NULL, bind_context_.Receive()); - DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtx failed. Error: " << hr; + return hr; } + } + + if (bind_context_.get() == NULL) { + hr = ::CreateAsyncBindCtxEx(NULL, 0, this, NULL, + bind_context_.Receive(), 0); + DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtxEx failed. Error: " << hr; } else { - DCHECK(bind_context_.get() != NULL); - hr = S_OK; + // Use existing bind context. + hr = ::RegisterBindStatusCallback(bind_context_, this, NULL, 0); + DCHECK(SUCCEEDED(hr)) << "RegisterBindStatusCallback failed. Error: " << hr; } if (SUCCEEDED(hr)) { @@ -682,6 +696,7 @@ void UrlmonUrlRequest::NotifyDelegateAndDie() { PluginUrlRequestDelegate* delegate = delegate_; delegate_ = NULL; ReleaseBindings(); + bind_context_.Release(); if (delegate) { URLRequestStatus result = status_.get_result(); delegate->OnResponseEnd(id(), result); @@ -733,9 +748,11 @@ std::string UrlmonUrlRequest::GetHttpHeaders() const { void UrlmonUrlRequest::ReleaseBindings() { binding_.Release(); + // Do not release bind_context here! + // We may get DownloadToHost request and therefore we want the bind_context + // to be available. if (bind_context_) { ::RevokeBindStatusCallback(bind_context_, this); - bind_context_.Release(); } } @@ -743,57 +760,123 @@ void UrlmonUrlRequest::ReleaseBindings() { // UrlmonUrlRequest::Cache implementation. // -size_t UrlmonUrlRequest::Cache::Size() const { - return cache_.size(); +UrlmonUrlRequest::Cache::~Cache() { + while (cache_.size()) { + uint8* t = cache_.front(); + cache_.pop_front(); + delete [] t; + } + + while (pool_.size()) { + uint8* t = pool_.front(); + pool_.pop_front(); + delete [] t; + } } -bool UrlmonUrlRequest::Cache::Read(IStream* dest, size_t size, +void UrlmonUrlRequest::Cache::GetReadBuffer(void** src, size_t* bytes_avail) { + DCHECK_LT(read_offset_, BUF_SIZE); + *src = NULL; + *bytes_avail = 0; + if (cache_.size()) { + if (cache_.size() == 1) + *bytes_avail = write_offset_ - read_offset_; + else + *bytes_avail = BUF_SIZE - read_offset_; + + // Return non-NULL pointer only if there is some data + if (*bytes_avail) + *src = cache_.front() + read_offset_; + } +} + +void UrlmonUrlRequest::Cache::BytesRead(size_t bytes) { + DCHECK_LT(read_offset_, BUF_SIZE); + DCHECK_LE(read_offset_ + bytes, BUF_SIZE); + DCHECK_LE(bytes, size_); + + size_ -= bytes; + read_offset_ += bytes; + if (read_offset_ == BUF_SIZE) { + uint8* p = cache_.front(); + cache_.pop_front(); + // check if pool_ became too large + pool_.push_front(p); + read_offset_ = 0; + } +} + +bool UrlmonUrlRequest::Cache::Read(void* dest, size_t bytes, size_t* bytes_copied) { + void* src; + size_t src_size; + DLOG(INFO) << __FUNCTION__; - if (!dest || !size || !is_valid()) { - NOTREACHED(); - return false; - } + *bytes_copied = 0; + while (bytes) { + GetReadBuffer(&src, &src_size); + if (src_size == 0) + break; - // Copy the data to the destination stream and remove it from our cache. - size_t size_written = 0; - size_t bytes_to_write = (size <= Size() ? size : Size()); + size_t bytes_to_copy = std::min(src_size, bytes); + memcpy(dest, src, bytes_to_copy); - if (bytes_to_write) { - dest->Write(&cache_[0], bytes_to_write, - reinterpret_cast<unsigned long*>(&size_written)); // NOLINT + BytesRead(bytes_to_copy); + bytes -= bytes_to_copy; + *bytes_copied += bytes_to_copy; } - DCHECK(size_written == bytes_to_write); - cache_.erase(cache_.begin(), cache_.begin() + size_written); + return true; +} + + +void UrlmonUrlRequest::Cache::GetWriteBuffer(void** dest, size_t* bytes_avail) { + if (cache_.size() == 0 || write_offset_ == BUF_SIZE) { - if (bytes_copied) - *bytes_copied = size_written; + if (pool_.size()) { + cache_.push_back(pool_.front()); + pool_.pop_front(); + } else { + cache_.push_back(new uint8[BUF_SIZE]); + } + + write_offset_ = 0; + } + + *dest = cache_.back() + write_offset_; + *bytes_avail = BUF_SIZE - write_offset_; +} - return (size_written != 0); +void UrlmonUrlRequest::Cache::BytesWritten(size_t bytes) { + DCHECK_LE(write_offset_ + bytes, BUF_SIZE); + write_offset_ += bytes; + size_ += bytes; } -bool UrlmonUrlRequest::Cache::Append(IStream* source, - size_t* bytes_copied) { +bool UrlmonUrlRequest::Cache::Append(IStream* source) { if (!source) { NOTREACHED(); return false; } - char read_buffer[32 * 1024]; HRESULT hr = S_OK; while (SUCCEEDED(hr)) { + void* dest = 0; + size_t bytes = 0; DWORD chunk_read = 0; // NOLINT - hr = source->Read(read_buffer, sizeof(read_buffer), &chunk_read); + GetWriteBuffer(&dest, &bytes); + hr = source->Read(dest, bytes, &chunk_read); + BytesWritten(chunk_read); - if (!chunk_read) + if (hr == S_OK && chunk_read == 0) { + // implied EOF break; + } - std::copy(read_buffer, read_buffer + chunk_read, - back_inserter(cache_)); - - if (bytes_copied) - *bytes_copied += chunk_read; + if (hr == S_FALSE) { + // EOF + break; + } } return SUCCEEDED(hr); @@ -859,39 +942,18 @@ net::Error UrlmonUrlRequest::HresultToNetError(HRESULT hr) { bool UrlmonUrlRequestManager::IsThreadSafe() { - return true; + return false; } -void UrlmonUrlRequestManager::UseRequestDataForUrl(RequestData* data, - const std::wstring& url) { - DCHECK(data); - DCHECK(request_data_for_url_.get() == NULL); - request_data_for_url_.reset(new RequestDataForUrl(data, url)); +void UrlmonUrlRequestManager::SetInfoForUrl(const std::wstring& url, + IMoniker* moniker, LPBC bind_ctx) { + url_info_.Set(url, moniker, bind_ctx); } void UrlmonUrlRequestManager::StartRequest(int request_id, - const ThreadSafeAutomationUrlRequest& request_info) { + const IPC::AutomationURLRequest& request_info) { DLOG(INFO) << __FUNCTION__; - RequestDataForUrl* use_request = NULL; - if (request_data_for_url_.get()) { - if (GURL(request_data_for_url_->url()) == GURL(request_info.url)) { - use_request = request_data_for_url_.release(); - } - } - - bool posted = ExecuteInWorkerThread(FROM_HERE, NewRunnableMethod(this, - &UrlmonUrlRequestManager::StartRequestWorker, - request_id, request_info, use_request)); - if (!posted) { - delete use_request; - } -} - -void UrlmonUrlRequestManager::StartRequestWorker(int request_id, - const ThreadSafeAutomationUrlRequest& request_info, - RequestDataForUrl* use_request) { - DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); - scoped_ptr<RequestDataForUrl> request_for_url(use_request); + DCHECK_EQ(0, calling_delegate_); if (stopping_) return; @@ -907,32 +969,24 @@ void UrlmonUrlRequestManager::StartRequestWorker(int request_id, request_info.method, request_info.referrer, request_info.extra_request_headers, - request_info.upload_data->get_data(), + request_info.upload_data, enable_frame_busting_); new_request->set_parent_window(notification_window_); new_request->set_privileged_mode(privileged_mode_); // Shall we use previously fetched data? - if (request_for_url.get()) { - new_request->SetRequestData(request_for_url->request_data()); + if (url_info_.IsForUrl(request_info.url)) { + new_request->UseBindCtx(url_info_.moniker_, url_info_.bind_ctx_); + url_info_.Clear(); } - DCHECK(LookupRequest(request_id).get() == NULL); request_map_[request_id] = new_request; - map_empty_.Reset(); new_request->Start(); } void UrlmonUrlRequestManager::ReadRequest(int request_id, int bytes_to_read) { DLOG(INFO) << __FUNCTION__ << " id: " << request_id; - ExecuteInWorkerThread(FROM_HERE, NewRunnableMethod(this, - &UrlmonUrlRequestManager::ReadRequestWorker, request_id, bytes_to_read)); -} - -void UrlmonUrlRequestManager::ReadRequestWorker(int request_id, - int bytes_to_read) { - DLOG(INFO) << __FUNCTION__ << " id: " << request_id; - DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); + DCHECK_EQ(0, calling_delegate_); scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id); // if zero, it may just have had network error. if (request) { @@ -940,25 +994,21 @@ void UrlmonUrlRequestManager::ReadRequestWorker(int request_id, } } -void UrlmonUrlRequestManager::EndRequest(int request_id) { - DLOG(INFO) << __FUNCTION__ << " id: " << request_id; - ExecuteInWorkerThread(FROM_HERE, NewRunnableMethod(this, - &UrlmonUrlRequestManager::EndRequestWorker, request_id)); -} - void UrlmonUrlRequestManager::DownloadRequestInHost(int request_id) { DLOG(INFO) << __FUNCTION__ << " " << request_id; if (IsWindow(notification_window_)) { scoped_refptr<UrlmonUrlRequest> request(LookupRequest(request_id)); if (request) { ScopedComPtr<IMoniker> moniker; - request->StealMoniker(moniker.Receive()); + ScopedComPtr<IBindCtx> bind_context; + request->StealMoniker(moniker.Receive(), bind_context.Receive()); DLOG_IF(ERROR, moniker == NULL) << __FUNCTION__ << " No moniker!"; if (moniker) { // We use SendMessage and not PostMessage to make sure that if the // notification window does not handle the message we won't leak // the moniker. - ::SendMessage(notification_window_, WM_DOWNLOAD_IN_HOST, 0, + ::SendMessage(notification_window_, WM_DOWNLOAD_IN_HOST, + reinterpret_cast<WPARAM>(bind_context.get()), reinterpret_cast<LPARAM>(moniker.get())); } } @@ -1045,67 +1095,29 @@ bool UrlmonUrlRequestManager::SetCookiesForUrl(int tab_handle, return true; } -void UrlmonUrlRequestManager::EndRequestWorker(int request_id) { +void UrlmonUrlRequestManager::EndRequest(int request_id) { DLOG(INFO) << __FUNCTION__ << " id: " << request_id; - DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); + DCHECK_EQ(0, calling_delegate_); scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id); if (request) { + request_map_.erase(request_id); request->Stop(); } } void UrlmonUrlRequestManager::StopAll() { DLOG(INFO) << __FUNCTION__; - do { - AutoLock lock(worker_thread_access_); - if (stopping_) - return; - - stopping_ = true; - - if (!worker_thread_.IsRunning()) - return; - - // ExecuteInWorkerThread will check for stopping_. Hence post directly - // to the worker thread. - worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, - &UrlmonUrlRequestManager::StopAllWorker)); - } while (0); - - // Note we may not call worker_thread_.Stop() here. The MessageLoop's quit - // task will be serialized after request::Stop tasks, but requests may - // not quit immediately. CoUninitialize has a modal message loop, but it - // does not help in this case. - // Normally we call binding->Abort() and expect OnStopBinding() callback - // where we inform UrlmonUrlRequestManager that request is dead. - // The problem is that while waiting for OnStopBinding(), Quit Task may be - // picked up and executed, thus exiting the thread. - map_empty_.Wait(); - - worker_thread_access_.Acquire(); - worker_thread_.Stop(); - worker_thread_access_.Release(); -} - -void UrlmonUrlRequestManager::StopAllWorker() { - DLOG(INFO) << __FUNCTION__; - DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); - DCHECK_EQ(true, stopping_); + if (stopping_) + return; - std::vector<scoped_refptr<UrlmonUrlRequest> > request_list; - // We copy the pending requests into a temporary vector as the Stop - // function in the request could also try to delete the request from - // the request map and the iterator could end up being invalid. + stopping_ = true; for (RequestMap::iterator it = request_map_.begin(); it != request_map_.end(); ++it) { DCHECK(it->second != NULL); - request_list.push_back(it->second); + it->second->Stop(); } - for (std::vector<scoped_refptr<UrlmonUrlRequest> >::size_type index = 0; - index < request_list.size(); ++index) { - request_list[index]->Stop(); - } + request_map_.empty(); } void UrlmonUrlRequestManager::OnResponseStarted(int request_id, @@ -1113,33 +1125,31 @@ void UrlmonUrlRequestManager::OnResponseStarted(int request_id, base::Time last_modified, const std::string& redirect_url, int redirect_status) { DLOG(INFO) << __FUNCTION__; - DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); DCHECK(LookupRequest(request_id).get() != NULL); + ++calling_delegate_; delegate_->OnResponseStarted(request_id, mime_type, headers, size, last_modified, redirect_url, redirect_status); + --calling_delegate_; } -void UrlmonUrlRequestManager::OnReadComplete(int request_id, const void* buffer, - int len) { +void UrlmonUrlRequestManager::OnReadComplete(int request_id, + const std::string& data) { DLOG(INFO) << __FUNCTION__; - DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); DCHECK(LookupRequest(request_id).get() != NULL); - delegate_->OnReadComplete(request_id, buffer, len); + ++calling_delegate_; + delegate_->OnReadComplete(request_id, data); + --calling_delegate_; } void UrlmonUrlRequestManager::OnResponseEnd(int request_id, const URLRequestStatus& status) { DLOG(INFO) << __FUNCTION__; - DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId()); + DCHECK(status.status() != URLRequestStatus::CANCELED); RequestMap::size_type n = request_map_.erase(request_id); DCHECK_EQ(1, n); - - if (request_map_.size() == 0) - map_empty_.Signal(); - - // Inform delegate unless the request has been explicitly cancelled. - if (status.status() != URLRequestStatus::CANCELED) - delegate_->OnResponseEnd(request_id, status); + ++calling_delegate_; + delegate_->OnResponseEnd(request_id, status); + --calling_delegate_; } scoped_refptr<UrlmonUrlRequest> UrlmonUrlRequestManager::LookupRequest( @@ -1151,8 +1161,7 @@ scoped_refptr<UrlmonUrlRequest> UrlmonUrlRequestManager::LookupRequest( } UrlmonUrlRequestManager::UrlmonUrlRequestManager() - : stopping_(false), worker_thread_("UrlMon fetch thread"), - map_empty_(true, true), notification_window_(NULL), + : stopping_(false), calling_delegate_(0), notification_window_(NULL), privileged_mode_(false) { } @@ -1160,46 +1169,27 @@ UrlmonUrlRequestManager::~UrlmonUrlRequestManager() { StopAll(); } -bool UrlmonUrlRequestManager::ExecuteInWorkerThread( - const tracked_objects::Location& from_here, Task* task) { - AutoLock lock(worker_thread_access_); - if (stopping_) { - delete task; - return false; - } - - if (!worker_thread_.IsRunning()) - worker_thread_.Start(); - - worker_thread_.message_loop()->PostTask(from_here, task); - return true; -} - void UrlmonUrlRequestManager::AddPrivacyDataForUrl( const std::string& url, const std::string& policy_ref, int32 flags) { bool fire_privacy_event = false; - { - AutoLock lock(privacy_info_lock_); + if (privacy_info_.privacy_records.size() == 0) + flags |= PRIVACY_URLISTOPLEVEL; - if (privacy_info_.privacy_records.size() == 0) - flags |= PRIVACY_URLISTOPLEVEL; - - if (!privacy_info_.privacy_impacted) { - if (flags & (COOKIEACTION_ACCEPT | COOKIEACTION_REJECT | - COOKIEACTION_DOWNGRADE)) { - privacy_info_.privacy_impacted = true; - fire_privacy_event = true; - } + if (!privacy_info_.privacy_impacted) { + if (flags & (COOKIEACTION_ACCEPT | COOKIEACTION_REJECT | + COOKIEACTION_DOWNGRADE)) { + privacy_info_.privacy_impacted = true; + fire_privacy_event = true; } + } - PrivacyInfo::PrivacyEntry& privacy_entry = - privacy_info_.privacy_records[UTF8ToWide(url)]; + PrivacyInfo::PrivacyEntry& privacy_entry = + privacy_info_.privacy_records[UTF8ToWide(url)]; - privacy_entry.flags |= flags; - privacy_entry.policy_ref = UTF8ToWide(policy_ref); - } + privacy_entry.flags |= flags; + privacy_entry.policy_ref = UTF8ToWide(policy_ref); if (fire_privacy_event && IsWindow(notification_window_)) { PostMessage(notification_window_, WM_FIRE_PRIVACY_CHANGE_NOTIFICATION, 1, diff --git a/chrome_frame/urlmon_url_request.h b/chrome_frame/urlmon_url_request.h index 189f64b..7e45123 100644 --- a/chrome_frame/urlmon_url_request.h +++ b/chrome_frame/urlmon_url_request.h @@ -21,27 +21,6 @@ class UrlmonUrlRequest; -class RequestDataForUrl { - public: - RequestDataForUrl(RequestData* data, const std::wstring& url) - : request_data_(data), url_(url) { - } - ~RequestDataForUrl() { - } - - const std::wstring& url() const { - return url_; - } - - RequestData* request_data() const { - return request_data_; - } - - protected: - scoped_refptr<RequestData> request_data_; - std::wstring url_; -}; - class UrlmonUrlRequestManager : public PluginUrlRequestManager, public PluginUrlRequestDelegate { @@ -66,13 +45,13 @@ class UrlmonUrlRequestManager UrlmonUrlRequestManager(); ~UrlmonUrlRequestManager(); - // Use cached data when Chrome request this url. + // Use specific bind context when Chrome request this url. // Used from ChromeActiveDocument's implementation of IPersistMoniker::Load(). - void UseRequestDataForUrl(RequestData* data, const std::wstring& url); + void SetInfoForUrl(const std::wstring& url, + IMoniker* moniker, LPBC bind_context); // Returns a copy of the url privacy information for this instance. PrivacyInfo privacy_info() { - AutoLock lock(privacy_info_lock_); return privacy_info_; } @@ -101,7 +80,7 @@ class UrlmonUrlRequestManager // PluginUrlRequestManager implementation. virtual bool IsThreadSafe(); virtual void StartRequest(int request_id, - const ThreadSafeAutomationUrlRequest& request_info); + const IPC::AutomationURLRequest& request_info); virtual void ReadRequest(int request_id, int bytes_to_read); virtual void EndRequest(int request_id); virtual void DownloadRequestInHost(int request_id); @@ -118,33 +97,41 @@ class UrlmonUrlRequestManager base::Time last_modified, const std::string& redirect_url, int redirect_status); - virtual void OnReadComplete(int request_id, const void* buffer, int len); + virtual void OnReadComplete(int request_id, const std::string& data); virtual void OnResponseEnd(int request_id, const URLRequestStatus& status); - bool ExecuteInWorkerThread(const tracked_objects::Location& from_here, - Task* task); - // Methods executed in worker thread. - void StartRequestWorker(int request_id, - const ThreadSafeAutomationUrlRequest& request_info, - RequestDataForUrl* use_request); - void ReadRequestWorker(int request_id, int bytes_to_read); - void EndRequestWorker(int request_id); - void StopAllWorker(); - // Map for (request_id <-> UrlmonUrlRequest) typedef std::map<int, scoped_refptr<UrlmonUrlRequest> > RequestMap; RequestMap request_map_; scoped_refptr<UrlmonUrlRequest> LookupRequest(int request_id); - scoped_ptr<RequestDataForUrl> request_data_for_url_; - STAThread worker_thread_; - base::WaitableEvent map_empty_; + struct UrlInfo { + void Clear() { + url_ = GURL::EmptyGURL(); + bind_ctx_.Release(); + moniker_.Release(); + } + + void Set(const std::wstring& url, IMoniker* moniker, LPBC bc) { + DCHECK(bind_ctx_.get() == NULL); + DCHECK(moniker_.get() == NULL); + url_ = GURL(url); + moniker_ = moniker; + bind_ctx_ = bc; + } + + bool IsForUrl(const std::string& url) { + return GURL(url) == url_; + } + + GURL url_; + ScopedComPtr<IBindCtx> bind_ctx_; + ScopedComPtr<IMoniker> moniker_; + } url_info_; + bool stopping_; - Lock worker_thread_access_; + int calling_delegate_; // re-entrancy protection (debug only check) - // This lock is used to synchronize access to the PrivacyInfo data structure - // as it can be accessed from the ui thread and the worker thread. - Lock privacy_info_lock_; PrivacyInfo privacy_info_; // The window to be used to fire notifications on. HWND notification_window_; diff --git a/chrome_frame/urlmon_url_request_private.h b/chrome_frame/urlmon_url_request_private.h index 596c099c..5ae3da3 100644 --- a/chrome_frame/urlmon_url_request_private.h +++ b/chrome_frame/urlmon_url_request_private.h @@ -8,7 +8,7 @@ #include <atlbase.h> #include <atlcom.h> #include <string> -#include <vector> +#include <deque> #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" @@ -30,10 +30,10 @@ class UrlmonUrlRequest virtual bool Read(int bytes_to_read); // Special function needed by ActiveDocument::Load() - HRESULT SetRequestData(RequestData* data); + HRESULT UseBindCtx(IMoniker* moniker, LPBC bc); // Used from "DownloadRequestInHost". - void StealMoniker(IMoniker** moniker); + void StealMoniker(IMoniker** moniker, IBindCtx** bctx); // Parent Window for UrlMon error dialogs void set_parent_window(HWND parent_window) { @@ -98,104 +98,42 @@ class UrlmonUrlRequest protected: void ReleaseBindings(); - // A fake stream class to make it easier to copy received data using - // IStream::CopyTo instead of allocating temporary buffers and keeping - // track of data copied so far. - class SendStream : public CComObjectRoot, public IStream { - public: - SendStream() { - } - - BEGIN_COM_MAP(SendStream) - COM_INTERFACE_ENTRY(IStream) - COM_INTERFACE_ENTRY(ISequentialStream) - END_COM_MAP() - - void Initialize(UrlmonUrlRequest* request) { - request_ = request; - } - - STDMETHOD(Write)(const void * buffer, ULONG size, ULONG* size_written); - STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(Seek)(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER* new_pos) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(SetSize)(ULARGE_INTEGER new_size) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(CopyTo)(IStream* stream, ULARGE_INTEGER cb, ULARGE_INTEGER* read, - ULARGE_INTEGER* written) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(Commit)(DWORD flags) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(Revert)() { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(LockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb, - DWORD type) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(UnlockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb, - DWORD type) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(Stat)(STATSTG *pstatstg, DWORD grfStatFlag) { - return E_NOTIMPL; - } - - STDMETHOD(Clone)(IStream** stream) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - protected: - scoped_refptr<UrlmonUrlRequest> request_; - DISALLOW_COPY_AND_ASSIGN(SendStream); - }; - // Manage data caching. Note: this class supports cache // size less than 2GB class Cache { public: - Cache() { + Cache() : size_(0), read_offset_(0), write_offset_(0) { } - // Adds data to the end of the cache. - bool Append(IStream* source, size_t* bytes_copied); + ~Cache(); - // Reads from the cache. - bool Read(IStream* dest, size_t size, size_t* bytes_copied); + // Adds data to the end of the cache. + bool Append(IStream* source); + // Copies up to |bytes| bytes from the cache to |dest| buffer. + bool Read(void* dest, size_t bytes, size_t* bytes_copied); // Returns the size of the cache. - size_t Size() const; + size_t size() const { + return size_; + } // Returns true if the cache has valid data. bool is_valid() const { - return Size() != 0; + return size() != 0; } - protected: - std::vector<byte> cache_; + private: + void GetWriteBuffer(void** dest, size_t* bytes_avail); + void BytesWritten(size_t bytes); + void GetReadBuffer(void** src, size_t* bytes_avail); + void BytesRead(size_t bytes); + + static const size_t BUF_SIZE = 0x8000; // 32k + std::deque<uint8*> cache_; + size_t size_; + size_t read_offset_; + size_t write_offset_; + std::deque<uint8*> pool_; }; HRESULT StartAsyncDownload(); @@ -205,6 +143,7 @@ class UrlmonUrlRequest static net::Error HresultToNetError(HRESULT hr); private: + size_t SendDataToDelegate(size_t bytes); // This class simplifies tracking the progress of operation. We have 3 main // states: DONE, WORKING and ABORTING. // When in [DONE] or [ABORTING] state, there is additional information @@ -309,6 +248,7 @@ class UrlmonUrlRequest PlatformThreadId thread_; HWND parent_window_; bool headers_received_; + int calling_delegate_; // re-entrancy protection. // Set to true if the ChromeFrame instance is running in privileged mode. bool privileged_mode_; DISALLOW_COPY_AND_ASSIGN(UrlmonUrlRequest); |