1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "chrome_frame/protocol_sink_wrap.h"
11 #include "base/logging.h"
12 #include "base/memory/singleton.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/scoped_bstr.h"
18 #include "chrome_frame/bho.h"
19 #include "chrome_frame/bind_context_info.h"
20 #include "chrome_frame/exception_barrier.h"
21 #include "chrome_frame/function_stub.h"
22 #include "chrome_frame/policy_settings.h"
23 #include "chrome_frame/utils.h"
27 // BINDSTATUS_SERVER_MIMETYPEAVAILABLE == 54. Introduced in IE 8, so
28 // not in everyone's headers yet. See:
29 // http://msdn.microsoft.com/en-us/library/ms775133(VS.85,loband).aspx
30 #ifndef BINDSTATUS_SERVER_MIMETYPEAVAILABLE
31 #define BINDSTATUS_SERVER_MIMETYPEAVAILABLE 54
34 bool ProtocolSinkWrap::ignore_xua_
= false;
36 static const char kTextHtmlMimeType
[] = "text/html";
37 const wchar_t kUrlMonDllName
[] = L
"urlmon.dll";
39 static const int kInternetProtocolStartIndex
= 3;
40 static const int kInternetProtocolReadIndex
= 9;
41 static const int kInternetProtocolStartExIndex
= 13;
42 static const int kInternetProtocolLockRequestIndex
= 11;
43 static const int kInternetProtocolUnlockRequestIndex
= 12;
44 static const int kInternetProtocolAbortIndex
= 5;
45 static const int kInternetProtocolTerminateIndex
= 6;
48 // IInternetProtocol/Ex patches.
49 STDMETHODIMP
Hook_Start(InternetProtocol_Start_Fn orig_start
,
50 IInternetProtocol
* protocol
,
52 IInternetProtocolSink
* prot_sink
,
53 IInternetBindInfo
* bind_info
,
57 STDMETHODIMP
Hook_StartEx(InternetProtocol_StartEx_Fn orig_start_ex
,
58 IInternetProtocolEx
* protocol
,
60 IInternetProtocolSink
* prot_sink
,
61 IInternetBindInfo
* bind_info
,
65 STDMETHODIMP
Hook_Read(InternetProtocol_Read_Fn orig_read
,
66 IInternetProtocol
* protocol
,
71 STDMETHODIMP
Hook_LockRequest(InternetProtocol_LockRequest_Fn orig_req
,
72 IInternetProtocol
* protocol
,
75 STDMETHODIMP
Hook_UnlockRequest(InternetProtocol_UnlockRequest_Fn orig_req
,
76 IInternetProtocol
* protocol
);
78 STDMETHODIMP
Hook_Abort(InternetProtocol_Abort_Fn orig_req
,
79 IInternetProtocol
* protocol
,
83 STDMETHODIMP
Hook_Terminate(InternetProtocol_Terminate_Fn orig_req
,
84 IInternetProtocol
* protocol
,
87 /////////////////////////////////////////////////////////////////////////////
88 BEGIN_VTABLE_PATCHES(CTransaction
)
89 VTABLE_PATCH_ENTRY(kInternetProtocolStartIndex
, Hook_Start
)
90 VTABLE_PATCH_ENTRY(kInternetProtocolReadIndex
, Hook_Read
)
91 VTABLE_PATCH_ENTRY(kInternetProtocolLockRequestIndex
, Hook_LockRequest
)
92 VTABLE_PATCH_ENTRY(kInternetProtocolUnlockRequestIndex
, Hook_UnlockRequest
)
93 VTABLE_PATCH_ENTRY(kInternetProtocolAbortIndex
, Hook_Abort
)
94 VTABLE_PATCH_ENTRY(kInternetProtocolTerminateIndex
, Hook_Terminate
)
97 BEGIN_VTABLE_PATCHES(CTransaction2
)
98 VTABLE_PATCH_ENTRY(kInternetProtocolStartExIndex
, Hook_StartEx
)
102 // ProtocolSinkWrap implementation
104 // Static map initialization
105 ProtData::ProtocolDataMap
ProtData::datamap_
;
106 base::Lock
ProtData::datamap_lock_
;
108 ProtocolSinkWrap::ProtocolSinkWrap() {
109 DVLOG(1) << __FUNCTION__
<< base::StringPrintf(" 0x%08X", this);
112 ProtocolSinkWrap::~ProtocolSinkWrap() {
113 DVLOG(1) << __FUNCTION__
<< base::StringPrintf(" 0x%08X", this);
116 base::win::ScopedComPtr
<IInternetProtocolSink
> ProtocolSinkWrap::CreateNewSink(
117 IInternetProtocolSink
* sink
, ProtData
* data
) {
118 DCHECK(sink
!= NULL
);
119 DCHECK(data
!= NULL
);
120 CComObject
<ProtocolSinkWrap
>* new_sink
= NULL
;
121 CComObject
<ProtocolSinkWrap
>::CreateInstance(&new_sink
);
122 new_sink
->delegate_
= sink
;
123 new_sink
->prot_data_
= data
;
124 return base::win::ScopedComPtr
<IInternetProtocolSink
>(new_sink
);
127 // IInternetProtocolSink methods
128 STDMETHODIMP
ProtocolSinkWrap::Switch(PROTOCOLDATA
* protocol_data
) {
131 hr
= delegate_
->Switch(protocol_data
);
135 STDMETHODIMP
ProtocolSinkWrap::ReportProgress(ULONG status_code
,
136 LPCWSTR status_text
) {
137 DVLOG(1) << "ProtocolSinkWrap::ReportProgress: "
138 << BindStatus2Str(status_code
)
139 << " Status: " << (status_text
? status_text
: L
"");
141 HRESULT hr
= prot_data_
->ReportProgress(delegate_
, status_code
, status_text
);
145 STDMETHODIMP
ProtocolSinkWrap::ReportData(DWORD flags
, ULONG progress
,
146 ULONG max_progress
) {
148 DVLOG(1) << "ProtocolSinkWrap::ReportData: " << Bscf2Str(flags
)
149 << " progress: " << progress
<< " progress_max: " << max_progress
;
151 HRESULT hr
= prot_data_
->ReportData(delegate_
, flags
, progress
, max_progress
);
155 STDMETHODIMP
ProtocolSinkWrap::ReportResult(HRESULT result
, DWORD error
,
156 LPCWSTR result_text
) {
157 DVLOG(1) << "ProtocolSinkWrap::ReportResult: result: " << result
158 << " error: " << error
159 << " Text: " << (result_text
? result_text
: L
"");
160 ExceptionBarrier barrier
;
161 HRESULT hr
= prot_data_
->ReportResult(delegate_
, result
, error
, result_text
);
167 base::win::ScopedComPtr
<IBindCtx
> BindCtxFromIBindInfo(
168 IInternetBindInfo
* bind_info
) {
169 LPOLESTR bind_ctx_string
= NULL
;
171 base::win::ScopedComPtr
<IBindCtx
> bind_ctx
;
172 bind_info
->GetBindString(BINDSTRING_PTR_BIND_CONTEXT
, &bind_ctx_string
, 1,
174 if (bind_ctx_string
) {
176 base::StringToInt(bind_ctx_string
, &bind_ctx_int
);
177 IBindCtx
* pbc
= reinterpret_cast<IBindCtx
*>(bind_ctx_int
);
178 bind_ctx
.Attach(pbc
);
179 CoTaskMemFree(bind_ctx_string
);
185 bool ShouldWrapSink(IInternetProtocolSink
* sink
, const wchar_t* url
) {
186 // Ignore everything that does not start with http:// or https://.
187 // |url| is already normalized (i.e. no leading spaces, capital letters in
188 // protocol etc) and non-null (we check in Hook_Start).
191 if (ProtocolSinkWrap::ignore_xua())
192 return false; // No need to intercept, we're ignoring X-UA-Compatible tags
194 if ((url
!= StrStrW(url
, L
"http://")) && (url
!= StrStrW(url
, L
"https://")))
197 base::win::ScopedComPtr
<IHttpNegotiate
> http_negotiate
;
198 HRESULT hr
= DoQueryService(GUID_NULL
, sink
, http_negotiate
.Receive());
199 if (http_negotiate
&& !IsSubFrameRequest(http_negotiate
))
205 // High level helpers
206 bool IsCFRequest(IBindCtx
* pbc
) {
207 base::win::ScopedComPtr
<BindContextInfo
> info
;
208 BindContextInfo::FromBindContext(pbc
, info
.Receive());
209 if (info
&& info
->chrome_request())
215 bool HasProtData(IBindCtx
* pbc
) {
216 base::win::ScopedComPtr
<BindContextInfo
> info
;
217 BindContextInfo::FromBindContext(pbc
, info
.Receive());
220 result
= info
->has_prot_data();
224 void PutProtData(IBindCtx
* pbc
, ProtData
* data
) {
225 // AddRef and Release to avoid a potential leak of a ProtData instance if
226 // FromBindContext fails.
228 base::win::ScopedComPtr
<BindContextInfo
> info
;
229 BindContextInfo::FromBindContext(pbc
, info
.Receive());
231 info
->set_prot_data(data
);
235 bool IsTextHtml(const wchar_t* status_text
) {
236 const std::wstring str
= status_text
;
237 bool is_text_html
= LowerCaseEqualsASCII(str
, kTextHtmlMimeType
);
241 bool IsAdditionallySupportedContentType(const wchar_t* status_text
) {
242 static const char* kHeaderContentTypes
[] = {
243 "application/xhtml+xml",
253 const std::wstring str
= status_text
;
254 for (int i
= 0; i
< arraysize(kHeaderContentTypes
); ++i
) {
255 if (LowerCaseEqualsASCII(str
, kHeaderContentTypes
[i
]))
259 if (PolicySettings::GetInstance()->GetRendererForContentType(
260 status_text
) == PolicySettings::RENDER_IN_CHROME_FRAME
) {
268 // RENDERER_TYPE_OTHER: if suggested mime type is not text/html.
269 // RENDERER_TYPE_UNDETERMINED: if suggested mime type is text/html.
270 // RENDERER_TYPE_CHROME_RESPONSE_HEADER: X-UA-Compatible tag is in HTTP headers.
271 // RENDERER_TYPE_CHROME_DEFAULT_RENDERER: GCF is the default renderer and the
272 // Url is not listed in the
273 // RenderInHostUrls registry key.
274 // RENDERER_TYPE_CHROME_OPT_IN_URL: GCF is not the default renderer and the Url
275 // is listed in the RenderInGcfUrls registry
277 RendererType
DetermineRendererTypeFromMetaData(
278 const wchar_t* suggested_mime_type
,
279 const std::wstring
& url
,
280 IWinInetHttpInfo
* info
) {
281 bool is_text_html
= IsTextHtml(suggested_mime_type
);
282 bool is_supported_content_type
= is_text_html
||
283 IsAdditionallySupportedContentType(suggested_mime_type
);
285 if (!is_supported_content_type
)
286 return RENDERER_TYPE_OTHER
;
289 RendererType renderer_type
= RendererTypeForUrl(url
);
290 if (IsChrome(renderer_type
)) {
291 return renderer_type
;
296 char buffer
[512] = "x-ua-compatible";
297 DWORD len
= sizeof(buffer
);
299 HRESULT hr
= info
->QueryInfo(HTTP_QUERY_CUSTOM
, buffer
, &len
, &flags
, NULL
);
301 if (hr
== S_OK
&& len
> 0) {
302 if (CheckXUaCompatibleDirective(buffer
, GetIEMajorVersion())) {
303 return RENDERER_TYPE_CHROME_RESPONSE_HEADER
;
308 // We can (and want) to sniff the content.
310 return RENDERER_TYPE_UNDETERMINED
;
313 // We cannot sniff the content.
314 return RENDERER_TYPE_OTHER
;
317 RendererType
DetermineRendererType(void* buffer
, DWORD size
, bool last_chance
) {
318 RendererType renderer_type
= RENDERER_TYPE_UNDETERMINED
;
320 renderer_type
= RENDERER_TYPE_OTHER
;
322 std::wstring html_contents
;
323 // TODO(joshia): detect and handle different content encodings
324 UTF8ToWide(reinterpret_cast<char*>(buffer
), size
, &html_contents
);
326 // Note that document_contents_ may have NULL characters in it. While
327 // browsers may handle this properly, we don't and will stop scanning
328 // for the XUACompat content value if we encounter one.
329 std::wstring xua_compat_content
;
330 if (SUCCEEDED(UtilGetXUACompatContentValue(html_contents
,
331 &xua_compat_content
))) {
332 if (CheckXUaCompatibleDirective(WideToASCII(xua_compat_content
),
333 GetIEMajorVersion())) {
334 renderer_type
= RENDERER_TYPE_CHROME_HTTP_EQUIV
;
338 return renderer_type
;
342 ProtData::ProtData(IInternetProtocol
* protocol
,
343 InternetProtocol_Read_Fn read_fun
, const wchar_t* url
)
344 : has_suggested_mime_type_(false), has_server_mime_type_(false),
345 buffer_size_(0), buffer_pos_(0),
346 renderer_type_(RENDERER_TYPE_UNDETERMINED
), protocol_(protocol
),
347 read_fun_(read_fun
), url_(url
) {
348 memset(buffer_
, 0, arraysize(buffer_
));
349 DVLOG(1) << __FUNCTION__
<< " " << this;
352 base::AutoLock
lock(datamap_lock_
);
353 DCHECK(datamap_
.end() == datamap_
.find(protocol_
));
354 datamap_
[protocol
] = this;
357 ProtData::~ProtData() {
358 DVLOG(1) << __FUNCTION__
<< " " << this;
362 HRESULT
ProtData::Read(void* buffer
, ULONG size
, ULONG
* size_read
) {
363 if (renderer_type_
== RENDERER_TYPE_UNDETERMINED
) {
367 const ULONG bytes_available
= buffer_size_
- buffer_pos_
;
368 const ULONG bytes_to_copy
= std::min(bytes_available
, size
);
370 // Copy from the local buffer.
371 memcpy(buffer
, buffer_
+ buffer_pos_
, bytes_to_copy
);
372 *size_read
= bytes_to_copy
;
373 buffer_pos_
+= bytes_to_copy
;
377 if (size
> bytes_available
) {
378 // User buffer is greater than what we have.
379 buffer
= reinterpret_cast<uint8
*>(buffer
) + bytes_to_copy
;
380 size
-= bytes_to_copy
;
381 hr
= read_fun_(protocol_
, buffer
, size
, &new_data
);
385 *size_read
= bytes_to_copy
+ new_data
;
389 return read_fun_(protocol_
, buffer
, size
, size_read
);
392 // Attempt to detect ChromeFrame from HTTP headers when
393 // BINDSTATUS_MIMETYPEAVAILABLE is received.
394 // There are three possible outcomes: CHROME_*/OTHER/UNDETERMINED.
395 // If RENDERER_TYPE_UNDETERMINED we are going to sniff the content later in
398 // With not-so-well-written software (mime filters/protocols/protocol patchers)
399 // BINDSTATUS_MIMETYPEAVAILABLE might be fired multiple times with different
400 // values for the same client.
401 // If the renderer_type_ member is:
402 // RENDERER_TYPE_CHROME_* - 2nd (and any subsequent)
403 // BINDSTATUS_MIMETYPEAVAILABLE is ignored.
404 // RENDERER_TYPE_OTHER - 2nd (and any subsequent) BINDSTATUS_MIMETYPEAVAILABLE
405 // is passed through.
406 // RENDERER_TYPE_UNDETERMINED - Try to detect ChromeFrame from HTTP headers
407 // (acts as if this is the first time
408 // BINDSTATUS_MIMETYPEAVAILABLE is received).
409 HRESULT
ProtData::ReportProgress(IInternetProtocolSink
* delegate
,
410 ULONG status_code
, LPCWSTR status_text
) {
411 switch (status_code
) {
412 case BINDSTATUS_DIRECTBIND
:
413 renderer_type_
= RENDERER_TYPE_OTHER
;
416 case BINDSTATUS_REDIRECTING
:
422 case BINDSTATUS_SERVER_MIMETYPEAVAILABLE
:
423 has_server_mime_type_
= true;
424 SaveSuggestedMimeType(status_text
);
427 // TODO(stoyan): BINDSTATUS_RAWMIMETYPE
428 case BINDSTATUS_MIMETYPEAVAILABLE
:
429 case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE
:
430 SaveSuggestedMimeType(status_text
);
431 // When Transaction is attached i.e. when existing BTS it terminated
432 // and "converted" to BTO, events will be re-fired for the new sink,
433 // but we may skip the renderer_type_ determination since it's already
435 if (renderer_type_
== RENDERER_TYPE_UNDETERMINED
) {
436 // This may seem awkward. CBinding's implementation of IWinInetHttpInfo
437 // will forward to CTransaction that will forward to the real protocol.
438 // We may ask CTransaction (our protocol_ member) for IWinInetHttpInfo.
439 base::win::ScopedComPtr
<IWinInetHttpInfo
> info
;
440 info
.QueryFrom(delegate
);
441 renderer_type_
= DetermineRendererTypeFromMetaData(suggested_mime_type_
,
445 if (IsChrome(renderer_type_
)) {
446 // Suggested mime type is "text/html" and we have DEFAULT_RENDERER,
447 // OPT_IN_URL, or RESPONSE_HEADER.
448 DVLOG(1) << "Forwarding BINDSTATUS_MIMETYPEAVAILABLE "
450 SaveReferrer(delegate
);
451 delegate
->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE
, kChromeMimeType
);
452 } else if (renderer_type_
== RENDERER_TYPE_OTHER
) {
453 // Suggested mime type is not "text/html" - we are not interested in
454 // this request anymore.
455 FireSuggestedMimeType(delegate
);
457 // Suggested mime type is "text/html"; We will try to sniff the
458 // HTML content in ReportData.
459 DCHECK_EQ(RENDERER_TYPE_UNDETERMINED
, renderer_type_
);
464 // We are just pass through at this point, avoid false positive crash reports.
465 ExceptionBarrierReportOnlyModule barrier
;
466 return delegate
->ReportProgress(status_code
, status_text
);
469 HRESULT
ProtData::ReportData(IInternetProtocolSink
* delegate
,
470 DWORD flags
, ULONG progress
, ULONG max_progress
) {
471 if (renderer_type_
!= RENDERER_TYPE_UNDETERMINED
) {
472 // We are just pass through now, avoid false positive crash reports.
473 ExceptionBarrierReportOnlyModule barrier
;
474 return delegate
->ReportData(flags
, progress
, max_progress
);
477 HRESULT hr
= FillBuffer();
479 bool last_chance
= false;
480 if (hr
== S_OK
|| hr
== S_FALSE
) {
484 renderer_type_
= SkipMetadataCheck() ? RENDERER_TYPE_OTHER
485 : DetermineRendererType(buffer_
, buffer_size_
, last_chance
);
487 if (renderer_type_
== RENDERER_TYPE_UNDETERMINED
) {
488 // do not report anything, we need more data.
492 if (IsChrome(renderer_type_
)) {
493 DVLOG(1) << "Forwarding BINDSTATUS_MIMETYPEAVAILABLE " << kChromeMimeType
;
494 SaveReferrer(delegate
);
495 delegate
->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE
, kChromeMimeType
);
498 if (renderer_type_
== RENDERER_TYPE_OTHER
) {
499 FireSuggestedMimeType(delegate
);
502 // This is the first data notification we forward, since up to now we hold
503 // the content received.
504 flags
|= BSCF_FIRSTDATANOTIFICATION
;
507 flags
|= (BSCF_LASTDATANOTIFICATION
| BSCF_DATAFULLYAVAILABLE
);
510 return delegate
->ReportData(flags
, progress
, max_progress
);
513 HRESULT
ProtData::ReportResult(IInternetProtocolSink
* delegate
, HRESULT result
,
514 DWORD error
, LPCWSTR result_text
) {
515 // We may receive ReportResult without ReportData, if the connection fails
517 if (renderer_type_
== RENDERER_TYPE_UNDETERMINED
) {
518 DVLOG(1) << "ReportResult received but renderer type is yet unknown.";
519 renderer_type_
= RENDERER_TYPE_OTHER
;
520 FireSuggestedMimeType(delegate
);
525 hr
= delegate
->ReportResult(result
, error
, result_text
);
530 void ProtData::UpdateUrl(const wchar_t* url
) {
535 // S_OK - buffer fully filled
536 // E_PENDING - some data added to buffer, but buffer is not yet full
537 // E_XXXX - some other error.
538 HRESULT
ProtData::FillBuffer() {
539 HRESULT hr_read
= S_OK
;
541 while ((hr_read
== S_OK
) && (buffer_size_
< kMaxContentSniffLength
)) {
543 hr_read
= read_fun_(protocol_
, buffer_
+ buffer_size_
,
544 kMaxContentSniffLength
- buffer_size_
, &size_read
);
545 buffer_size_
+= size_read
;
551 void ProtData::SaveSuggestedMimeType(LPCWSTR status_text
) {
552 has_suggested_mime_type_
= true;
553 suggested_mime_type_
.Allocate(status_text
);
556 void ProtData::FireSuggestedMimeType(IInternetProtocolSink
* delegate
) {
557 if (has_server_mime_type_
) {
558 DVLOG(1) << "Forwarding BINDSTATUS_SERVER_MIMETYPEAVAILABLE "
559 << suggested_mime_type_
;
560 delegate
->ReportProgress(BINDSTATUS_SERVER_MIMETYPEAVAILABLE
,
561 suggested_mime_type_
);
564 if (has_suggested_mime_type_
) {
565 DVLOG(1) << "Forwarding BINDSTATUS_MIMETYPEAVAILABLE "
566 << suggested_mime_type_
;
567 delegate
->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE
,
568 suggested_mime_type_
);
572 void ProtData::SaveReferrer(IInternetProtocolSink
* delegate
) {
573 DCHECK(IsChrome(renderer_type_
));
574 base::win::ScopedComPtr
<IWinInetHttpInfo
> info
;
575 info
.QueryFrom(delegate
);
577 char buffer
[4096] = {0};
578 DWORD len
= sizeof(buffer
);
580 HRESULT hr
= info
->QueryInfo(
581 HTTP_QUERY_REFERER
| HTTP_QUERY_FLAG_REQUEST_HEADERS
,
582 buffer
, &len
, &flags
, 0);
583 if (hr
== S_OK
&& len
> 0)
584 referrer_
.assign(buffer
);
586 DLOG(WARNING
) << "Failed to QI for IWinInetHttpInfo";
590 scoped_refptr
<ProtData
> ProtData::DataFromProtocol(
591 IInternetProtocol
* protocol
) {
592 scoped_refptr
<ProtData
> instance
;
593 base::AutoLock
lock(datamap_lock_
);
594 ProtocolDataMap::iterator it
= datamap_
.find(protocol
);
595 if (datamap_
.end() != it
)
596 instance
= it
->second
;
600 void ProtData::Invalidate() {
603 base::AutoLock
lock(datamap_lock_
);
604 DCHECK(datamap_
.end() != datamap_
.find(protocol_
));
605 datamap_
.erase(protocol_
);
610 // This function looks for the url pattern indicating that this request needs
611 // to be forced into chrome frame.
612 // This hack is required because window.open requests from Chrome don't have
613 // the URL up front. The URL comes in much later when the renderer initiates a
614 // top level navigation for the url passed into window.open.
615 // The new page must be rendered in ChromeFrame to preserve the opener
616 // relationship with its parent even if the new page does not have the chrome
618 bool HandleAttachToExistingExternalTab(LPCWSTR url
,
619 IInternetProtocol
* protocol
,
620 IInternetProtocolSink
* prot_sink
,
621 IBindCtx
* bind_ctx
) {
622 ChromeFrameUrl cf_url
;
623 if (cf_url
.Parse(url
) && cf_url
.attach_to_external_tab()) {
624 scoped_refptr
<ProtData
> prot_data
= ProtData::DataFromProtocol(protocol
);
626 // Pass NULL as the read function which indicates that always return EOF
627 // without calling the underlying protocol.
628 prot_data
= new ProtData(protocol
, NULL
, url
);
629 PutProtData(bind_ctx
, prot_data
);
632 prot_sink
->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE
, kChromeMimeType
);
634 int data_flags
= BSCF_FIRSTDATANOTIFICATION
| BSCF_LASTDATANOTIFICATION
;
635 prot_sink
->ReportData(data_flags
, 0, 0);
637 prot_sink
->ReportResult(S_OK
, 0, NULL
);
643 HRESULT
ForwardHookStart(InternetProtocol_Start_Fn orig_start
,
644 IInternetProtocol
* protocol
, LPCWSTR url
, IInternetProtocolSink
* prot_sink
,
645 IInternetBindInfo
* bind_info
, DWORD flags
, HANDLE_PTR reserved
) {
646 ExceptionBarrierReportOnlyModule barrier
;
647 return orig_start(protocol
, url
, prot_sink
, bind_info
, flags
, reserved
);
650 HRESULT
ForwardWrappedHookStart(InternetProtocol_Start_Fn orig_start
,
651 IInternetProtocol
* protocol
, LPCWSTR url
, IInternetProtocolSink
* prot_sink
,
652 IInternetBindInfo
* bind_info
, DWORD flags
, HANDLE_PTR reserved
) {
653 ExceptionBarrier barrier
;
654 return orig_start(protocol
, url
, prot_sink
, bind_info
, flags
, reserved
);
657 // IInternetProtocol/Ex hooks.
658 STDMETHODIMP
Hook_Start(InternetProtocol_Start_Fn orig_start
,
659 IInternetProtocol
* protocol
, LPCWSTR url
, IInternetProtocolSink
* prot_sink
,
660 IInternetBindInfo
* bind_info
, DWORD flags
, HANDLE_PTR reserved
) {
662 if (!url
|| !prot_sink
|| !bind_info
)
664 DVLOG_IF(1, url
!= NULL
) << "OnStart: " << url
<< PiFlags2Str(flags
);
666 base::win::ScopedComPtr
<IBindCtx
> bind_ctx
= BindCtxFromIBindInfo(bind_info
);
668 // MSHTML sometimes takes a short path, skips the creation of
669 // moniker and binding, by directly grabbing protocol from InternetSession
670 DVLOG(1) << "DirectBind for " << url
;
671 return ForwardHookStart(orig_start
, protocol
, url
, prot_sink
, bind_info
,
675 scoped_refptr
<ProtData
> prot_data
= ProtData::DataFromProtocol(protocol
);
676 if (prot_data
&& !HasProtData(bind_ctx
)) {
677 prot_data
->Invalidate();
681 if (HandleAttachToExistingExternalTab(url
, protocol
, prot_sink
, bind_ctx
)) {
685 if (IsCFRequest(bind_ctx
)) {
686 base::win::ScopedComPtr
<BindContextInfo
> info
;
687 BindContextInfo::FromBindContext(bind_ctx
, info
.Receive());
690 info
->set_protocol(protocol
);
692 return ForwardHookStart(orig_start
, protocol
, url
, prot_sink
, bind_info
,
697 DVLOG(1) << "Found existing ProtData!";
698 prot_data
->UpdateUrl(url
);
699 base::win::ScopedComPtr
<IInternetProtocolSink
> new_sink
=
700 ProtocolSinkWrap::CreateNewSink(prot_sink
, prot_data
);
701 return ForwardWrappedHookStart(orig_start
, protocol
, url
, new_sink
,
702 bind_info
, flags
, reserved
);
705 if (!ShouldWrapSink(prot_sink
, url
)) {
706 return ForwardHookStart(orig_start
, protocol
, url
, prot_sink
, bind_info
,
711 InternetProtocol_Read_Fn read_fun
= reinterpret_cast<InternetProtocol_Read_Fn
>
712 (CTransaction_PatchInfo
[1].stub_
->argument());
713 prot_data
= new ProtData(protocol
, read_fun
, url
);
714 PutProtData(bind_ctx
, prot_data
);
716 base::win::ScopedComPtr
<IInternetProtocolSink
> new_sink
=
717 ProtocolSinkWrap::CreateNewSink(prot_sink
, prot_data
);
718 return ForwardWrappedHookStart(orig_start
, protocol
, url
, new_sink
, bind_info
,
722 HRESULT
ForwardHookStartEx(InternetProtocol_StartEx_Fn orig_start_ex
,
723 IInternetProtocolEx
* protocol
, IUri
* uri
, IInternetProtocolSink
* prot_sink
,
724 IInternetBindInfo
* bind_info
, DWORD flags
, HANDLE_PTR reserved
) {
725 ExceptionBarrierReportOnlyModule barrier
;
726 return orig_start_ex(protocol
, uri
, prot_sink
, bind_info
, flags
, reserved
);
729 HRESULT
ForwardWrappedHookStartEx(InternetProtocol_StartEx_Fn orig_start_ex
,
730 IInternetProtocolEx
* protocol
, IUri
* uri
, IInternetProtocolSink
* prot_sink
,
731 IInternetBindInfo
* bind_info
, DWORD flags
, HANDLE_PTR reserved
) {
732 ExceptionBarrier barrier
;
733 return orig_start_ex(protocol
, uri
, prot_sink
, bind_info
, flags
, reserved
);
736 STDMETHODIMP
Hook_StartEx(InternetProtocol_StartEx_Fn orig_start_ex
,
737 IInternetProtocolEx
* protocol
, IUri
* uri
, IInternetProtocolSink
* prot_sink
,
738 IInternetBindInfo
* bind_info
, DWORD flags
, HANDLE_PTR reserved
) {
739 DCHECK(orig_start_ex
);
740 if (!uri
|| !prot_sink
|| !bind_info
)
743 base::win::ScopedBstr url
;
744 uri
->GetPropertyBSTR(Uri_PROPERTY_ABSOLUTE_URI
, url
.Receive(), 0);
745 DVLOG_IF(1, url
!= NULL
) << "OnStartEx: " << url
<< PiFlags2Str(flags
);
747 base::win::ScopedComPtr
<IBindCtx
> bind_ctx
= BindCtxFromIBindInfo(bind_info
);
749 // MSHTML sometimes takes a short path, skips the creation of
750 // moniker and binding, by directly grabbing protocol from InternetSession.
751 DVLOG(1) << "DirectBind for " << url
;
752 return ForwardHookStartEx(orig_start_ex
, protocol
, uri
, prot_sink
,
753 bind_info
, flags
, reserved
);
756 scoped_refptr
<ProtData
> prot_data
= ProtData::DataFromProtocol(protocol
);
757 if (prot_data
&& !HasProtData(bind_ctx
)) {
758 prot_data
->Invalidate();
762 if (HandleAttachToExistingExternalTab(url
, protocol
, prot_sink
, bind_ctx
)) {
766 if (IsCFRequest(bind_ctx
)) {
767 base::win::ScopedComPtr
<BindContextInfo
> info
;
768 BindContextInfo::FromBindContext(bind_ctx
, info
.Receive());
771 info
->set_protocol(protocol
);
773 return ForwardHookStartEx(orig_start_ex
, protocol
, uri
, prot_sink
,
774 bind_info
, flags
, reserved
);
778 DVLOG(1) << "Found existing ProtData!";
779 prot_data
->UpdateUrl(url
);
780 base::win::ScopedComPtr
<IInternetProtocolSink
> new_sink
=
781 ProtocolSinkWrap::CreateNewSink(prot_sink
, prot_data
);
782 return ForwardWrappedHookStartEx(orig_start_ex
, protocol
, uri
, new_sink
,
783 bind_info
, flags
, reserved
);
786 if (!ShouldWrapSink(prot_sink
, url
)) {
787 return ForwardHookStartEx(orig_start_ex
, protocol
, uri
, prot_sink
,
788 bind_info
, flags
, reserved
);
792 InternetProtocol_Read_Fn read_fun
= reinterpret_cast<InternetProtocol_Read_Fn
>
793 (CTransaction_PatchInfo
[1].stub_
->argument());
794 prot_data
= new ProtData(protocol
, read_fun
, url
);
795 PutProtData(bind_ctx
, prot_data
);
797 base::win::ScopedComPtr
<IInternetProtocolSink
> new_sink
=
798 ProtocolSinkWrap::CreateNewSink(prot_sink
, prot_data
);
799 return ForwardWrappedHookStartEx(orig_start_ex
, protocol
, uri
, new_sink
,
800 bind_info
, flags
, reserved
);
803 STDMETHODIMP
Hook_Read(InternetProtocol_Read_Fn orig_read
,
804 IInternetProtocol
* protocol
, void* buffer
, ULONG size
, ULONG
* size_read
) {
808 scoped_refptr
<ProtData
> prot_data
= ProtData::DataFromProtocol(protocol
);
810 // We are not wrapping this request, avoid false positive crash reports.
811 ExceptionBarrierReportOnlyModule barrier
;
812 hr
= orig_read(protocol
, buffer
, size
, size_read
);
816 if (prot_data
->is_attach_external_tab_request()) {
817 // return EOF always.
823 hr
= prot_data
->Read(buffer
, size
, size_read
);
827 STDMETHODIMP
Hook_LockRequest(InternetProtocol_LockRequest_Fn orig_req
,
828 IInternetProtocol
* protocol
,
832 scoped_refptr
<ProtData
> prot_data
= ProtData::DataFromProtocol(protocol
);
833 if (prot_data
&& prot_data
->is_attach_external_tab_request()) {
838 // We are just pass through at this point, avoid false positive crash
840 ExceptionBarrierReportOnlyModule barrier
;
841 return orig_req(protocol
, options
);
844 STDMETHODIMP
Hook_UnlockRequest(InternetProtocol_UnlockRequest_Fn orig_req
,
845 IInternetProtocol
* protocol
) {
848 scoped_refptr
<ProtData
> prot_data
= ProtData::DataFromProtocol(protocol
);
849 if (prot_data
&& prot_data
->is_attach_external_tab_request()) {
850 prot_data
->Release();
854 // We are just pass through at this point, avoid false positive crash
856 ExceptionBarrierReportOnlyModule barrier
;
857 return orig_req(protocol
);
860 STDMETHODIMP
Hook_Abort(InternetProtocol_Abort_Fn orig_req
,
861 IInternetProtocol
* protocol
,
864 scoped_refptr
<ProtData
> prot_data
= ProtData::DataFromProtocol(protocol
);
866 prot_data
->Invalidate();
868 // We are just pass through at this point, avoid false positive crash
870 ExceptionBarrierReportOnlyModule barrier
;
871 return orig_req(protocol
, hr
, options
);
874 STDMETHODIMP
Hook_Terminate(InternetProtocol_Terminate_Fn orig_req
,
875 IInternetProtocol
* protocol
,
877 scoped_refptr
<ProtData
> prot_data
= ProtData::DataFromProtocol(protocol
);
878 // We should not be invalidating the cached protocol data in the following
880 // 1. Pages which are switching into ChromeFrame.
881 // When IE switches into ChromeFrame, we report the Chrome mime type as
882 // the handler for the page. This results in urlmon terminating the
883 // protocol. When Chrome attempts to read the data we need to report the
884 // cached data back to Chrome.
885 // 2. For the attach external tab requests which are temporary navigations
886 // to ensure that a top level URL is opened in IE from ChromeFrame.
887 // We rely on the mapping to identify these requests as attach tab
888 // requests. This mapping is referred to in the
889 // IInternetProtocol::LockRequest/IInternetProtocol::UnlockRequest
890 // intercepts. Invalidating the mapping after LockRequest is called and
891 // before UnlockRequest causes IE to crash.
892 if (prot_data
&& !IsChrome(prot_data
->renderer_type()) &&
893 !prot_data
->is_attach_external_tab_request())
894 prot_data
->Invalidate();
896 // We are just pass through at this point, avoid false positive crash
898 ExceptionBarrierReportOnlyModule barrier
;
899 return orig_req(protocol
, options
);
902 // Patching / Hooking code.
903 class FakeProtocol
: public CComObjectRootEx
<CComSingleThreadModel
>,
904 public IInternetProtocol
{
906 BEGIN_COM_MAP(FakeProtocol
)
907 COM_INTERFACE_ENTRY(IInternetProtocol
)
908 COM_INTERFACE_ENTRY(IInternetProtocolRoot
)
911 STDMETHOD(Start
)(LPCWSTR url
, IInternetProtocolSink
*protocol_sink
,
912 IInternetBindInfo
* bind_info
, DWORD flags
, HANDLE_PTR reserved
) {
913 transaction_
.QueryFrom(protocol_sink
);
914 // Return some unusual error code.
915 return INET_E_INVALID_CERTIFICATE
;
918 STDMETHOD(Continue
)(PROTOCOLDATA
* protocol_data
) { return S_OK
; }
919 STDMETHOD(Abort
)(HRESULT reason
, DWORD options
) { return S_OK
; }
920 STDMETHOD(Terminate
)(DWORD options
) { return S_OK
; }
921 STDMETHOD(Suspend
)() { return S_OK
; }
922 STDMETHOD(Resume
)() { return S_OK
; }
923 STDMETHOD(Read
)(void *buffer
, ULONG size
, ULONG
* size_read
) { return S_OK
; }
924 STDMETHOD(Seek
)(LARGE_INTEGER move
, DWORD origin
, ULARGE_INTEGER
* new_pos
)
926 STDMETHOD(LockRequest
)(DWORD options
) { return S_OK
; }
927 STDMETHOD(UnlockRequest
)() { return S_OK
; }
929 base::win::ScopedComPtr
<IInternetProtocol
> transaction_
;
932 struct FakeFactory
: public IClassFactory
,
933 public CComObjectRootEx
<CComSingleThreadModel
> {
934 BEGIN_COM_MAP(FakeFactory
)
935 COM_INTERFACE_ENTRY(IClassFactory
)
938 STDMETHOD(CreateInstance
)(IUnknown
*pUnkOuter
, REFIID riid
, void **ppvObj
) {
940 return CLASS_E_NOAGGREGATION
;
941 HRESULT hr
= obj_
->QueryInterface(riid
, ppvObj
);
945 STDMETHOD(LockServer
)(BOOL fLock
) {
952 static void HookTransactionVtable(IInternetProtocol
* p
) {
953 base::win::ScopedComPtr
<IInternetProtocolEx
> ex
;
956 HRESULT hr
= vtable_patch::PatchInterfaceMethods(p
, CTransaction_PatchInfo
);
957 if (hr
== S_OK
&& ex
) {
958 vtable_patch::PatchInterfaceMethods(ex
.get(), CTransaction2_PatchInfo
);
962 void TransactionHooks::InstallHooks() {
963 if (IS_PATCHED(CTransaction
)) {
964 DLOG(WARNING
) << __FUNCTION__
<< " called more than once.";
968 CComObjectStackEx
<FakeProtocol
> prot
;
969 CComObjectStackEx
<FakeFactory
> factory
;
970 factory
.obj_
= &prot
;
971 base::win::ScopedComPtr
<IInternetSession
> session
;
972 HRESULT hr
= ::CoInternetGetSession(0, session
.Receive(), 0);
973 hr
= session
->RegisterNameSpace(&factory
, CLSID_NULL
, L
"611", 0, 0, 0);
974 DLOG_IF(FATAL
, FAILED(hr
)) << "Failed to register namespace";
979 base::win::ScopedComPtr
<IMoniker
> mk
;
980 base::win::ScopedComPtr
<IBindCtx
> bc
;
981 base::win::ScopedComPtr
<IStream
> stream
;
982 hr
= ::CreateAsyncBindCtxEx(0, 0, 0, 0, bc
.Receive(), 0);
983 DLOG_IF(FATAL
, FAILED(hr
)) << "CreateAsyncBindCtxEx failed " << hr
;
987 hr
= ::CreateURLMoniker(NULL
, L
"611://512", mk
.Receive());
988 DLOG_IF(FATAL
, FAILED(hr
)) << "CreateURLMoniker failed " << hr
;
992 hr
= mk
->BindToStorage(bc
, NULL
, IID_IStream
,
993 reinterpret_cast<void**>(stream
.Receive()));
994 DLOG_IF(FATAL
, hr
!= INET_E_INVALID_CERTIFICATE
) <<
995 "BindToStorage failed " << hr
;
998 hr
= session
->UnregisterNameSpace(&factory
, L
"611");
999 if (prot
.transaction_
) {
1000 HookTransactionVtable(prot
.transaction_
);
1001 // Explicit release, otherwise ~CComObjectStackEx will complain about
1002 // outstanding reference to us, because it runs before ~FakeProtocol
1003 prot
.transaction_
.Release();
1007 void TransactionHooks::RevertHooks() {
1008 vtable_patch::UnpatchInterfaceMethods(CTransaction_PatchInfo
);
1009 vtable_patch::UnpatchInterfaceMethods(CTransaction2_PatchInfo
);