Index: chrome_frame/urlmon_url_request.cc |
diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc |
deleted file mode 100644 |
index e888ff6fc6892152bcfcdc1b095351d872c2af56..0000000000000000000000000000000000000000 |
--- a/chrome_frame/urlmon_url_request.cc |
+++ /dev/null |
@@ -1,959 +0,0 @@ |
-// 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 "chrome_frame/urlmon_url_request.h" |
- |
-#include <urlmon.h> |
-#include <wininet.h> |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/logging.h" |
-#include "base/memory/scoped_ptr.h" |
-#include "base/message_loop/message_loop.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "base/threading/platform_thread.h" |
-#include "base/threading/thread.h" |
-#include "chrome/common/automation_messages.h" |
-#include "chrome_frame/bind_context_info.h" |
-#include "chrome_frame/chrome_frame_activex_base.h" |
-#include "chrome_frame/extra_system_apis.h" |
-#include "chrome_frame/html_utils.h" |
-#include "chrome_frame/urlmon_upload_data_stream.h" |
-#include "chrome_frame/urlmon_url_request_private.h" |
-#include "chrome_frame/utils.h" |
-#include "net/base/load_flags.h" |
-#include "net/http/http_response_headers.h" |
-#include "net/http/http_util.h" |
- |
-#define IS_HTTP_SUCCESS_CODE(code) (code >= 200 && code <= 299) |
- |
-UrlmonUrlRequest::UrlmonUrlRequest() |
- : pending_read_size_(0), |
- headers_received_(false), |
- calling_delegate_(0), |
- thread_(NULL), |
- parent_window_(NULL), |
- privileged_mode_(false), |
- pending_(false), |
- is_expecting_download_(true), |
- cleanup_transaction_(false) { |
- DVLOG(1) << __FUNCTION__ << me(); |
-} |
- |
-UrlmonUrlRequest::~UrlmonUrlRequest() { |
- DVLOG(1) << __FUNCTION__ << me(); |
-} |
- |
-std::string UrlmonUrlRequest::me() const { |
- return base::StringPrintf(" id: %i Obj: %X ", id(), this); |
-} |
- |
-bool UrlmonUrlRequest::Start() { |
- DVLOG(1) << __FUNCTION__ << me() << url(); |
- DCHECK(thread_ == 0 || thread_ == base::PlatformThread::CurrentId()); |
- thread_ = base::PlatformThread::CurrentId(); |
- status_.Start(); |
- // Initialize the net::HostPortPair structure from the url initially. We may |
- // not receive the ip address of the host if the request is satisfied from |
- // the cache. |
- socket_address_ = net::HostPortPair::FromURL(GURL(url())); |
- // The UrlmonUrlRequest instance can get destroyed in the context of |
- // StartAsyncDownload if BindToStorage finishes synchronously with an error. |
- // Grab a reference to protect against this. |
- scoped_refptr<UrlmonUrlRequest> ref(this); |
- HRESULT hr = StartAsyncDownload(); |
- if (FAILED(hr) && status_.get_state() != UrlmonUrlRequest::Status::DONE) { |
- status_.Done(); |
- status_.set_result(net::URLRequestStatus::FAILED, HresultToNetError(hr)); |
- NotifyDelegateAndDie(); |
- } |
- return true; |
-} |
- |
-void UrlmonUrlRequest::Stop() { |
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); |
- DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL)); |
- Status::State state = status_.get_state(); |
- delegate_ = NULL; |
- |
- // If DownloadInHost is already requested, we will quit soon anyway. |
- if (terminate_requested()) |
- return; |
- |
- switch (state) { |
- case Status::WORKING: |
- status_.Cancel(); |
- if (binding_) |
- binding_->Abort(); |
- break; |
- |
- case Status::ABORTING: |
- status_.Cancel(); |
- break; |
- |
- case Status::DONE: |
- status_.Cancel(); |
- NotifyDelegateAndDie(); |
- break; |
- } |
-} |
- |
-bool UrlmonUrlRequest::Read(int bytes_to_read) { |
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); |
- DCHECK_GE(bytes_to_read, 0); |
- DCHECK_EQ(0, calling_delegate_); |
- DVLOG(1) << __FUNCTION__ << me(); |
- |
- is_expecting_download_ = false; |
- |
- // Re-entrancy check. Thou shall not call Read() while process OnReadComplete! |
- DCHECK_EQ(0u, pending_read_size_); |
- if (pending_read_size_ != 0) |
- return false; |
- |
- DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL)); |
- if (status_.get_state() == Status::ABORTING) |
- return true; |
- |
- // Send data if available. |
- size_t bytes_copied = 0; |
- if ((bytes_copied = SendDataToDelegate(bytes_to_read))) { |
- DVLOG(1) << __FUNCTION__ << me() << " bytes read: " << bytes_copied; |
- return true; |
- } |
- |
- if (status_.get_state() == Status::WORKING) { |
- DVLOG(1) << __FUNCTION__ << me() << " pending: " << bytes_to_read; |
- pending_read_size_ = bytes_to_read; |
- } else { |
- DVLOG(1) << __FUNCTION__ << me() << " Response finished."; |
- NotifyDelegateAndDie(); |
- } |
- |
- return true; |
-} |
- |
-HRESULT UrlmonUrlRequest::InitPending(const GURL& url, IMoniker* moniker, |
- IBindCtx* bind_context, |
- bool enable_frame_busting, |
- bool privileged_mode, |
- HWND notification_window, |
- IStream* cache) { |
- DVLOG(1) << __FUNCTION__ << me() << url.spec(); |
- DCHECK(bind_context_ == NULL); |
- DCHECK(moniker_ == NULL); |
- DCHECK(cache_ == NULL); |
- DCHECK(thread_ == 0 || thread_ == base::PlatformThread::CurrentId()); |
- thread_ = base::PlatformThread::CurrentId(); |
- bind_context_ = bind_context; |
- moniker_ = moniker; |
- enable_frame_busting_ = enable_frame_busting; |
- privileged_mode_ = privileged_mode; |
- parent_window_ = notification_window; |
- cache_ = cache; |
- set_url(url.spec()); |
- set_pending(true); |
- |
- // Request has already started and data is fetched. We will get the |
- // GetBindInfo call as per contract but the return values are |
- // ignored. So just set "get" as a method to make our GetBindInfo |
- // implementation happy. |
- method_ = "get"; |
- return S_OK; |
-} |
- |
-void UrlmonUrlRequest::TerminateBind(const TerminateBindCallback& callback) { |
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); |
- DVLOG(1) << __FUNCTION__ << me(); |
- cleanup_transaction_ = false; |
- if (status_.get_state() == Status::DONE) { |
- // Binding is stopped. Note result could be an error. |
- callback.Run(moniker_, bind_context_, upload_data_, |
- request_headers_.c_str()); |
- } else { |
- // WORKING (ABORTING?). Save the callback. |
- // Now we will return INET_TERMINATE_BIND from ::OnDataAvailable() and in |
- // ::OnStopBinding will invoke the callback passing our moniker and |
- // bind context. |
- terminate_bind_callback_ = callback; |
- if (pending_data_) { |
- // For downloads to work correctly, we must induce a call to |
- // OnDataAvailable so that we can download INET_E_TERMINATED_BIND and |
- // get IE into the correct state. |
- // To accomplish this we read everything that's readily available in |
- // the current stream. Once we've reached the end of the stream we |
- // should get E_PENDING back and then later we'll get that call |
- // to OnDataAvailable. |
- std::string data; |
- base::win::ScopedComPtr<IStream> read_stream(pending_data_); |
- HRESULT hr; |
- while ((hr = ReadStream(read_stream, 0xffff, &data)) == S_OK) { |
- // Just drop the data. |
- } |
- DLOG_IF(WARNING, hr != E_PENDING) << __FUNCTION__ << |
- base::StringPrintf(" expected E_PENDING but got 0x%08X", hr); |
- } |
- } |
-} |
- |
-size_t UrlmonUrlRequest::SendDataToDelegate(size_t bytes_to_read) { |
- return 0; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::OnStartBinding(DWORD reserved, |
- IBinding* binding) { |
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); |
- binding_ = binding; |
- if (pending_) { |
- response_headers_ = GetHttpHeadersFromBinding(binding_); |
- DCHECK(!response_headers_.empty()); |
- } |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::GetPriority(LONG *priority) { |
- if (!priority) |
- return E_POINTER; |
- *priority = THREAD_PRIORITY_NORMAL; |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) { |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress, |
- ULONG status_code, LPCWSTR status_text) { |
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); |
- |
- if (status_.get_state() != Status::WORKING) |
- return S_OK; |
- |
- // Ignore any notifications received while we are in the pending state |
- // waiting for the request to be initiated by Chrome. |
- if (pending_ && status_code != BINDSTATUS_REDIRECTING) |
- return S_OK; |
- |
- if (!delegate_) { |
- DVLOG(1) << "Invalid delegate"; |
- return S_OK; |
- } |
- |
- switch (status_code) { |
- case BINDSTATUS_CONNECTING: { |
- if (status_text) { |
- socket_address_.set_host(base::WideToUTF8(status_text)); |
- } |
- break; |
- } |
- |
- case BINDSTATUS_REDIRECTING: { |
- // If we receive a redirect for the initial pending request initiated |
- // when our document loads we should stash it away and inform Chrome |
- // accordingly when it requests data for the original URL. |
- base::win::ScopedComPtr<BindContextInfo> info; |
- BindContextInfo::FromBindContext(bind_context_, info.Receive()); |
- DCHECK(info); |
- GURL previously_redirected(info ? info->GetUrl() : std::wstring()); |
- if (GURL(status_text) != previously_redirected) { |
- DVLOG(1) << __FUNCTION__ << me() << "redirect from " << url() |
- << " to " << status_text; |
- // Fetch the redirect status as they aren't all equal (307 in particular |
- // retains the HTTP request verb). |
- int http_code = GetHttpResponseStatusFromBinding(binding_); |
- status_.SetRedirected(http_code, base::WideToUTF8(status_text)); |
- // Abort. We will inform Chrome in OnStopBinding callback. |
- binding_->Abort(); |
- return E_ABORT; |
- } |
- break; |
- } |
- |
- case BINDSTATUS_COOKIE_SENT: |
- delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_READ); |
- break; |
- |
- case BINDSTATUS_COOKIE_SUPPRESSED: |
- delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_SUPPRESS); |
- break; |
- |
- case BINDSTATUS_COOKIE_STATE_ACCEPT: |
- delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_ACCEPT); |
- break; |
- |
- case BINDSTATUS_COOKIE_STATE_REJECT: |
- delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_REJECT); |
- break; |
- |
- case BINDSTATUS_COOKIE_STATE_LEASH: |
- delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_LEASH); |
- break; |
- |
- case BINDSTATUS_COOKIE_STATE_DOWNGRADE: |
- delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_DOWNGRADE); |
- break; |
- |
- case BINDSTATUS_COOKIE_STATE_UNKNOWN: |
- NOTREACHED() << L"Unknown cookie state received"; |
- break; |
- |
- default: |
- DVLOG(1) << __FUNCTION__ << me() |
- << base::StringPrintf(L"code: %i status: %ls", status_code, |
- status_text); |
- break; |
- } |
- |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) { |
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); |
- DVLOG(1) << __FUNCTION__ << me() |
- << "- Request stopped, Result: " << std::hex << result; |
- DCHECK(status_.get_state() == Status::WORKING || |
- status_.get_state() == Status::ABORTING); |
- |
- Status::State state = status_.get_state(); |
- |
- // Mark we a are done. |
- status_.Done(); |
- |
- if (result == INET_E_TERMINATED_BIND) { |
- if (terminate_requested()) { |
- terminate_bind_callback_.Run(moniker_, bind_context_, upload_data_, |
- request_headers_.c_str()); |
- } else { |
- cleanup_transaction_ = true; |
- } |
- // We may have returned INET_E_TERMINATED_BIND from OnDataAvailable. |
- result = S_OK; |
- } |
- |
- if (state == Status::WORKING) { |
- status_.set_result(result); |
- |
- if (FAILED(result)) { |
- int http_code = GetHttpResponseStatusFromBinding(binding_); |
- // For certain requests like empty POST requests the server can return |
- // back a HTTP success code in the range 200 to 299. We need to flag |
- // these requests as succeeded. |
- if (IS_HTTP_SUCCESS_CODE(http_code)) { |
- // If this DCHECK fires it means that the server returned a HTTP |
- // success code outside the standard range 200-206. We need to confirm |
- // if the following code path is correct. |
- DCHECK_LE(http_code, 206); |
- status_.set_result(S_OK); |
- std::string headers = GetHttpHeadersFromBinding(binding_); |
- OnResponse(0, base::UTF8ToWide(headers).c_str(), NULL, NULL); |
- } else if (net::HttpResponseHeaders::IsRedirectResponseCode(http_code) && |
- result == E_ACCESSDENIED) { |
- // Special case. If the last request was a redirect and the current OS |
- // error value is E_ACCESSDENIED, that means an unsafe redirect was |
- // attempted. In that case, correct the OS error value to be the more |
- // specific ERR_UNSAFE_REDIRECT error value. |
- status_.set_result(net::URLRequestStatus::FAILED, |
- net::ERR_UNSAFE_REDIRECT); |
- } |
- } |
- |
- // The code below seems easy but it is not. :) |
- // The network policy in Chrome network is that error code/end_of_stream |
- // should be returned only as a result of read (or start) request. |
- // Here are the possible cases: |
- // pending_data_|pending_read |
- // FALSE |FALSE => EndRequest if no headers, otherwise wait for Read. |
- // FALSE |TRUE => EndRequest. |
- // TRUE |FALSE => Wait for Read. |
- // TRUE |TRUE => Something went wrong!! |
- |
- if (pending_data_) { |
- DCHECK_EQ(pending_read_size_, 0UL); |
- ReleaseBindings(); |
- return S_OK; |
- } |
- |
- if (headers_received_ && pending_read_size_ == 0) { |
- ReleaseBindings(); |
- return S_OK; |
- } |
- |
- // No headers or there is a pending read from Chrome. |
- NotifyDelegateAndDie(); |
- return S_OK; |
- } |
- |
- // Status::ABORTING |
- if (status_.was_redirected()) { |
- // Just release bindings here. Chrome will issue EndRequest(request_id) |
- // after processing headers we had provided. |
- if (!pending_) { |
- std::string headers = GetHttpHeadersFromBinding(binding_); |
- OnResponse(0, base::UTF8ToWide(headers).c_str(), NULL, NULL); |
- } |
- ReleaseBindings(); |
- return S_OK; |
- } |
- |
- // Stop invoked. |
- NotifyDelegateAndDie(); |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags, |
- BINDINFO* bind_info) { |
- if ((bind_info == NULL) || (bind_info->cbSize == 0) || (bind_flags == NULL)) |
- return E_INVALIDARG; |
- |
- *bind_flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA; |
- |
- bind_info->dwOptionsFlags = INTERNET_FLAG_NO_AUTO_REDIRECT; |
- bind_info->dwOptions = BINDINFO_OPTIONS_WININETFLAG; |
- |
- // TODO(ananta) |
- // Look into whether the other load flags need to be supported in chrome |
- // frame. |
- if (load_flags_ & net::LOAD_VALIDATE_CACHE) |
- *bind_flags |= BINDF_RESYNCHRONIZE; |
- |
- if (load_flags_ & net::LOAD_BYPASS_CACHE) |
- *bind_flags |= BINDF_GETNEWESTVERSION; |
- |
- if (LowerCaseEqualsASCII(method(), "get")) { |
- bind_info->dwBindVerb = BINDVERB_GET; |
- } else if (LowerCaseEqualsASCII(method(), "post")) { |
- bind_info->dwBindVerb = BINDVERB_POST; |
- } else if (LowerCaseEqualsASCII(method(), "put")) { |
- bind_info->dwBindVerb = BINDVERB_PUT; |
- } else { |
- std::wstring verb(base::ASCIIToWide(StringToUpperASCII(method()))); |
- bind_info->dwBindVerb = BINDVERB_CUSTOM; |
- bind_info->szCustomVerb = reinterpret_cast<wchar_t*>( |
- ::CoTaskMemAlloc((verb.length() + 1) * sizeof(wchar_t))); |
- lstrcpyW(bind_info->szCustomVerb, verb.c_str()); |
- } |
- |
- if (bind_info->dwBindVerb == BINDVERB_POST || |
- bind_info->dwBindVerb == BINDVERB_PUT || |
- post_data_len() > 0) { |
- // Bypass caching proxies on upload requests and avoid writing responses to |
- // the browser's cache. |
- *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_PRAGMA_NO_CACHE; |
- |
- // Attempt to avoid storing the response for upload requests. |
- // See http://crbug.com/55918 |
- if (resource_type_ != ResourceType::MAIN_FRAME) |
- *bind_flags |= BINDF_NOWRITECACHE; |
- |
- // Initialize the STGMEDIUM. |
- memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM)); |
- bind_info->grfBindInfoF = 0; |
- |
- if (bind_info->dwBindVerb != BINDVERB_CUSTOM) |
- bind_info->szCustomVerb = NULL; |
- |
- if ((post_data_len() || is_chunked_upload()) && |
- get_upload_data(&bind_info->stgmedData.pstm) == S_OK) { |
- bind_info->stgmedData.tymed = TYMED_ISTREAM; |
- if (!is_chunked_upload()) { |
- bind_info->cbstgmedData = static_cast<DWORD>(post_data_len()); |
- } |
- DVLOG(1) << __FUNCTION__ << me() << method() |
- << " request with " << base::Int64ToString(post_data_len()) |
- << " bytes. url=" << url(); |
- } else { |
- DVLOG(1) << __FUNCTION__ << me() << "POST request with no data!"; |
- } |
- } |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size, |
- FORMATETC* formatetc, |
- STGMEDIUM* storage) { |
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); |
- DVLOG(1) << __FUNCTION__ << me() << "bytes available: " << size; |
- |
- if (terminate_requested()) { |
- DVLOG(1) << " Download requested. INET_E_TERMINATED_BIND returned"; |
- return INET_E_TERMINATED_BIND; |
- } |
- |
- if (!storage || (storage->tymed != TYMED_ISTREAM)) { |
- NOTREACHED(); |
- return E_INVALIDARG; |
- } |
- |
- IStream* read_stream = storage->pstm; |
- if (!read_stream) { |
- NOTREACHED(); |
- return E_UNEXPECTED; |
- } |
- |
- // Some requests such as HEAD have zero data. |
- if (size > 0) |
- pending_data_ = read_stream; |
- |
- if (pending_read_size_) { |
- size_t bytes_copied = SendDataToDelegate(pending_read_size_); |
- DVLOG(1) << __FUNCTION__ << me() << "size read: " << bytes_copied; |
- } else { |
- DVLOG(1) << __FUNCTION__ << me() << "- waiting for remote read"; |
- } |
- |
- if (BSCF_LASTDATANOTIFICATION & flags) { |
- if (!is_expecting_download_ || pending()) { |
- DVLOG(1) << __FUNCTION__ << me() << "EOF"; |
- return S_OK; |
- } |
- // Always return INET_E_TERMINATED_BIND to allow bind context reuse |
- // if DownloadToHost is suddenly requested. |
- DVLOG(1) << __FUNCTION__ << " EOF: INET_E_TERMINATED_BIND returned"; |
- return INET_E_TERMINATED_BIND; |
- } |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::OnObjectAvailable(REFIID iid, IUnknown* object) { |
- // We are calling BindToStorage on the moniker we should always get called |
- // back on OnDataAvailable and should never get OnObjectAvailable |
- NOTREACHED(); |
- return E_NOTIMPL; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url, |
- const wchar_t* current_headers, DWORD reserved, |
- wchar_t** additional_headers) { |
- DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); |
- if (!additional_headers) { |
- NOTREACHED(); |
- return E_POINTER; |
- } |
- |
- DVLOG(1) << __FUNCTION__ << me() << "headers: \n" << current_headers; |
- |
- if (status_.get_state() == Status::ABORTING) { |
- // At times the BINDSTATUS_REDIRECTING notification which is sent to the |
- // IBindStatusCallback interface does not have an accompanying HTTP |
- // redirect status code, i.e. the attempt to query the HTTP status code |
- // from the binding returns 0, 200, etc which are invalid redirect codes. |
- // We don't want urlmon to follow redirects. We return E_ABORT in our |
- // IBindStatusCallback::OnProgress function and also abort the binding. |
- // However urlmon still tries to establish a transaction with the |
- // redirected URL which confuses the web server. |
- // Fix is to abort the attempted transaction. |
- DLOG(WARNING) << __FUNCTION__ << me() |
- << ": Aborting connection to URL:" |
- << url |
- << " as the binding has been aborted"; |
- return E_ABORT; |
- } |
- |
- HRESULT hr = S_OK; |
- |
- std::string new_headers; |
- if (is_chunked_upload()) { |
- new_headers = base::StringPrintf("Transfer-Encoding: chunked\r\n"); |
- } |
- |
- if (!extra_headers().empty()) { |
- // TODO(robertshield): We may need to sanitize headers on POST here. |
- new_headers += extra_headers(); |
- } |
- |
- if (!referrer().empty()) { |
- // Referrer is famously misspelled in HTTP: |
- new_headers += base::StringPrintf("Referer: %s\r\n", referrer().c_str()); |
- } |
- |
- // In the rare case if "User-Agent" string is already in |current_headers|. |
- // We send Chrome's user agent in requests initiated within ChromeFrame to |
- // enable third party content in pages rendered in ChromeFrame to correctly |
- // send content for Chrome as the third party content may not be equipped to |
- // identify chromeframe as the user agent. This also ensures that the user |
- // agent reported in scripts in chrome frame is consistent with that sent |
- // in outgoing requests. |
- std::string user_agent = http_utils::AddChromeFrameToUserAgentValue( |
- http_utils::GetChromeUserAgent()); |
- new_headers += ReplaceOrAddUserAgent(current_headers, user_agent); |
- |
- if (!new_headers.empty()) { |
- *additional_headers = reinterpret_cast<wchar_t*>( |
- CoTaskMemAlloc((new_headers.size() + 1) * sizeof(wchar_t))); |
- |
- if (*additional_headers == NULL) { |
- NOTREACHED(); |
- hr = E_OUTOFMEMORY; |
- } else { |
- lstrcpynW(*additional_headers, base::ASCIIToWide(new_headers).c_str(), |
- new_headers.size()); |
- } |
- } |
- request_headers_ = new_headers; |
- return hr; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode, |
- const wchar_t* response_headers, const wchar_t* request_headers, |
- wchar_t** additional_headers) { |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::GetWindow(const GUID& guid_reason, |
- HWND* parent_window) { |
- if (!parent_window) |
- return E_INVALIDARG; |
- |
-#ifndef NDEBUG |
- wchar_t guid[40] = {0}; |
- ::StringFromGUID2(guid_reason, guid, arraysize(guid)); |
- const wchar_t* str = guid; |
- if (guid_reason == IID_IAuthenticate) |
- str = L"IAuthenticate"; |
- else if (guid_reason == IID_IHttpSecurity) |
- str = L"IHttpSecurity"; |
- else if (guid_reason == IID_IWindowForBindingUI) |
- str = L"IWindowForBindingUI"; |
- DVLOG(1) << __FUNCTION__ << me() << "GetWindow: " << str; |
-#endif |
- // We should return a non-NULL HWND as parent. Otherwise no dialog is shown. |
- // TODO(iyengar): This hits when running the URL request tests. |
- DLOG_IF(WARNING, !::IsWindow(parent_window_)) |
- << "UrlmonUrlRequest::GetWindow - no window!"; |
- *parent_window = parent_window_; |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::Authenticate(HWND* parent_window, |
- LPWSTR* user_name, |
- LPWSTR* password) { |
- if (!parent_window) |
- return E_INVALIDARG; |
- |
- if (privileged_mode_) |
- return E_ACCESSDENIED; |
- |
- DCHECK(::IsWindow(parent_window_)); |
- *parent_window = parent_window_; |
- return S_OK; |
-} |
- |
-STDMETHODIMP UrlmonUrlRequest::OnSecurityProblem(DWORD problem) { |
- // Urlmon notifies the client of authentication problems, certificate |
- // errors, etc by querying the object implementing the IBindStatusCallback |
- // interface for the IHttpSecurity interface. If this interface is not |
- // implemented then Urlmon checks for the problem codes defined below |
- // and performs actions as defined below:- |
- // It invokes the ReportProgress method of the protocol sink with |
- // these problem codes and eventually invokes the ReportResult method |
- // on the protocol sink which ends up in a call to the OnStopBinding |
- // method of the IBindStatusCallBack interface. |
- |
- // MSHTML's implementation of the IBindStatusCallback interface does not |
- // implement the IHttpSecurity interface. However it handles the |
- // OnStopBinding call with a HRESULT of 0x800c0019 and navigates to |
- // an interstitial page which presents the user with a choice of whether |
- // to abort the navigation. |
- |
- // In our OnStopBinding implementation we stop the navigation and inform |
- // Chrome about the result. Ideally Chrome should behave in a manner similar |
- // to IE, i.e. display the SSL error interstitial page and if the user |
- // decides to proceed anyway we would turn off SSL warnings for that |
- // particular navigation and allow IE to download the content. |
- // We would need to return the certificate information to Chrome for display |
- // purposes. Currently we only return a dummy certificate to Chrome. |
- // At this point we decided that it is a lot of work at this point and |
- // decided to go with the easier option of implementing the IHttpSecurity |
- // interface and replicating the checks performed by Urlmon. This |
- // causes Urlmon to display a dialog box on the same lines as IE6. |
- DVLOG(1) << __FUNCTION__ << me() << "Security problem : " << problem; |
- |
- // On IE6 the default IBindStatusCallback interface does not implement the |
- // IHttpSecurity interface and thus causes IE to put up a certificate error |
- // dialog box. We need to emulate this behavior for sites with mismatched |
- // certificates to work. |
- if (GetIEVersion() == IE_6) |
- return S_FALSE; |
- |
- HRESULT hr = E_ABORT; |
- |
- switch (problem) { |
- case ERROR_INTERNET_SEC_CERT_REV_FAILED: { |
- hr = RPC_E_RETRY; |
- break; |
- } |
- |
- case ERROR_INTERNET_SEC_CERT_DATE_INVALID: |
- case ERROR_INTERNET_SEC_CERT_CN_INVALID: |
- case ERROR_INTERNET_INVALID_CA: { |
- hr = S_FALSE; |
- break; |
- } |
- |
- default: { |
- NOTREACHED() << "Unhandled security problem : " << problem; |
- break; |
- } |
- } |
- return hr; |
-} |
- |
-HRESULT UrlmonUrlRequest::StartAsyncDownload() { |
- DVLOG(1) << __FUNCTION__ << me() << url(); |
- HRESULT hr = E_FAIL; |
- DCHECK((moniker_ && bind_context_) || (!moniker_ && !bind_context_)); |
- |
- if (!moniker_.get()) { |
- std::wstring wide_url = base::UTF8ToWide(url()); |
- hr = CreateURLMonikerEx(NULL, wide_url.c_str(), moniker_.Receive(), |
- URL_MK_UNIFORM); |
- if (FAILED(hr)) { |
- NOTREACHED() << "CreateURLMonikerEx 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 { |
- // Use existing bind context. |
- hr = ::RegisterBindStatusCallback(bind_context_, this, NULL, 0); |
- DCHECK(SUCCEEDED(hr)) << "RegisterBindStatusCallback failed. Error: " << hr; |
- } |
- |
- if (SUCCEEDED(hr)) { |
- base::win::ScopedComPtr<IStream> stream; |
- |
- // BindToStorage may complete synchronously. |
- // We still get all the callbacks - OnStart/StopBinding, this may result |
- // in destruction of our object. It's fine but we access some members |
- // below for debug info. :) |
- base::win::ScopedComPtr<IHttpSecurity> self(this); |
- |
- // Inform our moniker patch this binding should not be tortured. |
- base::win::ScopedComPtr<BindContextInfo> info; |
- BindContextInfo::FromBindContext(bind_context_, info.Receive()); |
- DCHECK(info); |
- if (info) |
- info->set_chrome_request(true); |
- |
- hr = moniker_->BindToStorage(bind_context_, NULL, __uuidof(IStream), |
- reinterpret_cast<void**>(stream.Receive())); |
- if (hr == S_OK) |
- DCHECK(binding_ != NULL || status_.get_state() == Status::DONE); |
- |
- if (FAILED(hr)) { |
- // TODO(joshia): Look into. This currently fails for: |
- // http://user2:secret@localhost:1337/auth-basic?set-cookie-if-challenged |
- // when running the UrlRequest unit tests. |
- DLOG(ERROR) << __FUNCTION__ << me() << |
- base::StringPrintf("IUrlMoniker::BindToStorage failed 0x%08X.", hr); |
- // In most cases we'll get a MK_E_SYNTAX error here but if we abort |
- // the navigation ourselves such as in the case of seeing something |
- // else than ALLOWALL in X-Frame-Options. |
- } |
- } |
- |
- DLOG_IF(ERROR, FAILED(hr)) << me() << |
- base::StringPrintf(L"StartAsyncDownload failed: 0x%08X", hr); |
- |
- return hr; |
-} |
- |
-void UrlmonUrlRequest::NotifyDelegateAndDie() { |
-} |
- |
-void UrlmonUrlRequest::TerminateTransaction() { |
- if (cleanup_transaction_ && bind_context_ && moniker_) { |
- // We return INET_E_TERMINATED_BIND from our OnDataAvailable implementation |
- // to ensure that the transaction stays around if Chrome decides to issue |
- // a download request when it finishes inspecting the headers received in |
- // OnResponse. However this causes the urlmon transaction object to leak. |
- // To workaround this we save away the IInternetProtocol interface which is |
- // implemented by the urlmon CTransaction object in our BindContextInfo |
- // instance which is maintained per bind context. Invoking Terminate |
- // on this with the special flags 0x2000000 cleanly releases the |
- // transaction. |
- static const int kUrlmonTerminateTransactionFlags = 0x2000000; |
- base::win::ScopedComPtr<BindContextInfo> info; |
- BindContextInfo::FromBindContext(bind_context_, info.Receive()); |
- DCHECK(info); |
- if (info && info->protocol()) { |
- info->protocol()->Terminate(kUrlmonTerminateTransactionFlags); |
- } |
- } |
- bind_context_.Release(); |
-} |
- |
-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); |
-} |
- |
-net::Error UrlmonUrlRequest::HresultToNetError(HRESULT hr) { |
- const int kInvalidHostName = 0x8007007b; |
- // Useful reference: |
- // http://msdn.microsoft.com/en-us/library/ms775145(VS.85).aspx |
- |
- net::Error ret = net::ERR_UNEXPECTED; |
- |
- switch (hr) { |
- case S_OK: |
- ret = net::OK; |
- break; |
- |
- case MK_E_SYNTAX: |
- ret = net::ERR_INVALID_URL; |
- break; |
- |
- case INET_E_CANNOT_CONNECT: |
- ret = net::ERR_CONNECTION_FAILED; |
- break; |
- |
- case INET_E_DOWNLOAD_FAILURE: |
- case INET_E_CONNECTION_TIMEOUT: |
- case E_ABORT: |
- ret = net::ERR_CONNECTION_ABORTED; |
- break; |
- |
- case INET_E_DATA_NOT_AVAILABLE: |
- ret = net::ERR_EMPTY_RESPONSE; |
- break; |
- |
- case INET_E_RESOURCE_NOT_FOUND: |
- // To behave more closely to the chrome network stack, we translate this |
- // error value as tunnel connection failed. This error value is tested |
- // in the ProxyTunnelRedirectTest and UnexpectedServerAuthTest tests. |
- ret = net::ERR_TUNNEL_CONNECTION_FAILED; |
- break; |
- |
- // The following error codes can be returned while processing an invalid |
- // url. http://msdn.microsoft.com/en-us/library/bb250493(v=vs.85).aspx |
- case INET_E_INVALID_URL: |
- case INET_E_UNKNOWN_PROTOCOL: |
- case INET_E_REDIRECT_FAILED: |
- case INET_E_SECURITY_PROBLEM: |
- case kInvalidHostName: |
- case E_INVALIDARG: |
- case E_OUTOFMEMORY: |
- ret = net::ERR_INVALID_URL; |
- break; |
- |
- case INET_E_INVALID_CERTIFICATE: |
- ret = net::ERR_CERT_INVALID; |
- break; |
- |
- case E_ACCESSDENIED: |
- ret = net::ERR_ACCESS_DENIED; |
- break; |
- |
- default: |
- DLOG(WARNING) |
- << base::StringPrintf("TODO: translate HRESULT 0x%08X to net::Error", |
- hr); |
- break; |
- } |
- return ret; |
-} |
- |
- |
-PluginUrlRequestManager::ThreadSafeFlags |
- UrlmonUrlRequestManager::GetThreadSafeFlags() { |
- return PluginUrlRequestManager::NOT_THREADSAFE; |
-} |
- |
-void UrlmonUrlRequestManager::SetInfoForUrl(const std::wstring& url, |
- IMoniker* moniker, LPBC bind_ctx) { |
- CComObject<UrlmonUrlRequest>* new_request = NULL; |
- CComObject<UrlmonUrlRequest>::CreateInstance(&new_request); |
- if (new_request) { |
- GURL start_url(url); |
- DCHECK(start_url.is_valid()); |
- DCHECK(pending_request_ == NULL); |
- |
- base::win::ScopedComPtr<BindContextInfo> info; |
- BindContextInfo::FromBindContext(bind_ctx, info.Receive()); |
- DCHECK(info); |
- IStream* cache = info ? info->cache() : NULL; |
- pending_request_ = new_request; |
- pending_request_->InitPending(start_url, moniker, bind_ctx, |
- enable_frame_busting_, privileged_mode_, |
- notification_window_, cache); |
- // Start the request |
- bool is_started = pending_request_->Start(); |
- DCHECK(is_started); |
- } |
-} |
- |
-void UrlmonUrlRequestManager::BindTerminated(IMoniker* moniker, |
- IBindCtx* bind_ctx, |
- IStream* post_data, |
- const char* request_headers) { |
-} |
- |
-scoped_refptr<UrlmonUrlRequest> UrlmonUrlRequestManager::LookupRequest( |
- int request_id, RequestMap* request_map) { |
- RequestMap::iterator it = request_map->find(request_id); |
- if (request_map->end() != it) |
- return it->second; |
- return NULL; |
-} |
- |
-UrlmonUrlRequestManager::UrlmonUrlRequestManager() |
- : stopping_(false), notification_window_(NULL), |
- privileged_mode_(false), |
- container_(NULL), |
- background_worker_thread_enabled_(true) { |
- background_thread_.reset(new base::Thread("cf_iexplore_background_thread")); |
- background_thread_->init_com_with_mta(false); |
- background_worker_thread_enabled_ = |
- GetConfigBool(true, kUseBackgroundThreadForSubResources); |
- if (background_worker_thread_enabled_) { |
- base::Thread::Options options; |
- options.message_loop_type = base::MessageLoop::TYPE_UI; |
- background_thread_->StartWithOptions(options); |
- } |
-} |
- |
-UrlmonUrlRequestManager::~UrlmonUrlRequestManager() { |
-} |
- |
-void UrlmonUrlRequestManager::AddPrivacyDataForUrl( |
- const std::string& url, const std::string& policy_ref, |
- int32 flags) { |
- DCHECK(!url.empty()); |
- |
- bool fire_privacy_event = false; |
- |
- if (privacy_info_.privacy_records.empty()) |
- 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; |
- } |
- } |
- |
- PrivacyInfo::PrivacyEntry& privacy_entry = |
- privacy_info_.privacy_records[base::UTF8ToWide(url)]; |
- |
- privacy_entry.flags |= flags; |
- privacy_entry.policy_ref = base::UTF8ToWide(policy_ref); |
- |
- if (fire_privacy_event && IsWindow(notification_window_)) { |
- PostMessage(notification_window_, WM_FIRE_PRIVACY_CHANGE_NOTIFICATION, 1, |
- 0); |
- } |
-} |