// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_FRAME_PROTOCOL_SINK_WRAP_H_
#define CHROME_FRAME_PROTOCOL_SINK_WRAP_H_
#include <exdisp.h>
#include <urlmon.h>
#include <atlbase.h>
#include <atlcom.h>
#include <map>
#include <string>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_bstr.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"
// Typedefs for IInternetProtocol and related methods that we patch.
typedef HRESULT (STDMETHODCALLTYPE* InternetProtocol_Start_Fn)(
IInternetProtocol* this_object, LPCWSTR url,
IInternetProtocolSink* prot_sink, IInternetBindInfo* bind_info,
DWORD flags, HANDLE_PTR reserved);
typedef HRESULT (STDMETHODCALLTYPE* InternetProtocol_Read_Fn)(
IInternetProtocol* this_object, void* buffer, ULONG size,
ULONG* size_read);
typedef HRESULT (STDMETHODCALLTYPE* InternetProtocol_StartEx_Fn)(
IInternetProtocolEx* this_object, IUri* uri,
IInternetProtocolSink* prot_sink, IInternetBindInfo* bind_info,
DWORD flags, HANDLE_PTR reserved);
typedef HRESULT (STDMETHODCALLTYPE* InternetProtocol_LockRequest_Fn)(
IInternetProtocol* this_object, DWORD options);
typedef HRESULT (STDMETHODCALLTYPE* InternetProtocol_UnlockRequest_Fn)(
IInternetProtocol* this_object);
typedef HRESULT (STDMETHODCALLTYPE* InternetProtocol_Abort_Fn)(
IInternetProtocol* this_object, HRESULT hr, DWORD options);
typedef HRESULT (STDMETHODCALLTYPE* InternetProtocol_Terminate_Fn)(
IInternetProtocol* this_object, DWORD options);
class ProtData;
// A class to wrap protocol sink in IInternetProtocol::Start[Ex] for
// HTTP and HTTPS protocols.
//
// This is an alternative to a mime filter and we have to do this in order
// to inspect initial portion of HTML for 'chrome' meta tag and report
// a different mime type in that case.
//
// We implement several documented interfaces
// supported by the original sink provided by urlmon. There are a few
// undocumented interfaces that we have chosen not to implement
// but delegate simply the QI.
class ProtocolSinkWrap
: public CComObjectRootEx<CComMultiThreadModel>,
public IInternetProtocolSink {
public:
BEGIN_COM_MAP(ProtocolSinkWrap)
COM_INTERFACE_ENTRY(IInternetProtocolSink)
COM_INTERFACE_BLIND_DELEGATE()
END_COM_MAP()
static base::win::ScopedComPtr<IInternetProtocolSink> CreateNewSink(
IInternetProtocolSink* sink, ProtData* prot_data);
// Enables or disables activation of Chrome Frame via the X-UA-Compatible
// header or meta tag. The tag/header is respected by default.
static void set_ignore_xua(bool ignore_xua) { ignore_xua_ = ignore_xua; }
static bool ignore_xua() { return ignore_xua_; }
// Apparently this has to be public, to satisfy COM_INTERFACE_BLIND_DELEGATE
IInternetProtocolSink* delegate() {
return delegate_;
}
protected:
ProtocolSinkWrap();
~ProtocolSinkWrap();
private:
static bool ignore_xua_;
// IInternetProtocolSink methods
STDMETHOD(Switch)(PROTOCOLDATA* protocol_data);
STDMETHOD(ReportProgress)(ULONG status_code, LPCWSTR status_text);
STDMETHOD(ReportData)(DWORD flags, ULONG progress, ULONG max_progress);
STDMETHOD(ReportResult)(HRESULT result, DWORD error, LPCWSTR result_text);
// Remember original sink
base::win::ScopedComPtr<IInternetProtocolSink> delegate_;
base::win::ScopedComPtr<IServiceProvider> delegate_service_provider_;
scoped_refptr<ProtData> prot_data_;
DISALLOW_COPY_AND_ASSIGN(ProtocolSinkWrap);
};
class ProtData : public base::RefCounted<ProtData> {
public:
ProtData(IInternetProtocol* protocol, InternetProtocol_Read_Fn read_fun,
const wchar_t* url);
~ProtData();
HRESULT Read(void* buffer, ULONG size, ULONG* size_read);
HRESULT ReportProgress(IInternetProtocolSink* delegate,
ULONG status_code,
LPCWSTR status_text);
HRESULT ReportData(IInternetProtocolSink* delegate,
DWORD flags, ULONG progress, ULONG max_progress);
HRESULT ReportResult(IInternetProtocolSink* delegate, HRESULT result,
DWORD error, LPCWSTR result_text);
void UpdateUrl(const wchar_t* url);
static scoped_refptr<ProtData> DataFromProtocol(IInternetProtocol* protocol);
RendererType renderer_type() {
return renderer_type_;
}
// Valid only if renderer_type_ is CHROME.
const std::string& referrer() const {
return referrer_;
}
bool is_attach_external_tab_request() const {
return read_fun_ == NULL;
}
// Removes the mapping between the protocol and the ProtData.
void Invalidate();
const std::wstring& url() const {
return url_;
}
private:
typedef std::map<IInternetProtocol*, ProtData*> ProtocolDataMap;
static ProtocolDataMap datamap_;
static base::Lock datamap_lock_;
// Url we are retrieving. Used for RendererTypeForUrl() 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_;
// What BINDSTATUS_MIMETYPEAVAILABLE and Co. tells us.
base::win::ScopedBstr suggested_mime_type_;
// At least one of the following has been received:
// BINDSTATUS_MIMETYPEAVAILABLE,
// MIMESTATUS_VERIFIEDMIMETYPEAVAILABLE
// BINDSTATUS_SERVER_MIMETYPEAVAILABLE
bool has_suggested_mime_type_;
// BINDSTATUS_SERVER_MIMETYPEAVAILABLE received, so we shall fire one.
bool has_server_mime_type_;
RendererType renderer_type_;
// Buffer for accumulated data including 1 extra for NULL-terminator
static const size_t kMaxContentSniffLength = 2 * 1024;
char buffer_[kMaxContentSniffLength + 1];
unsigned long buffer_size_; // NOLINT
unsigned long buffer_pos_; // NOLINT
HRESULT FillBuffer();
void SaveSuggestedMimeType(LPCWSTR status_text);
void FireSuggestedMimeType(IInternetProtocolSink* delegate);
void SaveReferrer(IInternetProtocolSink* delegate);
};
struct TransactionHooks {
void InstallHooks();
void RevertHooks();
};
DECLSPEC_SELECTANY struct TransactionHooks g_trans_hooks;
#endif // CHROME_FRAME_PROTOCOL_SINK_WRAP_H_