diff options
-rw-r--r-- | chrome_frame/bho.cc | 22 | ||||
-rw-r--r-- | chrome_frame/http_negotiate.cc | 101 | ||||
-rw-r--r-- | chrome_frame/http_negotiate.h | 27 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request.cc | 23 | ||||
-rw-r--r-- | chrome_frame/utils.cc | 25 | ||||
-rw-r--r-- | chrome_frame/utils.h | 5 |
6 files changed, 180 insertions, 23 deletions
diff --git a/chrome_frame/bho.cc b/chrome_frame/bho.cc index 9fff456..33396a6 100644 --- a/chrome_frame/bho.cc +++ b/chrome_frame/bho.cc @@ -7,7 +7,9 @@ #include <shlguid.h> #include <shobjidl.h> +#include "base/file_path.h" #include "base/logging.h" +#include "base/path_service.h" #include "base/registry.h" #include "base/scoped_bstr_win.h" #include "base/scoped_comptr_win.h" @@ -247,12 +249,32 @@ Bho* Bho::GetCurrentThreadBhoInstance() { return bho_current_thread_instance_.Pointer()->Get(); } +namespace { +// Utility function that prevents the current module from ever being unloaded. +void PinModule() { + FilePath module_path; + if (PathService::Get(base::FILE_MODULE, &module_path)) { + HMODULE unused; + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN, + module_path.value().c_str(), &unused)) { + NOTREACHED() << "Failed to pin module " << module_path.value().c_str(); + } + } else { + NOTREACHED() << "Could not get module path."; + } +} +} // namespace + bool PatchHelper::InitializeAndPatchProtocolsIfNeeded() { bool ret = false; _pAtlModule->m_csStaticDataInitAndTypeInfo.Lock(); if (state_ == UNKNOWN) { + // If we're going to start patching things, we'd better make sure that we + // stick around for ever more: + PinModule(); + HttpNegotiatePatch::Initialize(); bool patch_protocol = GetConfigBool(true, kPatchProtocols); diff --git a/chrome_frame/http_negotiate.cc b/chrome_frame/http_negotiate.cc index efffeccc..1ea79e2 100644 --- a/chrome_frame/http_negotiate.cc +++ b/chrome_frame/http_negotiate.cc @@ -16,6 +16,16 @@ #include "chrome_frame/utils.h" #include "chrome_frame/vtable_patch_manager.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_util.h" + +const wchar_t kChromeMimeType[] = L"application/chromepage"; +const char kUACompatibleHttpHeader[] = "x-ua-compatible"; + +// From the latest urlmon.h. TODO(robertshield): Remove this once we update +// our SDK version. +static const int BINDSTATUS_SERVER_MIMETYPEAVAILABLE = 54; + static const int kHttpNegotiateBeginningTransactionIndex = 3; static const int kHttpNegotiateOnResponseTransactionIndex = 4; @@ -26,6 +36,21 @@ BEGIN_VTABLE_PATCHES(IHttpNegotiate) HttpNegotiatePatch::OnResponse) END_VTABLE_PATCHES() +static const int kBindStatusCallbackStartBindingIndex = 3; + +BEGIN_VTABLE_PATCHES(IBindStatusCallback) + VTABLE_PATCH_ENTRY(kBindStatusCallbackStartBindingIndex, + HttpNegotiatePatch::StartBinding) +END_VTABLE_PATCHES() + +static const int kInternetProtocolSinkReportProgressIndex = 4; + +BEGIN_VTABLE_PATCHES(IInternetProtocolSink) + VTABLE_PATCH_ENTRY(kInternetProtocolSinkReportProgressIndex, + HttpNegotiatePatch::ReportProgress) +END_VTABLE_PATCHES() + + HttpNegotiatePatch::HttpNegotiatePatch() { } @@ -85,6 +110,18 @@ HRESULT HttpNegotiatePatch::PatchHttpNegotiate(IUnknown* to_patch) { << StringPrintf("IHttpNegotiate not supported 0x%08X", hr); } + ScopedComPtr<IBindStatusCallback> bscb; + hr = bscb.QueryFrom(to_patch); + + if (bscb) { + hr = vtable_patch::PatchInterfaceMethods(bscb, + IBindStatusCallback_PatchInfo); + DLOG_IF(ERROR, FAILED(hr)) + << StringPrintf("BindStatusCallback patch failed 0x%08X", hr); + } else { + DLOG(WARNING) << StringPrintf("IBindStatusCallback not supported 0x%08X", + hr); + } return hr; } @@ -162,3 +199,67 @@ HRESULT HttpNegotiatePatch::OnResponse(IHttpNegotiate_OnResponse_Fn original, additional_request_headers); return hr; } + +// static +HRESULT HttpNegotiatePatch::StartBinding( + IBindStatusCallback_StartBinding_Fn original, + IBindStatusCallback* me, DWORD reserved, IBinding* binding) { + ScopedComPtr<IBinding> local_binding(binding); + ScopedComPtr<IInternetProtocolSink> protocol_sink; + + HRESULT hr = protocol_sink.QueryFrom(local_binding); + if (FAILED(hr) || !protocol_sink) { + DLOG(WARNING) << "Failed to get IInternetProtocolSink from IBinding."; + } else { + if (!IS_PATCHED(IInternetProtocolSink)) { + hr = vtable_patch::PatchInterfaceMethods(protocol_sink, + IInternetProtocolSink_PatchInfo); + } + + DLOG_IF(WARNING, FAILED(hr)) + << "Failed to patch IInternetProtocolSink from IBinding."; + } + + hr = original(me, reserved, binding); + return hr; +} + +// static +HRESULT HttpNegotiatePatch::ReportProgress( + IInternetProtocolSink_ReportProgress_Fn original, IInternetProtocolSink* me, + ULONG status_code, LPCWSTR status_text) { + if (status_code == BINDSTATUS_MIMETYPEAVAILABLE || + status_code == BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE || + status_code == BINDSTATUS_SERVER_MIMETYPEAVAILABLE) { + // Check to see if we need to alter the mime type that gets reported + // by inspecting the raw header information: + ScopedComPtr<IWinInetHttpInfo> win_inet_http_info; + HRESULT hr = win_inet_http_info.QueryFrom(me); + + if (FAILED(hr) || !win_inet_http_info) { + NOTREACHED() << "Could not get at an IWinInetHttpInfo in " + << "IInternetProtocolSink::ReportProgress."; + } else { + // We have headers: check to see if the server is requesting CF via + // the X-UA-Compatible: chrome=1 HTTP header. + std::string headers(GetRawHttpHeaders(win_inet_http_info)); + if (net::HttpUtil::HasHeader(headers, kUACompatibleHttpHeader)) { + net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), + "\r\n"); + while (it.GetNext()) { + if (LowerCaseEqualsASCII(it.name_begin(), it.name_end(), + kUACompatibleHttpHeader)) { + std::string ua_value(StringToLowerASCII(it.values())); + if (ua_value.find("chrome=1") != std::string::npos) { + status_text = kChromeMimeType; + break; + } + } + } + } + } + } + + HRESULT hr = original(me, status_code, status_text); + return hr; +}
\ No newline at end of file diff --git a/chrome_frame/http_negotiate.h b/chrome_frame/http_negotiate.h index 4a22d9c..3146539 100644 --- a/chrome_frame/http_negotiate.h +++ b/chrome_frame/http_negotiate.h @@ -17,8 +17,24 @@ typedef HRESULT (STDMETHODCALLTYPE* IHttpNegotiate_OnResponse_Fn)( IHttpNegotiate* me, DWORD response_code, LPCWSTR response_header, LPCWSTR request_header, LPWSTR* additional_request_headers); +// Typedefs for IBindStatusCallback methods. +typedef HRESULT (STDMETHODCALLTYPE* IBindStatusCallback_StartBinding_Fn)( + IBindStatusCallback* me, DWORD reserved, IBinding *binding); + +// Typedefs for IInternetProtocolSink methods. +typedef HRESULT (STDMETHODCALLTYPE* IInternetProtocolSink_ReportProgress_Fn)( + IInternetProtocolSink* me, ULONG status_code, LPCWSTR status_text); + // Patches methods of urlmon's IHttpNegotiate implementation for the purposes // of adding to the http user agent header. + +// Also patches one of the IBindStatusCallback implementations in urlmon to pick +// up an IBinding during the StartBinding call. The IBinding implementor then +// gets a patch applied to its IInternetProtocolSink's ReportProgress method. +// The patched is there so that the reporting of the MIME type to the IBinding +// implementor can be changed if an X-Chrome-Frame HTTP header is present +// in the response headers. If anyone can suggest a more straightforward way of +// doing this, I would be eternally grateful. class HttpNegotiatePatch { // class is not to be instantiated atm. HttpNegotiatePatch(); @@ -28,15 +44,24 @@ class HttpNegotiatePatch { static bool Initialize(); static void Uninitialize(); + // IHttpNegotiate patch methods static STDMETHODIMP BeginningTransaction( IHttpNegotiate_BeginningTransaction_Fn original, IHttpNegotiate* me, LPCWSTR url, LPCWSTR headers, DWORD reserved, LPWSTR* additional_headers); - static STDMETHODIMP OnResponse( IHttpNegotiate_OnResponse_Fn original, IHttpNegotiate* me, DWORD response_code, LPCWSTR response_header, LPCWSTR request_header, LPWSTR* additional_request_headers); + // IBindStatusCallback patch methods + static STDMETHODIMP StartBinding(IBindStatusCallback_StartBinding_Fn original, + IBindStatusCallback* me, DWORD reserved, IBinding *binding); + + // IInternetProtocolSink patch methods + static STDMETHODIMP ReportProgress( + IInternetProtocolSink_ReportProgress_Fn original, + IInternetProtocolSink* me, ULONG status_code, LPCWSTR status_text); + protected: static HRESULT PatchHttpNegotiate(IUnknown* to_patch); diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc index b2588ab..66aacd6 100644 --- a/chrome_frame/urlmon_url_request.cc +++ b/chrome_frame/urlmon_url_request.cc @@ -773,28 +773,7 @@ std::string UrlmonUrlRequest::GetHttpHeaders() const { return std::string(); } - scoped_ptr<char> buffer; - DWORD size = 0; - DWORD flags = 0; - DWORD reserved = 0; - HRESULT hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &size, - &flags, &reserved); - if (!size) { - DLOG(WARNING) << "Failed to query HTTP headers size. Error 0x%x" << hr; - return std::string(); - } - - buffer.reset(new char[size]); - memset(buffer.get(), 0, size); - - hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, buffer.get(), - &size, &flags, &reserved); - if (FAILED(hr)) { - DLOG(WARNING) << "Failed to query HTTP headers. Error 0x%x" << hr; - return std::string(); - } - - return buffer.get(); + return GetRawHttpHeaders(info); } void UrlmonUrlRequest::ReleaseBindings() { diff --git a/chrome_frame/utils.cc b/chrome_frame/utils.cc index 61bf7df..10bf92a 100644 --- a/chrome_frame/utils.cc +++ b/chrome_frame/utils.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <shlobj.h> +#include <wininet.h> #include "chrome_frame/html_utils.h" #include "chrome_frame/utils.h" @@ -610,3 +611,27 @@ bool IsValidUrlScheme(const std::wstring& url, bool is_privileged) { return false; } + +std::string GetRawHttpHeaders(IWinInetHttpInfo* info) { + DCHECK(info); + + std::string buffer; + + DWORD size = 0; + DWORD flags = 0; + DWORD reserved = 0; + HRESULT hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &size, + &flags, &reserved); + if (!size) { + DLOG(WARNING) << "Failed to query HTTP headers size. Error: " << hr; + } else { + buffer.resize(size + 1); + hr = info->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, &buffer[0], + &size, &flags, &reserved); + if (FAILED(hr)) { + DLOG(WARNING) << "Failed to query HTTP headers. Error: " << hr; + } + } + + return buffer; +} diff --git a/chrome_frame/utils.h b/chrome_frame/utils.h index 717ee4f..351fba1 100644 --- a/chrome_frame/utils.h +++ b/chrome_frame/utils.h @@ -7,6 +7,7 @@ #include <atlbase.h> #include <string> +#include <UrlMon.h> #include "base/basictypes.h" #include "base/logging.h" @@ -206,6 +207,10 @@ HRESULT GetUrlFromMoniker(IMoniker* moniker, IBindCtx* bind_context, // When is_privileged is true, chrome extension URLs will be considered valid. bool IsValidUrlScheme(const std::wstring& url, bool is_privileged); +// Returns the raw http headers for the current request given an +// IWinInetHttpInfo pointer. +std::string GetRawHttpHeaders(IWinInetHttpInfo* info); + // See COM_INTERFACE_BLIND_DELEGATE below for details. template <class T> STDMETHODIMP CheckOutgoingInterface(void* obj, REFIID iid, void** ret, |