summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome_frame/bho.cc22
-rw-r--r--chrome_frame/http_negotiate.cc101
-rw-r--r--chrome_frame/http_negotiate.h27
-rw-r--r--chrome_frame/urlmon_url_request.cc23
-rw-r--r--chrome_frame/utils.cc25
-rw-r--r--chrome_frame/utils.h5
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,