diff options
-rw-r--r-- | chrome_frame/bho.cc | 9 | ||||
-rw-r--r-- | chrome_frame/chrome_active_document.cc | 11 | ||||
-rw-r--r-- | chrome_frame/http_negotiate.cc | 165 | ||||
-rw-r--r-- | chrome_frame/http_negotiate.h | 36 | ||||
-rw-r--r-- | chrome_frame/protocol_sink_wrap.cc | 81 | ||||
-rw-r--r-- | chrome_frame/protocol_sink_wrap.h | 22 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request.cc | 3 |
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))); |