| Index: chrome_frame/protocol_sink_wrap.cc
|
| diff --git a/chrome_frame/protocol_sink_wrap.cc b/chrome_frame/protocol_sink_wrap.cc
|
| deleted file mode 100644
|
| index 9bd069ed86d4aea293956b72ee5a43cb56e3f12c..0000000000000000000000000000000000000000
|
| --- a/chrome_frame/protocol_sink_wrap.cc
|
| +++ /dev/null
|
| @@ -1,1010 +0,0 @@
|
| -// 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.
|
| -
|
| -#include <htiframe.h>
|
| -#include <mshtml.h>
|
| -#include <algorithm>
|
| -
|
| -#include "chrome_frame/protocol_sink_wrap.h"
|
| -
|
| -#include "base/logging.h"
|
| -#include "base/memory/singleton.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| -#include "base/win/scoped_bstr.h"
|
| -#include "chrome_frame/bho.h"
|
| -#include "chrome_frame/bind_context_info.h"
|
| -#include "chrome_frame/exception_barrier.h"
|
| -#include "chrome_frame/function_stub.h"
|
| -#include "chrome_frame/policy_settings.h"
|
| -#include "chrome_frame/utils.h"
|
| -
|
| -using std::min;
|
| -
|
| -// BINDSTATUS_SERVER_MIMETYPEAVAILABLE == 54. Introduced in IE 8, so
|
| -// not in everyone's headers yet. See:
|
| -// http://msdn.microsoft.com/en-us/library/ms775133(VS.85,loband).aspx
|
| -#ifndef BINDSTATUS_SERVER_MIMETYPEAVAILABLE
|
| -#define BINDSTATUS_SERVER_MIMETYPEAVAILABLE 54
|
| -#endif
|
| -
|
| -bool ProtocolSinkWrap::ignore_xua_ = false;
|
| -
|
| -static const char kTextHtmlMimeType[] = "text/html";
|
| -const wchar_t kUrlMonDllName[] = L"urlmon.dll";
|
| -
|
| -static const int kInternetProtocolStartIndex = 3;
|
| -static const int kInternetProtocolReadIndex = 9;
|
| -static const int kInternetProtocolStartExIndex = 13;
|
| -static const int kInternetProtocolLockRequestIndex = 11;
|
| -static const int kInternetProtocolUnlockRequestIndex = 12;
|
| -static const int kInternetProtocolAbortIndex = 5;
|
| -static const int kInternetProtocolTerminateIndex = 6;
|
| -
|
| -
|
| -// IInternetProtocol/Ex patches.
|
| -STDMETHODIMP Hook_Start(InternetProtocol_Start_Fn orig_start,
|
| - IInternetProtocol* protocol,
|
| - LPCWSTR url,
|
| - IInternetProtocolSink* prot_sink,
|
| - IInternetBindInfo* bind_info,
|
| - DWORD flags,
|
| - HANDLE_PTR reserved);
|
| -
|
| -STDMETHODIMP Hook_StartEx(InternetProtocol_StartEx_Fn orig_start_ex,
|
| - IInternetProtocolEx* protocol,
|
| - IUri* uri,
|
| - IInternetProtocolSink* prot_sink,
|
| - IInternetBindInfo* bind_info,
|
| - DWORD flags,
|
| - HANDLE_PTR reserved);
|
| -
|
| -STDMETHODIMP Hook_Read(InternetProtocol_Read_Fn orig_read,
|
| - IInternetProtocol* protocol,
|
| - void* buffer,
|
| - ULONG size,
|
| - ULONG* size_read);
|
| -
|
| -STDMETHODIMP Hook_LockRequest(InternetProtocol_LockRequest_Fn orig_req,
|
| - IInternetProtocol* protocol,
|
| - DWORD options);
|
| -
|
| -STDMETHODIMP Hook_UnlockRequest(InternetProtocol_UnlockRequest_Fn orig_req,
|
| - IInternetProtocol* protocol);
|
| -
|
| -STDMETHODIMP Hook_Abort(InternetProtocol_Abort_Fn orig_req,
|
| - IInternetProtocol* protocol,
|
| - HRESULT hr,
|
| - DWORD options);
|
| -
|
| -STDMETHODIMP Hook_Terminate(InternetProtocol_Terminate_Fn orig_req,
|
| - IInternetProtocol* protocol,
|
| - DWORD options);
|
| -
|
| -/////////////////////////////////////////////////////////////////////////////
|
| -BEGIN_VTABLE_PATCHES(CTransaction)
|
| - VTABLE_PATCH_ENTRY(kInternetProtocolStartIndex, Hook_Start)
|
| - VTABLE_PATCH_ENTRY(kInternetProtocolReadIndex, Hook_Read)
|
| - VTABLE_PATCH_ENTRY(kInternetProtocolLockRequestIndex, Hook_LockRequest)
|
| - VTABLE_PATCH_ENTRY(kInternetProtocolUnlockRequestIndex, Hook_UnlockRequest)
|
| - VTABLE_PATCH_ENTRY(kInternetProtocolAbortIndex, Hook_Abort)
|
| - VTABLE_PATCH_ENTRY(kInternetProtocolTerminateIndex, Hook_Terminate)
|
| -END_VTABLE_PATCHES()
|
| -
|
| -BEGIN_VTABLE_PATCHES(CTransaction2)
|
| - VTABLE_PATCH_ENTRY(kInternetProtocolStartExIndex, Hook_StartEx)
|
| -END_VTABLE_PATCHES()
|
| -
|
| -//
|
| -// ProtocolSinkWrap implementation
|
| -
|
| -// Static map initialization
|
| -ProtData::ProtocolDataMap ProtData::datamap_;
|
| -base::Lock ProtData::datamap_lock_;
|
| -
|
| -ProtocolSinkWrap::ProtocolSinkWrap() {
|
| - DVLOG(1) << __FUNCTION__ << base::StringPrintf(" 0x%08X", this);
|
| -}
|
| -
|
| -ProtocolSinkWrap::~ProtocolSinkWrap() {
|
| - DVLOG(1) << __FUNCTION__ << base::StringPrintf(" 0x%08X", this);
|
| -}
|
| -
|
| -base::win::ScopedComPtr<IInternetProtocolSink> ProtocolSinkWrap::CreateNewSink(
|
| - IInternetProtocolSink* sink, ProtData* data) {
|
| - DCHECK(sink != NULL);
|
| - DCHECK(data != NULL);
|
| - CComObject<ProtocolSinkWrap>* new_sink = NULL;
|
| - CComObject<ProtocolSinkWrap>::CreateInstance(&new_sink);
|
| - new_sink->delegate_ = sink;
|
| - new_sink->prot_data_ = data;
|
| - return base::win::ScopedComPtr<IInternetProtocolSink>(new_sink);
|
| -}
|
| -
|
| -// IInternetProtocolSink methods
|
| -STDMETHODIMP ProtocolSinkWrap::Switch(PROTOCOLDATA* protocol_data) {
|
| - HRESULT hr = E_FAIL;
|
| - if (delegate_)
|
| - hr = delegate_->Switch(protocol_data);
|
| - return hr;
|
| -}
|
| -
|
| -STDMETHODIMP ProtocolSinkWrap::ReportProgress(ULONG status_code,
|
| - LPCWSTR status_text) {
|
| - DVLOG(1) << "ProtocolSinkWrap::ReportProgress: "
|
| - << BindStatus2Str(status_code)
|
| - << " Status: " << (status_text ? status_text : L"");
|
| -
|
| - HRESULT hr = prot_data_->ReportProgress(delegate_, status_code, status_text);
|
| - return hr;
|
| -}
|
| -
|
| -STDMETHODIMP ProtocolSinkWrap::ReportData(DWORD flags, ULONG progress,
|
| - ULONG max_progress) {
|
| - DCHECK(delegate_);
|
| - DVLOG(1) << "ProtocolSinkWrap::ReportData: " << Bscf2Str(flags)
|
| - << " progress: " << progress << " progress_max: " << max_progress;
|
| -
|
| - HRESULT hr = prot_data_->ReportData(delegate_, flags, progress, max_progress);
|
| - return hr;
|
| -}
|
| -
|
| -STDMETHODIMP ProtocolSinkWrap::ReportResult(HRESULT result, DWORD error,
|
| - LPCWSTR result_text) {
|
| - DVLOG(1) << "ProtocolSinkWrap::ReportResult: result: " << result
|
| - << " error: " << error
|
| - << " Text: " << (result_text ? result_text : L"");
|
| - ExceptionBarrier barrier;
|
| - HRESULT hr = prot_data_->ReportResult(delegate_, result, error, result_text);
|
| - return hr;
|
| -}
|
| -
|
| -
|
| -// Helpers
|
| -base::win::ScopedComPtr<IBindCtx> BindCtxFromIBindInfo(
|
| - IInternetBindInfo* bind_info) {
|
| - LPOLESTR bind_ctx_string = NULL;
|
| - ULONG count;
|
| - base::win::ScopedComPtr<IBindCtx> bind_ctx;
|
| - bind_info->GetBindString(BINDSTRING_PTR_BIND_CONTEXT, &bind_ctx_string, 1,
|
| - &count);
|
| - if (bind_ctx_string) {
|
| - int bind_ctx_int;
|
| - base::StringToInt(bind_ctx_string, &bind_ctx_int);
|
| - IBindCtx* pbc = reinterpret_cast<IBindCtx*>(bind_ctx_int);
|
| - bind_ctx.Attach(pbc);
|
| - CoTaskMemFree(bind_ctx_string);
|
| - }
|
| -
|
| - return bind_ctx;
|
| -}
|
| -
|
| -bool ShouldWrapSink(IInternetProtocolSink* sink, const wchar_t* url) {
|
| - // Ignore everything that does not start with http:// or https://.
|
| - // |url| is already normalized (i.e. no leading spaces, capital letters in
|
| - // protocol etc) and non-null (we check in Hook_Start).
|
| - DCHECK(url != NULL);
|
| -
|
| - if (ProtocolSinkWrap::ignore_xua())
|
| - return false; // No need to intercept, we're ignoring X-UA-Compatible tags
|
| -
|
| - if ((url != StrStrW(url, L"http://")) && (url != StrStrW(url, L"https://")))
|
| - return false;
|
| -
|
| - base::win::ScopedComPtr<IHttpNegotiate> http_negotiate;
|
| - HRESULT hr = DoQueryService(GUID_NULL, sink, http_negotiate.Receive());
|
| - if (http_negotiate && !IsSubFrameRequest(http_negotiate))
|
| - return true;
|
| -
|
| - return false;
|
| -}
|
| -
|
| -// High level helpers
|
| -bool IsCFRequest(IBindCtx* pbc) {
|
| - base::win::ScopedComPtr<BindContextInfo> info;
|
| - BindContextInfo::FromBindContext(pbc, info.Receive());
|
| - if (info && info->chrome_request())
|
| - return true;
|
| -
|
| - return false;
|
| -}
|
| -
|
| -bool HasProtData(IBindCtx* pbc) {
|
| - base::win::ScopedComPtr<BindContextInfo> info;
|
| - BindContextInfo::FromBindContext(pbc, info.Receive());
|
| - bool result = false;
|
| - if (info)
|
| - result = info->has_prot_data();
|
| - return result;
|
| -}
|
| -
|
| -void PutProtData(IBindCtx* pbc, ProtData* data) {
|
| - // AddRef and Release to avoid a potential leak of a ProtData instance if
|
| - // FromBindContext fails.
|
| - data->AddRef();
|
| - base::win::ScopedComPtr<BindContextInfo> info;
|
| - BindContextInfo::FromBindContext(pbc, info.Receive());
|
| - if (info)
|
| - info->set_prot_data(data);
|
| - data->Release();
|
| -}
|
| -
|
| -bool IsTextHtml(const wchar_t* status_text) {
|
| - const std::wstring str = status_text;
|
| - bool is_text_html = LowerCaseEqualsASCII(str, kTextHtmlMimeType);
|
| - return is_text_html;
|
| -}
|
| -
|
| -bool IsAdditionallySupportedContentType(const wchar_t* status_text) {
|
| - static const char* kHeaderContentTypes[] = {
|
| - "application/xhtml+xml",
|
| - "application/xml",
|
| - "image/svg",
|
| - "image/svg+xml",
|
| - "text/xml",
|
| - "video/ogg",
|
| - "video/webm",
|
| - "video/mp4"
|
| - };
|
| -
|
| - const std::wstring str = status_text;
|
| - for (int i = 0; i < arraysize(kHeaderContentTypes); ++i) {
|
| - if (LowerCaseEqualsASCII(str, kHeaderContentTypes[i]))
|
| - return true;
|
| - }
|
| -
|
| - if (PolicySettings::GetInstance()->GetRendererForContentType(
|
| - status_text) == PolicySettings::RENDER_IN_CHROME_FRAME) {
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -// Returns:
|
| -// RENDERER_TYPE_OTHER: if suggested mime type is not text/html.
|
| -// RENDERER_TYPE_UNDETERMINED: if suggested mime type is text/html.
|
| -// RENDERER_TYPE_CHROME_RESPONSE_HEADER: X-UA-Compatible tag is in HTTP headers.
|
| -// RENDERER_TYPE_CHROME_DEFAULT_RENDERER: GCF is the default renderer and the
|
| -// Url is not listed in the
|
| -// RenderInHostUrls registry key.
|
| -// RENDERER_TYPE_CHROME_OPT_IN_URL: GCF is not the default renderer and the Url
|
| -// is listed in the RenderInGcfUrls registry
|
| -// key.
|
| -RendererType DetermineRendererTypeFromMetaData(
|
| - const wchar_t* suggested_mime_type,
|
| - const std::wstring& url,
|
| - IWinInetHttpInfo* info) {
|
| - bool is_text_html = IsTextHtml(suggested_mime_type);
|
| - bool is_supported_content_type = is_text_html ||
|
| - IsAdditionallySupportedContentType(suggested_mime_type);
|
| -
|
| - if (!is_supported_content_type)
|
| - return RENDERER_TYPE_OTHER;
|
| -
|
| - if (!url.empty()) {
|
| - RendererType renderer_type = RendererTypeForUrl(url);
|
| - if (IsChrome(renderer_type)) {
|
| - return renderer_type;
|
| - }
|
| - }
|
| -
|
| - if (info) {
|
| - char buffer[512] = "x-ua-compatible";
|
| - DWORD len = sizeof(buffer);
|
| - DWORD flags = 0;
|
| - HRESULT hr = info->QueryInfo(HTTP_QUERY_CUSTOM, buffer, &len, &flags, NULL);
|
| -
|
| - if (hr == S_OK && len > 0) {
|
| - if (CheckXUaCompatibleDirective(buffer, GetIEMajorVersion())) {
|
| - return RENDERER_TYPE_CHROME_RESPONSE_HEADER;
|
| - }
|
| - }
|
| - }
|
| -
|
| - // We can (and want) to sniff the content.
|
| - if (is_text_html) {
|
| - return RENDERER_TYPE_UNDETERMINED;
|
| - }
|
| -
|
| - // We cannot sniff the content.
|
| - return RENDERER_TYPE_OTHER;
|
| -}
|
| -
|
| -RendererType DetermineRendererType(void* buffer, DWORD size, bool last_chance) {
|
| - RendererType renderer_type = RENDERER_TYPE_UNDETERMINED;
|
| - if (last_chance)
|
| - renderer_type = RENDERER_TYPE_OTHER;
|
| -
|
| - std::wstring html_contents;
|
| - // TODO(joshia): detect and handle different content encodings
|
| - base::UTF8ToWide(reinterpret_cast<char*>(buffer), size, &html_contents);
|
| -
|
| - // Note that document_contents_ may have NULL characters in it. While
|
| - // browsers may handle this properly, we don't and will stop scanning
|
| - // for the XUACompat content value if we encounter one.
|
| - std::wstring xua_compat_content;
|
| - if (SUCCEEDED(UtilGetXUACompatContentValue(html_contents,
|
| - &xua_compat_content))) {
|
| - if (CheckXUaCompatibleDirective(WideToASCII(xua_compat_content),
|
| - GetIEMajorVersion())) {
|
| - renderer_type = RENDERER_TYPE_CHROME_HTTP_EQUIV;
|
| - }
|
| - }
|
| -
|
| - return renderer_type;
|
| -}
|
| -
|
| -// ProtData
|
| -ProtData::ProtData(IInternetProtocol* protocol,
|
| - InternetProtocol_Read_Fn read_fun, const wchar_t* url)
|
| - : has_suggested_mime_type_(false), has_server_mime_type_(false),
|
| - buffer_size_(0), buffer_pos_(0),
|
| - renderer_type_(RENDERER_TYPE_UNDETERMINED), protocol_(protocol),
|
| - read_fun_(read_fun), url_(url) {
|
| - memset(buffer_, 0, arraysize(buffer_));
|
| - DVLOG(1) << __FUNCTION__ << " " << this;
|
| -
|
| - // Add to map.
|
| - base::AutoLock lock(datamap_lock_);
|
| - DCHECK(datamap_.end() == datamap_.find(protocol_));
|
| - datamap_[protocol] = this;
|
| -}
|
| -
|
| -ProtData::~ProtData() {
|
| - DVLOG(1) << __FUNCTION__ << " " << this;
|
| - Invalidate();
|
| -}
|
| -
|
| -HRESULT ProtData::Read(void* buffer, ULONG size, ULONG* size_read) {
|
| - if (renderer_type_ == RENDERER_TYPE_UNDETERMINED) {
|
| - return E_PENDING;
|
| - }
|
| -
|
| - const ULONG bytes_available = buffer_size_ - buffer_pos_;
|
| - const ULONG bytes_to_copy = std::min(bytes_available, size);
|
| - if (bytes_to_copy) {
|
| - // Copy from the local buffer.
|
| - memcpy(buffer, buffer_ + buffer_pos_, bytes_to_copy);
|
| - *size_read = bytes_to_copy;
|
| - buffer_pos_ += bytes_to_copy;
|
| -
|
| - HRESULT hr = S_OK;
|
| - ULONG new_data = 0;
|
| - if (size > bytes_available) {
|
| - // User buffer is greater than what we have.
|
| - buffer = reinterpret_cast<uint8*>(buffer) + bytes_to_copy;
|
| - size -= bytes_to_copy;
|
| - hr = read_fun_(protocol_, buffer, size, &new_data);
|
| - }
|
| -
|
| - if (size_read)
|
| - *size_read = bytes_to_copy + new_data;
|
| - return hr;
|
| - }
|
| -
|
| - return read_fun_(protocol_, buffer, size, size_read);
|
| -}
|
| -
|
| -// Attempt to detect ChromeFrame from HTTP headers when
|
| -// BINDSTATUS_MIMETYPEAVAILABLE is received.
|
| -// There are three possible outcomes: CHROME_*/OTHER/UNDETERMINED.
|
| -// If RENDERER_TYPE_UNDETERMINED we are going to sniff the content later in
|
| -// ReportData().
|
| -//
|
| -// With not-so-well-written software (mime filters/protocols/protocol patchers)
|
| -// BINDSTATUS_MIMETYPEAVAILABLE might be fired multiple times with different
|
| -// values for the same client.
|
| -// If the renderer_type_ member is:
|
| -// RENDERER_TYPE_CHROME_* - 2nd (and any subsequent)
|
| -// BINDSTATUS_MIMETYPEAVAILABLE is ignored.
|
| -// RENDERER_TYPE_OTHER - 2nd (and any subsequent) BINDSTATUS_MIMETYPEAVAILABLE
|
| -// is passed through.
|
| -// RENDERER_TYPE_UNDETERMINED - Try to detect ChromeFrame from HTTP headers
|
| -// (acts as if this is the first time
|
| -// BINDSTATUS_MIMETYPEAVAILABLE is received).
|
| -HRESULT ProtData::ReportProgress(IInternetProtocolSink* delegate,
|
| - ULONG status_code, LPCWSTR status_text) {
|
| - switch (status_code) {
|
| - case BINDSTATUS_DIRECTBIND:
|
| - renderer_type_ = RENDERER_TYPE_OTHER;
|
| - break;
|
| -
|
| - case BINDSTATUS_REDIRECTING:
|
| - url_.clear();
|
| - if (status_text)
|
| - url_ = status_text;
|
| - break;
|
| -
|
| - case BINDSTATUS_SERVER_MIMETYPEAVAILABLE:
|
| - has_server_mime_type_ = true;
|
| - SaveSuggestedMimeType(status_text);
|
| - return S_OK;
|
| -
|
| - // TODO(stoyan): BINDSTATUS_RAWMIMETYPE
|
| - case BINDSTATUS_MIMETYPEAVAILABLE:
|
| - case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE:
|
| - SaveSuggestedMimeType(status_text);
|
| - // 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_ == RENDERER_TYPE_UNDETERMINED) {
|
| - // 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.
|
| - base::win::ScopedComPtr<IWinInetHttpInfo> info;
|
| - info.QueryFrom(delegate);
|
| - renderer_type_ = DetermineRendererTypeFromMetaData(suggested_mime_type_,
|
| - url_, info);
|
| - }
|
| -
|
| - if (IsChrome(renderer_type_)) {
|
| - // Suggested mime type is "text/html" and we have DEFAULT_RENDERER,
|
| - // OPT_IN_URL, or RESPONSE_HEADER.
|
| - DVLOG(1) << "Forwarding BINDSTATUS_MIMETYPEAVAILABLE "
|
| - << kChromeMimeType;
|
| - SaveReferrer(delegate);
|
| - delegate->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, kChromeMimeType);
|
| - } else if (renderer_type_ == RENDERER_TYPE_OTHER) {
|
| - // Suggested mime type is not "text/html" - we are not interested in
|
| - // this request anymore.
|
| - FireSuggestedMimeType(delegate);
|
| - } else {
|
| - // Suggested mime type is "text/html"; We will try to sniff the
|
| - // HTML content in ReportData.
|
| - DCHECK_EQ(RENDERER_TYPE_UNDETERMINED, renderer_type_);
|
| - }
|
| - return S_OK;
|
| - }
|
| -
|
| - // We are just pass through at this point, avoid false positive crash reports.
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - return delegate->ReportProgress(status_code, status_text);
|
| -}
|
| -
|
| -HRESULT ProtData::ReportData(IInternetProtocolSink* delegate,
|
| - DWORD flags, ULONG progress, ULONG max_progress) {
|
| - if (renderer_type_ != RENDERER_TYPE_UNDETERMINED) {
|
| - // We are just pass through now, avoid false positive crash reports.
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - return delegate->ReportData(flags, progress, max_progress);
|
| - }
|
| -
|
| - HRESULT hr = FillBuffer();
|
| -
|
| - bool last_chance = false;
|
| - if (hr == S_OK || hr == S_FALSE) {
|
| - last_chance = true;
|
| - }
|
| -
|
| - renderer_type_ = SkipMetadataCheck() ? RENDERER_TYPE_OTHER
|
| - : DetermineRendererType(buffer_, buffer_size_, last_chance);
|
| -
|
| - if (renderer_type_ == RENDERER_TYPE_UNDETERMINED) {
|
| - // do not report anything, we need more data.
|
| - return S_OK;
|
| - }
|
| -
|
| - if (IsChrome(renderer_type_)) {
|
| - DVLOG(1) << "Forwarding BINDSTATUS_MIMETYPEAVAILABLE " << kChromeMimeType;
|
| - SaveReferrer(delegate);
|
| - delegate->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, kChromeMimeType);
|
| - }
|
| -
|
| - if (renderer_type_ == RENDERER_TYPE_OTHER) {
|
| - FireSuggestedMimeType(delegate);
|
| - }
|
| -
|
| - // This is the first data notification we forward, since up to now we hold
|
| - // the content received.
|
| - flags |= BSCF_FIRSTDATANOTIFICATION;
|
| -
|
| - if (hr == S_FALSE) {
|
| - flags |= (BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE);
|
| - }
|
| -
|
| - return delegate->ReportData(flags, progress, max_progress);
|
| -}
|
| -
|
| -HRESULT ProtData::ReportResult(IInternetProtocolSink* delegate, HRESULT result,
|
| - DWORD error, LPCWSTR result_text) {
|
| - // We may receive ReportResult without ReportData, if the connection fails
|
| - // for example.
|
| - if (renderer_type_ == RENDERER_TYPE_UNDETERMINED) {
|
| - DVLOG(1) << "ReportResult received but renderer type is yet unknown.";
|
| - renderer_type_ = RENDERER_TYPE_OTHER;
|
| - FireSuggestedMimeType(delegate);
|
| - }
|
| -
|
| - HRESULT hr = S_OK;
|
| - if (delegate)
|
| - hr = delegate->ReportResult(result, error, result_text);
|
| - return hr;
|
| -}
|
| -
|
| -
|
| -void ProtData::UpdateUrl(const wchar_t* url) {
|
| - url_ = url;
|
| -}
|
| -
|
| -// S_FALSE - EOF
|
| -// S_OK - buffer fully filled
|
| -// E_PENDING - some data added to buffer, but buffer is not yet full
|
| -// E_XXXX - some other error.
|
| -HRESULT ProtData::FillBuffer() {
|
| - HRESULT hr_read = S_OK;
|
| -
|
| - while ((hr_read == S_OK) && (buffer_size_ < kMaxContentSniffLength)) {
|
| - ULONG size_read = 0;
|
| - hr_read = read_fun_(protocol_, buffer_ + buffer_size_,
|
| - kMaxContentSniffLength - buffer_size_, &size_read);
|
| - buffer_size_ += size_read;
|
| - }
|
| -
|
| - return hr_read;
|
| -}
|
| -
|
| -void ProtData::SaveSuggestedMimeType(LPCWSTR status_text) {
|
| - has_suggested_mime_type_ = true;
|
| - suggested_mime_type_.Allocate(status_text);
|
| -}
|
| -
|
| -void ProtData::FireSuggestedMimeType(IInternetProtocolSink* delegate) {
|
| - if (has_server_mime_type_) {
|
| - DVLOG(1) << "Forwarding BINDSTATUS_SERVER_MIMETYPEAVAILABLE "
|
| - << suggested_mime_type_;
|
| - delegate->ReportProgress(BINDSTATUS_SERVER_MIMETYPEAVAILABLE,
|
| - suggested_mime_type_);
|
| - }
|
| -
|
| - if (has_suggested_mime_type_) {
|
| - DVLOG(1) << "Forwarding BINDSTATUS_MIMETYPEAVAILABLE "
|
| - << suggested_mime_type_;
|
| - delegate->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE,
|
| - suggested_mime_type_);
|
| - }
|
| -}
|
| -
|
| -void ProtData::SaveReferrer(IInternetProtocolSink* delegate) {
|
| - DCHECK(IsChrome(renderer_type_));
|
| - base::win::ScopedComPtr<IWinInetHttpInfo> info;
|
| - info.QueryFrom(delegate);
|
| - 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);
|
| - } else {
|
| - DLOG(WARNING) << "Failed to QI for IWinInetHttpInfo";
|
| - }
|
| -}
|
| -
|
| -scoped_refptr<ProtData> ProtData::DataFromProtocol(
|
| - IInternetProtocol* protocol) {
|
| - scoped_refptr<ProtData> instance;
|
| - base::AutoLock lock(datamap_lock_);
|
| - ProtocolDataMap::iterator it = datamap_.find(protocol);
|
| - if (datamap_.end() != it)
|
| - instance = it->second;
|
| - return instance;
|
| -}
|
| -
|
| -void ProtData::Invalidate() {
|
| - if (protocol_) {
|
| - // Remove from map.
|
| - base::AutoLock lock(datamap_lock_);
|
| - DCHECK(datamap_.end() != datamap_.find(protocol_));
|
| - datamap_.erase(protocol_);
|
| - protocol_ = NULL;
|
| - }
|
| -}
|
| -
|
| -// This function looks for the url pattern indicating that this request needs
|
| -// to be forced into chrome frame.
|
| -// This hack is required because window.open requests from Chrome don't have
|
| -// the URL up front. The URL comes in much later when the renderer initiates a
|
| -// top level navigation for the url passed into window.open.
|
| -// The new page must be rendered in ChromeFrame to preserve the opener
|
| -// relationship with its parent even if the new page does not have the chrome
|
| -// meta tag.
|
| -bool HandleAttachToExistingExternalTab(LPCWSTR url,
|
| - IInternetProtocol* protocol,
|
| - IInternetProtocolSink* prot_sink,
|
| - IBindCtx* bind_ctx) {
|
| - ChromeFrameUrl cf_url;
|
| - if (cf_url.Parse(url) && cf_url.attach_to_external_tab()) {
|
| - scoped_refptr<ProtData> prot_data = ProtData::DataFromProtocol(protocol);
|
| - if (!prot_data) {
|
| - // Pass NULL as the read function which indicates that always return EOF
|
| - // without calling the underlying protocol.
|
| - prot_data = new ProtData(protocol, NULL, url);
|
| - PutProtData(bind_ctx, prot_data);
|
| - }
|
| -
|
| - prot_sink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, kChromeMimeType);
|
| -
|
| - int data_flags = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION;
|
| - prot_sink->ReportData(data_flags, 0, 0);
|
| -
|
| - prot_sink->ReportResult(S_OK, 0, NULL);
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -HRESULT ForwardHookStart(InternetProtocol_Start_Fn orig_start,
|
| - IInternetProtocol* protocol, LPCWSTR url, IInternetProtocolSink* prot_sink,
|
| - IInternetBindInfo* bind_info, DWORD flags, HANDLE_PTR reserved) {
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - return orig_start(protocol, url, prot_sink, bind_info, flags, reserved);
|
| -}
|
| -
|
| -HRESULT ForwardWrappedHookStart(InternetProtocol_Start_Fn orig_start,
|
| - IInternetProtocol* protocol, LPCWSTR url, IInternetProtocolSink* prot_sink,
|
| - IInternetBindInfo* bind_info, DWORD flags, HANDLE_PTR reserved) {
|
| - ExceptionBarrier barrier;
|
| - return orig_start(protocol, url, prot_sink, bind_info, flags, reserved);
|
| -}
|
| -
|
| -// IInternetProtocol/Ex hooks.
|
| -STDMETHODIMP Hook_Start(InternetProtocol_Start_Fn orig_start,
|
| - IInternetProtocol* protocol, LPCWSTR url, IInternetProtocolSink* prot_sink,
|
| - IInternetBindInfo* bind_info, DWORD flags, HANDLE_PTR reserved) {
|
| - DCHECK(orig_start);
|
| - if (!url || !prot_sink || !bind_info)
|
| - return E_INVALIDARG;
|
| - DVLOG_IF(1, url != NULL) << "OnStart: " << url << PiFlags2Str(flags);
|
| -
|
| - base::win::ScopedComPtr<IBindCtx> bind_ctx = BindCtxFromIBindInfo(bind_info);
|
| - if (!bind_ctx) {
|
| - // MSHTML sometimes takes a short path, skips the creation of
|
| - // moniker and binding, by directly grabbing protocol from InternetSession
|
| - DVLOG(1) << "DirectBind for " << url;
|
| - return ForwardHookStart(orig_start, protocol, url, prot_sink, bind_info,
|
| - flags, reserved);
|
| - }
|
| -
|
| - scoped_refptr<ProtData> prot_data = ProtData::DataFromProtocol(protocol);
|
| - if (prot_data && !HasProtData(bind_ctx)) {
|
| - prot_data->Invalidate();
|
| - prot_data = NULL;
|
| - }
|
| -
|
| - if (HandleAttachToExistingExternalTab(url, protocol, prot_sink, bind_ctx)) {
|
| - return S_OK;
|
| - }
|
| -
|
| - if (IsCFRequest(bind_ctx)) {
|
| - base::win::ScopedComPtr<BindContextInfo> info;
|
| - BindContextInfo::FromBindContext(bind_ctx, info.Receive());
|
| - DCHECK(info);
|
| - if (info) {
|
| - info->set_protocol(protocol);
|
| - }
|
| - return ForwardHookStart(orig_start, protocol, url, prot_sink, bind_info,
|
| - flags, reserved);
|
| - }
|
| -
|
| - if (prot_data) {
|
| - DVLOG(1) << "Found existing ProtData!";
|
| - prot_data->UpdateUrl(url);
|
| - base::win::ScopedComPtr<IInternetProtocolSink> new_sink =
|
| - ProtocolSinkWrap::CreateNewSink(prot_sink, prot_data);
|
| - return ForwardWrappedHookStart(orig_start, protocol, url, new_sink,
|
| - bind_info, flags, reserved);
|
| - }
|
| -
|
| - if (!ShouldWrapSink(prot_sink, url)) {
|
| - return ForwardHookStart(orig_start, protocol, url, prot_sink, bind_info,
|
| - flags, reserved);
|
| - }
|
| -
|
| - // Fresh request.
|
| - InternetProtocol_Read_Fn read_fun = reinterpret_cast<InternetProtocol_Read_Fn>
|
| - (CTransaction_PatchInfo[1].stub_->argument());
|
| - prot_data = new ProtData(protocol, read_fun, url);
|
| - PutProtData(bind_ctx, prot_data);
|
| -
|
| - base::win::ScopedComPtr<IInternetProtocolSink> new_sink =
|
| - ProtocolSinkWrap::CreateNewSink(prot_sink, prot_data);
|
| - return ForwardWrappedHookStart(orig_start, protocol, url, new_sink, bind_info,
|
| - flags, reserved);
|
| -}
|
| -
|
| -HRESULT ForwardHookStartEx(InternetProtocol_StartEx_Fn orig_start_ex,
|
| - IInternetProtocolEx* protocol, IUri* uri, IInternetProtocolSink* prot_sink,
|
| - IInternetBindInfo* bind_info, DWORD flags, HANDLE_PTR reserved) {
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - return orig_start_ex(protocol, uri, prot_sink, bind_info, flags, reserved);
|
| -}
|
| -
|
| -HRESULT ForwardWrappedHookStartEx(InternetProtocol_StartEx_Fn orig_start_ex,
|
| - IInternetProtocolEx* protocol, IUri* uri, IInternetProtocolSink* prot_sink,
|
| - IInternetBindInfo* bind_info, DWORD flags, HANDLE_PTR reserved) {
|
| - ExceptionBarrier barrier;
|
| - return orig_start_ex(protocol, uri, prot_sink, bind_info, flags, reserved);
|
| -}
|
| -
|
| -STDMETHODIMP Hook_StartEx(InternetProtocol_StartEx_Fn orig_start_ex,
|
| - IInternetProtocolEx* protocol, IUri* uri, IInternetProtocolSink* prot_sink,
|
| - IInternetBindInfo* bind_info, DWORD flags, HANDLE_PTR reserved) {
|
| - DCHECK(orig_start_ex);
|
| - if (!uri || !prot_sink || !bind_info)
|
| - return E_INVALIDARG;
|
| -
|
| - base::win::ScopedBstr url;
|
| - uri->GetPropertyBSTR(Uri_PROPERTY_ABSOLUTE_URI, url.Receive(), 0);
|
| - DVLOG_IF(1, url != NULL) << "OnStartEx: " << url << PiFlags2Str(flags);
|
| -
|
| - base::win::ScopedComPtr<IBindCtx> bind_ctx = BindCtxFromIBindInfo(bind_info);
|
| - if (!bind_ctx) {
|
| - // MSHTML sometimes takes a short path, skips the creation of
|
| - // moniker and binding, by directly grabbing protocol from InternetSession.
|
| - DVLOG(1) << "DirectBind for " << url;
|
| - return ForwardHookStartEx(orig_start_ex, protocol, uri, prot_sink,
|
| - bind_info, flags, reserved);
|
| - }
|
| -
|
| - scoped_refptr<ProtData> prot_data = ProtData::DataFromProtocol(protocol);
|
| - if (prot_data && !HasProtData(bind_ctx)) {
|
| - prot_data->Invalidate();
|
| - prot_data = NULL;
|
| - }
|
| -
|
| - if (HandleAttachToExistingExternalTab(url, protocol, prot_sink, bind_ctx)) {
|
| - return S_OK;
|
| - }
|
| -
|
| - if (IsCFRequest(bind_ctx)) {
|
| - base::win::ScopedComPtr<BindContextInfo> info;
|
| - BindContextInfo::FromBindContext(bind_ctx, info.Receive());
|
| - DCHECK(info);
|
| - if (info) {
|
| - info->set_protocol(protocol);
|
| - }
|
| - return ForwardHookStartEx(orig_start_ex, protocol, uri, prot_sink,
|
| - bind_info, flags, reserved);
|
| - }
|
| -
|
| - if (prot_data) {
|
| - DVLOG(1) << "Found existing ProtData!";
|
| - prot_data->UpdateUrl(url);
|
| - base::win::ScopedComPtr<IInternetProtocolSink> new_sink =
|
| - ProtocolSinkWrap::CreateNewSink(prot_sink, prot_data);
|
| - return ForwardWrappedHookStartEx(orig_start_ex, protocol, uri, new_sink,
|
| - bind_info, flags, reserved);
|
| - }
|
| -
|
| - if (!ShouldWrapSink(prot_sink, url)) {
|
| - return ForwardHookStartEx(orig_start_ex, protocol, uri, prot_sink,
|
| - bind_info, flags, reserved);
|
| - }
|
| -
|
| - // Fresh request.
|
| - InternetProtocol_Read_Fn read_fun = reinterpret_cast<InternetProtocol_Read_Fn>
|
| - (CTransaction_PatchInfo[1].stub_->argument());
|
| - prot_data = new ProtData(protocol, read_fun, url);
|
| - PutProtData(bind_ctx, prot_data);
|
| -
|
| - base::win::ScopedComPtr<IInternetProtocolSink> new_sink =
|
| - ProtocolSinkWrap::CreateNewSink(prot_sink, prot_data);
|
| - return ForwardWrappedHookStartEx(orig_start_ex, protocol, uri, new_sink,
|
| - bind_info, flags, reserved);
|
| -}
|
| -
|
| -STDMETHODIMP Hook_Read(InternetProtocol_Read_Fn orig_read,
|
| - IInternetProtocol* protocol, void* buffer, ULONG size, ULONG* size_read) {
|
| - DCHECK(orig_read);
|
| - HRESULT hr = E_FAIL;
|
| -
|
| - scoped_refptr<ProtData> prot_data = ProtData::DataFromProtocol(protocol);
|
| - if (!prot_data) {
|
| - // We are not wrapping this request, avoid false positive crash reports.
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - hr = orig_read(protocol, buffer, size, size_read);
|
| - return hr;
|
| - }
|
| -
|
| - if (prot_data->is_attach_external_tab_request()) {
|
| - // return EOF always.
|
| - if (size_read)
|
| - *size_read = 0;
|
| - return S_FALSE;
|
| - }
|
| -
|
| - hr = prot_data->Read(buffer, size, size_read);
|
| - return hr;
|
| -}
|
| -
|
| -STDMETHODIMP Hook_LockRequest(InternetProtocol_LockRequest_Fn orig_req,
|
| - IInternetProtocol* protocol,
|
| - DWORD options) {
|
| - DCHECK(orig_req);
|
| -
|
| - scoped_refptr<ProtData> prot_data = ProtData::DataFromProtocol(protocol);
|
| - if (prot_data && prot_data->is_attach_external_tab_request()) {
|
| - prot_data->AddRef();
|
| - return S_OK;
|
| - }
|
| -
|
| - // We are just pass through at this point, avoid false positive crash
|
| - // reports.
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - return orig_req(protocol, options);
|
| -}
|
| -
|
| -STDMETHODIMP Hook_UnlockRequest(InternetProtocol_UnlockRequest_Fn orig_req,
|
| - IInternetProtocol* protocol) {
|
| - DCHECK(orig_req);
|
| -
|
| - scoped_refptr<ProtData> prot_data = ProtData::DataFromProtocol(protocol);
|
| - if (prot_data && prot_data->is_attach_external_tab_request()) {
|
| - prot_data->Release();
|
| - return S_OK;
|
| - }
|
| -
|
| - // We are just pass through at this point, avoid false positive crash
|
| - // reports.
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - return orig_req(protocol);
|
| -}
|
| -
|
| -STDMETHODIMP Hook_Abort(InternetProtocol_Abort_Fn orig_req,
|
| - IInternetProtocol* protocol,
|
| - HRESULT hr,
|
| - DWORD options) {
|
| - scoped_refptr<ProtData> prot_data = ProtData::DataFromProtocol(protocol);
|
| - if (prot_data)
|
| - prot_data->Invalidate();
|
| -
|
| - // We are just pass through at this point, avoid false positive crash
|
| - // reports.
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - return orig_req(protocol, hr, options);
|
| -}
|
| -
|
| -STDMETHODIMP Hook_Terminate(InternetProtocol_Terminate_Fn orig_req,
|
| - IInternetProtocol* protocol,
|
| - DWORD options) {
|
| - scoped_refptr<ProtData> prot_data = ProtData::DataFromProtocol(protocol);
|
| - // We should not be invalidating the cached protocol data in the following
|
| - // cases:-
|
| - // 1. Pages which are switching into ChromeFrame.
|
| - // When IE switches into ChromeFrame, we report the Chrome mime type as
|
| - // the handler for the page. This results in urlmon terminating the
|
| - // protocol. When Chrome attempts to read the data we need to report the
|
| - // cached data back to Chrome.
|
| - // 2. For the attach external tab requests which are temporary navigations
|
| - // to ensure that a top level URL is opened in IE from ChromeFrame.
|
| - // We rely on the mapping to identify these requests as attach tab
|
| - // requests. This mapping is referred to in the
|
| - // IInternetProtocol::LockRequest/IInternetProtocol::UnlockRequest
|
| - // intercepts. Invalidating the mapping after LockRequest is called and
|
| - // before UnlockRequest causes IE to crash.
|
| - if (prot_data && !IsChrome(prot_data->renderer_type()) &&
|
| - !prot_data->is_attach_external_tab_request())
|
| - prot_data->Invalidate();
|
| -
|
| - // We are just pass through at this point, avoid false positive crash
|
| - // reports.
|
| - ExceptionBarrierReportOnlyModule barrier;
|
| - return orig_req(protocol, options);
|
| -}
|
| -
|
| -// Patching / Hooking code.
|
| -class FakeProtocol : public CComObjectRootEx<CComSingleThreadModel>,
|
| - public IInternetProtocol {
|
| - public:
|
| - BEGIN_COM_MAP(FakeProtocol)
|
| - COM_INTERFACE_ENTRY(IInternetProtocol)
|
| - COM_INTERFACE_ENTRY(IInternetProtocolRoot)
|
| - END_COM_MAP()
|
| -
|
| - STDMETHOD(Start)(LPCWSTR url, IInternetProtocolSink *protocol_sink,
|
| - IInternetBindInfo* bind_info, DWORD flags, HANDLE_PTR reserved) {
|
| - transaction_.QueryFrom(protocol_sink);
|
| - // Return some unusual error code.
|
| - return INET_E_INVALID_CERTIFICATE;
|
| - }
|
| -
|
| - STDMETHOD(Continue)(PROTOCOLDATA* protocol_data) { return S_OK; }
|
| - STDMETHOD(Abort)(HRESULT reason, DWORD options) { return S_OK; }
|
| - STDMETHOD(Terminate)(DWORD options) { return S_OK; }
|
| - STDMETHOD(Suspend)() { return S_OK; }
|
| - STDMETHOD(Resume)() { return S_OK; }
|
| - STDMETHOD(Read)(void *buffer, ULONG size, ULONG* size_read) { return S_OK; }
|
| - STDMETHOD(Seek)(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER* new_pos)
|
| - { return S_OK; }
|
| - STDMETHOD(LockRequest)(DWORD options) { return S_OK; }
|
| - STDMETHOD(UnlockRequest)() { return S_OK; }
|
| -
|
| - base::win::ScopedComPtr<IInternetProtocol> transaction_;
|
| -};
|
| -
|
| -struct FakeFactory : public IClassFactory,
|
| - public CComObjectRootEx<CComSingleThreadModel> {
|
| - BEGIN_COM_MAP(FakeFactory)
|
| - COM_INTERFACE_ENTRY(IClassFactory)
|
| - END_COM_MAP()
|
| -
|
| - STDMETHOD(CreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) {
|
| - if (pUnkOuter)
|
| - return CLASS_E_NOAGGREGATION;
|
| - HRESULT hr = obj_->QueryInterface(riid, ppvObj);
|
| - return hr;
|
| - }
|
| -
|
| - STDMETHOD(LockServer)(BOOL fLock) {
|
| - return S_OK;
|
| - }
|
| -
|
| - IUnknown* obj_;
|
| -};
|
| -
|
| -static void HookTransactionVtable(IInternetProtocol* p) {
|
| - base::win::ScopedComPtr<IInternetProtocolEx> ex;
|
| - ex.QueryFrom(p);
|
| -
|
| - HRESULT hr = vtable_patch::PatchInterfaceMethods(p, CTransaction_PatchInfo);
|
| - if (hr == S_OK && ex) {
|
| - vtable_patch::PatchInterfaceMethods(ex.get(), CTransaction2_PatchInfo);
|
| - }
|
| -}
|
| -
|
| -void TransactionHooks::InstallHooks() {
|
| - if (IS_PATCHED(CTransaction)) {
|
| - DLOG(WARNING) << __FUNCTION__ << " called more than once.";
|
| - return;
|
| - }
|
| -
|
| - CComObjectStackEx<FakeProtocol> prot;
|
| - CComObjectStackEx<FakeFactory> factory;
|
| - factory.obj_ = &prot;
|
| - base::win::ScopedComPtr<IInternetSession> session;
|
| - HRESULT hr = ::CoInternetGetSession(0, session.Receive(), 0);
|
| - hr = session->RegisterNameSpace(&factory, CLSID_NULL, L"611", 0, 0, 0);
|
| - DLOG_IF(FATAL, FAILED(hr)) << "Failed to register namespace";
|
| - if (hr != S_OK)
|
| - return;
|
| -
|
| - do {
|
| - base::win::ScopedComPtr<IMoniker> mk;
|
| - base::win::ScopedComPtr<IBindCtx> bc;
|
| - base::win::ScopedComPtr<IStream> stream;
|
| - hr = ::CreateAsyncBindCtxEx(0, 0, 0, 0, bc.Receive(), 0);
|
| - DLOG_IF(FATAL, FAILED(hr)) << "CreateAsyncBindCtxEx failed " << hr;
|
| - if (hr != S_OK)
|
| - break;
|
| -
|
| - hr = ::CreateURLMoniker(NULL, L"611://512", mk.Receive());
|
| - DLOG_IF(FATAL, FAILED(hr)) << "CreateURLMoniker failed " << hr;
|
| - if (hr != S_OK)
|
| - break;
|
| -
|
| - hr = mk->BindToStorage(bc, NULL, IID_IStream,
|
| - reinterpret_cast<void**>(stream.Receive()));
|
| - DLOG_IF(FATAL, hr != INET_E_INVALID_CERTIFICATE) <<
|
| - "BindToStorage failed " << hr;
|
| - } while (0);
|
| -
|
| - hr = session->UnregisterNameSpace(&factory, L"611");
|
| - if (prot.transaction_) {
|
| - HookTransactionVtable(prot.transaction_);
|
| - // Explicit release, otherwise ~CComObjectStackEx will complain about
|
| - // outstanding reference to us, because it runs before ~FakeProtocol
|
| - prot.transaction_.Release();
|
| - }
|
| -}
|
| -
|
| -void TransactionHooks::RevertHooks() {
|
| - vtable_patch::UnpatchInterfaceMethods(CTransaction_PatchInfo);
|
| - vtable_patch::UnpatchInterfaceMethods(CTransaction2_PatchInfo);
|
| -}
|
|
|