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); |
-} |