Commit aaf12450 authored by stoyan@chromium.org's avatar stoyan@chromium.org

BUG=47879

Review URL: http://codereview.chromium.org/2824057

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52806 0039d316-1c4b-4281-b951-d872f2087c98
parent 962b9821
......@@ -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;
}
......@@ -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;
......
......@@ -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;
}
......@@ -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_
......@@ -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;
......
......@@ -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 {
......
......@@ -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)));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment