summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome_frame/bho.cc9
-rw-r--r--chrome_frame/chrome_active_document.cc11
-rw-r--r--chrome_frame/http_negotiate.cc165
-rw-r--r--chrome_frame/http_negotiate.h36
-rw-r--r--chrome_frame/protocol_sink_wrap.cc81
-rw-r--r--chrome_frame/protocol_sink_wrap.h22
-rw-r--r--chrome_frame/urlmon_url_request.cc3
7 files changed, 247 insertions, 80 deletions
diff --git a/chrome_frame/bho.cc b/chrome_frame/bho.cc
index afa191d..436d5cb 100644
--- a/chrome_frame/bho.cc
+++ b/chrome_frame/bho.cc
@@ -342,17 +342,17 @@ bool PatchHelper::InitializeAndPatchProtocolsIfNeeded() {
if (!IsUnpinnedMode())
PinModule();
- HttpNegotiatePatch::Initialize();
-
ProtocolPatchMethod patch_method = GetPatchMethod();
if (patch_method == PATCH_METHOD_INET_PROTOCOL) {
g_trans_hooks.InstallHooks();
state_ = PATCH_PROTOCOL;
} else if (patch_method == PATCH_METHOD_IBROWSER) {
+ HttpNegotiatePatch::Initialize();
state_ = PATCH_IBROWSER;
} else {
DCHECK(patch_method == PATCH_METHOD_MONIKER);
state_ = PATCH_MONIKER;
+ HttpNegotiatePatch::Initialize();
MonikerPatch::Initialize();
}
@@ -378,10 +378,11 @@ void PatchHelper::UnpatchIfNeeded() {
} else if (state_ == PATCH_IBROWSER) {
vtable_patch::UnpatchInterfaceMethods(IBrowserService_PatchInfo);
MonikerPatch::Uninitialize();
+ HttpNegotiatePatch::Uninitialize();
+ } else {
+ HttpNegotiatePatch::Uninitialize();
}
- HttpNegotiatePatch::Uninitialize();
-
state_ = UNKNOWN;
}
diff --git a/chrome_frame/chrome_active_document.cc b/chrome_frame/chrome_active_document.cc
index bd8d7a4..de06c75 100644
--- a/chrome_frame/chrome_active_document.cc
+++ b/chrome_frame/chrome_active_document.cc
@@ -265,7 +265,16 @@ STDMETHODIMP ChromeActiveDocument::Load(BOOL fully_avalable,
return E_INVALIDARG;
}
- const std::string& referrer = mgr ? mgr->referrer() : EmptyString();
+ std::string referrer = mgr ? mgr->referrer() : EmptyString();
+ // With CTransaction patch we have more robust way to grab the referrer for
+ // each top-level-switch-to-CF request by peeking at our sniffing data
+ // object that lives inside the bind context.
+ if (g_patch_helper.state() == PatchHelper::PATCH_PROTOCOL && info) {
+ scoped_refptr<ProtData> prot_data = info->get_prot_data();
+ if (prot_data)
+ referrer = prot_data->referrer();
+ }
+
if (!LaunchUrl(url, referrer, is_new_navigation)) {
NOTREACHED() << __FUNCTION__ << " Failed to launch url:" << url;
return E_INVALIDARG;
diff --git a/chrome_frame/http_negotiate.cc b/chrome_frame/http_negotiate.cc
index 30d7961..db51fd5 100644
--- a/chrome_frame/http_negotiate.cc
+++ b/chrome_frame/http_negotiate.cc
@@ -93,9 +93,57 @@ class SimpleBindStatusCallback : public CComObjectRootEx<CComSingleThreadModel>,
return E_NOTIMPL;
}
};
-
} // end namespace
+std::string AppendCFUserAgentString(LPCWSTR headers,
+ LPCWSTR additional_headers) {
+ static const char kLowerCaseUserAgent[] = "user-agent";
+ using net::HttpUtil;
+
+ std::string ascii_headers;
+ if (additional_headers) {
+ ascii_headers = WideToASCII(additional_headers);
+ }
+
+ // Extract "User-Agent" from |additiona_headers| or |headers|.
+ HttpUtil::HeadersIterator headers_iterator(ascii_headers.begin(),
+ ascii_headers.end(), "\r\n");
+ std::string user_agent_value;
+ if (headers_iterator.AdvanceTo(kLowerCaseUserAgent)) {
+ user_agent_value = headers_iterator.values();
+ } else if (headers != NULL) {
+ // See if there's a user-agent header specified in the original headers.
+ std::string original_headers(WideToASCII(headers));
+ HttpUtil::HeadersIterator original_it(original_headers.begin(),
+ original_headers.end(), "\r\n");
+ if (original_it.AdvanceTo(kLowerCaseUserAgent))
+ user_agent_value = original_it.values();
+ }
+
+ // Use the default "User-Agent" if none was provided.
+ if (user_agent_value.empty())
+ user_agent_value = http_utils::GetDefaultUserAgent();
+
+ // Now add chromeframe to it.
+ user_agent_value = http_utils::AddChromeFrameToUserAgentValue(
+ user_agent_value);
+
+ // Build new headers, skip the existing user agent value from
+ // existing headers.
+ std::string new_headers;
+ headers_iterator.Reset();
+ while (headers_iterator.GetNext()) {
+ std::string name(headers_iterator.name());
+ if (!LowerCaseEqualsASCII(name, kLowerCaseUserAgent)) {
+ new_headers += name + ": " + headers_iterator.values() + "\r\n";
+ }
+ }
+
+ new_headers += "User-Agent: " + user_agent_value;
+ new_headers += "\r\n";
+ return new_headers;
+}
+
HRESULT GetBrowserServiceFromProtocolSink(IInternetProtocolSink* sink,
IBrowserService** browser_service) {
DCHECK(browser_service);
@@ -173,21 +221,17 @@ HRESULT HttpNegotiatePatch::PatchHttpNegotiate(IUnknown* to_patch) {
<< StringPrintf("IHttpNegotiate not supported 0x%08X", hr);
}
- // CTransaction patch supports sniffing HTTP headers, so no need of
- // sniffing inside HttpNegotiatePatch::ReportProgress.
- if (GetPatchMethod() != PATCH_METHOD_INET_PROTOCOL) {
- 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);
- }
+ 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;
}
@@ -233,59 +277,11 @@ HRESULT HttpNegotiatePatch::BeginningTransaction(
DLOG(INFO) << "No NavigationManager";
}
- static const char kLowerCaseUserAgent[] = "user-agent";
-
- using net::HttpUtil;
-
- std::string ascii_headers;
- if (*additional_headers) {
- ascii_headers = WideToASCII(*additional_headers);
- DLOG(INFO) << __FUNCTION__ << " additional headers: " << ascii_headers;
- }
-
- HttpUtil::HeadersIterator headers_iterator(ascii_headers.begin(),
- ascii_headers.end(), "\r\n");
- std::string user_agent_value;
- if (headers_iterator.AdvanceTo(kLowerCaseUserAgent)) {
- user_agent_value = headers_iterator.values();
- } else if (headers != NULL) {
- // See if there's a user-agent header specified in the original headers.
- std::string original_headers(WideToASCII(headers));
- HttpUtil::HeadersIterator original_it(original_headers.begin(),
- original_headers.end(), "\r\n");
- if (original_it.AdvanceTo(kLowerCaseUserAgent))
- user_agent_value = original_it.values();
- }
-
- // Use the default one if none was provided.
- if (user_agent_value.empty())
- user_agent_value = http_utils::GetDefaultUserAgent();
-
- // Now add chromeframe to it.
- user_agent_value = http_utils::AddChromeFrameToUserAgentValue(
- user_agent_value);
-
- // Build new headers, skip the existing user agent value from
- // existing headers.
- std::string new_headers;
- headers_iterator.Reset();
- while (headers_iterator.GetNext()) {
- std::string name(headers_iterator.name());
- if (!LowerCaseEqualsASCII(name, kLowerCaseUserAgent)) {
- new_headers += name + ": " + headers_iterator.values() + "\r\n";
- }
- }
-
- new_headers += "User-Agent: " + user_agent_value;
- new_headers += "\r\n\r\n";
-
- if (*additional_headers)
- ::CoTaskMemFree(*additional_headers);
- *additional_headers = reinterpret_cast<wchar_t*>(::CoTaskMemAlloc(
- (new_headers.length() + 1) * sizeof(wchar_t)));
- lstrcpyW(*additional_headers, ASCIIToWide(new_headers).c_str());
-
- return hr;
+ std::string updated = AppendCFUserAgentString(headers, *additional_headers);
+ *additional_headers = reinterpret_cast<wchar_t*>(::CoTaskMemRealloc(
+ *additional_headers, (updated.length() + 1) * sizeof(wchar_t)));
+ lstrcpyW(*additional_headers, ASCIIToWide(updated).c_str());
+ return S_OK;
}
// static
@@ -408,3 +404,34 @@ HRESULT HttpNegotiatePatch::ReportProgress(
return original(me, status_code, status_text);
}
}
+
+STDMETHODIMP UserAgentAddOn::BeginningTransaction(LPCWSTR url, LPCWSTR headers,
+ DWORD reserved,
+ LPWSTR* additional_headers) {
+ HRESULT hr = S_OK;
+ if (delegate_) {
+ hr = delegate_->BeginningTransaction(url, headers, reserved,
+ additional_headers);
+ }
+
+ if (hr == S_OK) {
+ // Add "chromeframe" user-agent string.
+ std::string updated_headers = AppendCFUserAgentString(headers,
+ *additional_headers);
+ *additional_headers = reinterpret_cast<wchar_t*>(::CoTaskMemRealloc(
+ *additional_headers, (updated_headers.length() + 1) * sizeof(wchar_t)));
+ lstrcpyW(*additional_headers, ASCIIToWide(updated_headers).c_str());
+ }
+ return hr;
+}
+
+STDMETHODIMP UserAgentAddOn::OnResponse(DWORD response_code,
+ LPCWSTR response_headers, LPCWSTR request_headers,
+ LPWSTR* additional_headers) {
+ HRESULT hr = S_OK;
+ if (delegate_) {
+ hr = delegate_->OnResponse(response_code, response_headers, request_headers,
+ additional_headers);
+ }
+ return hr;
+}
diff --git a/chrome_frame/http_negotiate.h b/chrome_frame/http_negotiate.h
index 43ac405..dd4748a 100644
--- a/chrome_frame/http_negotiate.h
+++ b/chrome_frame/http_negotiate.h
@@ -6,9 +6,11 @@
#define CHROME_FRAME_HTTP_NEGOTIATE_H_
#include <shdeprecated.h>
+#include <string>
#include <urlmon.h>
#include "base/basictypes.h"
+#include "base/scoped_comptr_win.h"
// Typedefs for IHttpNegotiate methods.
typedef HRESULT (STDMETHODCALLTYPE* IHttpNegotiate_BeginningTransaction_Fn)(
@@ -76,4 +78,38 @@ HRESULT GetBrowserServiceFromProtocolSink(IInternetProtocolSink* sink,
// TODO(robertshield): Remove this once we update our SDK version.
extern const int LOCAL_BINDSTATUS_SERVER_MIMETYPEAVAILABLE;
+// Scans |additional_headers| and |headers| for User-Agent header or grabs
+// the default User-Agent if none is found. Append "chromeframe" at the end
+// of the string. Returns the original content of |additional_headers| but
+// with the new User-Agent header. Somewhat unusual interface is dictated
+// because we use it in IHttpNegotiate::BeginningTransaction.
+// The function is a public since we want to use it from
+// UrlmonUrlRequest::BeginningTransaction for the unusual but yet possible case
+// when |headers| contains an User-Agent string.
+// TODO(stoyan): Add unit test.
+std::string AppendCFUserAgentString(LPCWSTR headers,
+ LPCWSTR additional_headers);
+// Simple class that wraps IHttpNegotiate interface and adds "chromeframe"
+// to User-agent Http header.
+class UserAgentAddOn : public IHttpNegotiate {
+ public:
+ UserAgentAddOn() {}
+ ~UserAgentAddOn() {}
+ void set_delegate(IHttpNegotiate* delegate) {
+ delegate_ = delegate;
+ }
+
+ bool has_delegate() const {
+ return delegate_ != NULL;
+ }
+
+ protected:
+ // IHttpNegotiate
+ STDMETHOD(BeginningTransaction)(LPCWSTR url, LPCWSTR headers, DWORD reserved,
+ LPWSTR* additional_headers);
+ STDMETHOD(OnResponse)(DWORD response_code, LPCWSTR response_headers,
+ LPCWSTR request_headers, LPWSTR* additional_headers);
+ ScopedComPtr<IHttpNegotiate> delegate_;
+};
+
#endif // CHROME_FRAME_HTTP_NEGOTIATE_H_
diff --git a/chrome_frame/protocol_sink_wrap.cc b/chrome_frame/protocol_sink_wrap.cc
index 716e308..f5ed710 100644
--- a/chrome_frame/protocol_sink_wrap.cc
+++ b/chrome_frame/protocol_sink_wrap.cc
@@ -91,6 +91,48 @@ ScopedComPtr<IInternetProtocolSink> ProtocolSinkWrap::CreateNewSink(
return ScopedComPtr<IInternetProtocolSink>(new_sink);
}
+HRESULT ProtocolSinkWrap::ObtainServiceProvider() {
+ HRESULT hr = S_OK;
+ if (!delegate_service_provider_) {
+ hr = delegate_service_provider_.QueryFrom(delegate_);
+ }
+ return hr;
+}
+
+HRESULT ProtocolSinkWrap::ObtainHttpNegotiate() {
+ if (UserAgentAddOn::has_delegate())
+ return S_OK;
+
+ HRESULT hr = ObtainServiceProvider();
+ if (hr == S_OK) {
+ ScopedComPtr<IHttpNegotiate> http_negotiate;
+ hr = delegate_service_provider_->QueryService(
+ IID_IHttpNegotiate,
+ IID_IHttpNegotiate,
+ reinterpret_cast<void**>(http_negotiate.Receive()));
+ UserAgentAddOn::set_delegate(http_negotiate);
+ }
+ return hr;
+}
+
+STDMETHODIMP ProtocolSinkWrap::QueryService(REFGUID guidService, REFIID riid,
+ void** ppvObject) {
+ // We really insist to append "chromeframe" user-agent header, even in the
+ // very unlikely case when delegate does not support IServiceProvider and/or
+ // IHttpNegotiate.
+ if (guidService == IID_IHttpNegotiate && riid == IID_IHttpNegotiate) {
+ ObtainHttpNegotiate();
+ AddRef();
+ *ppvObject = reinterpret_cast<void**>(static_cast<IHttpNegotiate*>(this));
+ return S_OK;
+ }
+
+ HRESULT hr = ObtainServiceProvider();
+ if (hr == S_OK)
+ hr = delegate_service_provider_->QueryService(guidService, riid, ppvObject);
+ return hr;
+}
+
// IInternetProtocolSink methods
STDMETHODIMP ProtocolSinkWrap::Switch(PROTOCOLDATA* protocol_data) {
HRESULT hr = E_FAIL;
@@ -318,18 +360,27 @@ HRESULT ProtData::ReportProgress(IInternetProtocolSink* delegate,
// TODO(stoyan): BINDSTATUS_RAWMIMETYPE
case BINDSTATUS_MIMETYPEAVAILABLE:
case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE:
- SaveSuggestedMimeType(status_text);
-
- ScopedComPtr<IWinInetHttpInfo> info;
- info.QueryFrom(delegate);
- renderer_type_ = DetermineRendererTypeFromMetaData(suggested_mime_type_,
- url_, info);
+ // When Transaction is attached i.e. when existing BTS it terminated
+ // and "converted" to BTO, events will be re-fired for the new sink,
+ // but we may skip the renderer_type_ determination since it's already
+ // done.
+ if (renderer_type_ == UNDETERMINED) {
+ SaveSuggestedMimeType(status_text);
+ // This may seem awkward. CBinding's implementation of IWinInetHttpInfo
+ // will forward to CTransaction that will forward to the real protocol.
+ // We may ask CTransaction (our protocol_ member) for IWinInetHttpInfo.
+ ScopedComPtr<IWinInetHttpInfo> info;
+ info.QueryFrom(delegate);
+ renderer_type_ = DetermineRendererTypeFromMetaData(suggested_mime_type_,
+ url_, info);
+ }
if (renderer_type_ == CHROME) {
// Suggested mime type is "text/html" and we either have OptInUrl
// or X-UA-Compatible HTTP headers.
DLOG(INFO) << "Forwarding BINDSTATUS_MIMETYPEAVAILABLE "
<< kChromeMimeType;
+ SaveReferrer(delegate);
delegate->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, kChromeMimeType);
} else if (renderer_type_ == OTHER) {
// Suggested mime type is not "text/html" - we are not interested in
@@ -369,6 +420,7 @@ HRESULT ProtData::ReportData(IInternetProtocolSink* delegate,
if (renderer_type_ == CHROME) {
DLOG(INFO) << "Forwarding BINDSTATUS_MIMETYPEAVAILABLE "
<< kChromeMimeType;
+ SaveReferrer(delegate);
delegate->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, kChromeMimeType);
}
@@ -446,6 +498,23 @@ void ProtData::FireSugestedMimeType(IInternetProtocolSink* delegate) {
}
}
+void ProtData::SaveReferrer(IInternetProtocolSink* delegate) {
+ DCHECK_EQ(CHROME, renderer_type_);
+ ScopedComPtr<IWinInetHttpInfo> info;
+ info.QueryFrom(delegate);
+ DCHECK(info);
+ if (info) {
+ char buffer[4096] = {0};
+ DWORD len = sizeof(buffer);
+ DWORD flags = 0;
+ HRESULT hr = info->QueryInfo(
+ HTTP_QUERY_REFERER | HTTP_QUERY_FLAG_REQUEST_HEADERS,
+ buffer, &len, &flags, 0);
+ if (hr == S_OK && len > 0)
+ referrer_.assign(buffer);
+ }
+}
+
scoped_refptr<ProtData> ProtData::DataFromProtocol(
IInternetProtocol* protocol) {
scoped_refptr<ProtData> instance;
diff --git a/chrome_frame/protocol_sink_wrap.h b/chrome_frame/protocol_sink_wrap.h
index a6b88b6..c441adc 100644
--- a/chrome_frame/protocol_sink_wrap.h
+++ b/chrome_frame/protocol_sink_wrap.h
@@ -19,6 +19,7 @@
#include "base/scoped_bstr_win.h"
#include "googleurl/src/gurl.h"
#include "chrome_frame/chrome_frame_delegate.h"
+#include "chrome_frame/http_negotiate.h"
#include "chrome_frame/ie8_types.h"
#include "chrome_frame/utils.h"
#include "chrome_frame/vtable_patch_manager.h"
@@ -60,10 +61,13 @@ class ProtData;
// but delegate simply the QI.
class ProtocolSinkWrap
: public CComObjectRootEx<CComMultiThreadModel>,
+ public IServiceProvider,
+ public UserAgentAddOn, // implements IHttpNegotiate
public IInternetProtocolSink {
public:
BEGIN_COM_MAP(ProtocolSinkWrap)
+ COM_INTERFACE_ENTRY(IServiceProvider)
COM_INTERFACE_ENTRY(IInternetProtocolSink)
COM_INTERFACE_BLIND_DELEGATE()
END_COM_MAP()
@@ -87,8 +91,16 @@ END_COM_MAP()
STDMETHOD(ReportData)(DWORD flags, ULONG progress, ULONG max_progress);
STDMETHOD(ReportResult)(HRESULT result, DWORD error, LPCWSTR result_text);
+ // IServiceProvider - return our HttpNegotiate or forward to delegate
+ STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void** ppvObject);
+
+ // Helpers.
+ HRESULT ObtainHttpNegotiate();
+ HRESULT ObtainServiceProvider();
+
// Remember original sink
ScopedComPtr<IInternetProtocolSink> delegate_;
+ ScopedComPtr<IServiceProvider> delegate_service_provider_;
scoped_refptr<ProtData> prot_data_;
DISALLOW_COPY_AND_ASSIGN(ProtocolSinkWrap);
};
@@ -113,6 +125,11 @@ class ProtData : public base::RefCounted<ProtData> {
return renderer_type_;
}
+ // Valid only if renderer_type_ is CHROME.
+ const std::string& referrer() const {
+ return referrer_;
+ }
+
private:
typedef std::map<IInternetProtocol*, ProtData*> ProtocolDataMap;
static ProtocolDataMap datamap_;
@@ -120,6 +137,10 @@ class ProtData : public base::RefCounted<ProtData> {
// Url we are retrieving. Used for IsOptInUrl() only.
std::wstring url_;
+ // HTTP "Referrer" header if we detect are going to switch.
+ // We have to save and pass it to Chrome, so scripts can read it via DOM.
+ std::string referrer_;
+
// Our gate to IInternetProtocol::Read()
IInternetProtocol* protocol_;
InternetProtocol_Read_Fn read_fun_;
@@ -145,6 +166,7 @@ class ProtData : public base::RefCounted<ProtData> {
HRESULT FillBuffer();
void SaveSuggestedMimeType(LPCWSTR status_text);
void FireSugestedMimeType(IInternetProtocolSink* delegate);
+ void SaveReferrer(IInternetProtocolSink* delegate);
};
struct TransactionHooks {
diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc
index 642a438..e951f18 100644
--- a/chrome_frame/urlmon_url_request.cc
+++ b/chrome_frame/urlmon_url_request.cc
@@ -556,6 +556,9 @@ STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url,
new_headers += StringPrintf("Referer: %s\r\n", referrer().c_str());
}
+ // In the rare case if "User-Agent" string is already in |current_headers|.
+ new_headers += AppendCFUserAgentString(current_headers, NULL);
+
if (!new_headers.empty()) {
*additional_headers = reinterpret_cast<wchar_t*>(
CoTaskMemAlloc((new_headers.size() + 1) * sizeof(wchar_t)));