diff options
author | amit@chromium.org <amit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-09 00:51:10 +0000 |
---|---|---|
committer | amit@chromium.org <amit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-09 00:51:10 +0000 |
commit | 97965e190cb9f5009f6c97195bef9225c6034931 (patch) | |
tree | 96f2320c853a50d06664a1b2134a0dcb97c42bac | |
parent | 4a626f876a6dcd77e4d2cfc5d2496acdd44d7fe1 (diff) | |
download | chromium_src-97965e190cb9f5009f6c97195bef9225c6034931.zip chromium_src-97965e190cb9f5009f6c97195bef9225c6034931.tar.gz chromium_src-97965e190cb9f5009f6c97195bef9225c6034931.tar.bz2 |
Switch renderer in Moniker patch
Step one of the changes. Inspect data and cause a switch
in the moniker patch.
Review URL: http://codereview.chromium.org/1589013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44038 0039d316-1c4b-4281-b951-d872f2087c98
23 files changed, 1307 insertions, 1536 deletions
diff --git a/chrome_frame/bho.cc b/chrome_frame/bho.cc index 4432c9f..3a731bc 100644 --- a/chrome_frame/bho.cc +++ b/chrome_frame/bho.cc @@ -251,16 +251,6 @@ HRESULT Bho::OnHttpEquiv(IBrowserService_OnHttpEquiv_Fn original_httpequiv, } } } - } else if (done) { - if (!CheckForCFNavigation(browser, false)) { - DLOG(INFO) << "Releasing cached data."; - NavigationManager* mgr = NavigationManager::GetThreadInstance(); - if (mgr) - mgr->ReleaseRequestData(); - } else { - DLOG(INFO) << __FUNCTION__ - << " not freeing request data - browser tagged"; - } } return original_httpequiv(browser, shell_view, done, in_arg, out_arg); @@ -326,18 +316,17 @@ bool PatchHelper::InitializeAndPatchProtocolsIfNeeded() { ProtocolPatchMethod patch_method = static_cast<ProtocolPatchMethod>( GetConfigInt(PATCH_METHOD_IBROWSER, kPatchProtocols)); - if (patch_method == PATCH_METHOD_INET_PROTOCOL) { ProtocolSinkWrap::PatchProtocolHandlers(); state_ = PATCH_PROTOCOL; + } else if (patch_method == PATCH_METHOD_IBROWSER) { + state_ = PATCH_IBROWSER; } else { - DCHECK(patch_method == PATCH_METHOD_IBROWSER || - patch_method == PATCH_METHOD_IBROWSER_AND_MONIKER); - state_ = PATCH_IBROWSER; - if (patch_method == PATCH_METHOD_IBROWSER_AND_MONIKER) { - MonikerPatch::Initialize(); - } + DCHECK(patch_method == PATCH_METHOD_MONIKER); + state_ = PATCH_MONIKER; + MonikerPatch::Initialize(); } + ret = true; } diff --git a/chrome_frame/bho.h b/chrome_frame/bho.h index 430436b..466dcdb 100644 --- a/chrome_frame/bho.h +++ b/chrome_frame/bho.h @@ -27,7 +27,7 @@ class DeleteChromeHistory; class PatchHelper { public: - enum State { UNKNOWN, PATCH_IBROWSER, PATCH_PROTOCOL }; + enum State { UNKNOWN, PATCH_IBROWSER, PATCH_PROTOCOL, PATCH_MONIKER }; PatchHelper() : state_(UNKNOWN) { } diff --git a/chrome_frame/bind_status_callback_impl.cc b/chrome_frame/bind_status_callback_impl.cc new file mode 100644 index 0000000..90da0916 --- /dev/null +++ b/chrome_frame/bind_status_callback_impl.cc @@ -0,0 +1,228 @@ +// Copyright (c) 2010 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 "chrome_frame/bind_status_callback_impl.h" + +#include "base/logging.h" +#include "base/string_util.h" + +BSCBImpl::BSCBImpl() { + DLOG(INFO) << __FUNCTION__ << me(); +} + +BSCBImpl::~BSCBImpl() { + DLOG(INFO) << __FUNCTION__ << me(); +} + +std::string BSCBImpl::me() { + return StringPrintf(" obj=0x%08X", + static_cast<BSCBImpl*>(this)); +} + +HRESULT BSCBImpl::DelegateQI(void* obj, REFIID iid, void** ret, DWORD cookie) { + BSCBImpl* me = reinterpret_cast<BSCBImpl*>(obj); + HRESULT hr = E_NOINTERFACE; + if (me->delegate_) + hr = me->delegate_.QueryInterface(iid, ret); + return hr; +} + +void BSCBImpl::Initialize(IBindStatusCallback* original) { + DCHECK(!delegate_); + delegate_ = original; +} + +HRESULT BSCBImpl::AttachToBind(IBindCtx* bind_ctx) { + HRESULT hr = S_OK; + hr = ::RegisterBindStatusCallback(bind_ctx, this, delegate_.Receive(), 0); + if (SUCCEEDED(hr)) { + bind_ctx_ = bind_ctx; + } + + return hr; +} + +HRESULT BSCBImpl::ReleaseBind() { + HRESULT hr = S_OK; + if (delegate_ && bind_ctx_) { + ScopedComPtr<IBindStatusCallback> this_callback; + hr = ::RegisterBindStatusCallback(bind_ctx_, delegate_, + this_callback.Receive(), 0); + DCHECK(this_callback && + (this_callback == static_cast<IBindStatusCallback*>(this))); + } + + delegate_.Release(); + bind_ctx_.Release(); + return hr; +} + +// IServiceProvider +HRESULT BSCBImpl::QueryService(REFGUID service, REFIID iid, void** object) { + HRESULT hr = E_NOINTERFACE; + if (delegate_) { + ScopedComPtr<IServiceProvider> svc; + svc.QueryFrom(delegate_); + if (svc) { + hr = svc->QueryService(service, iid, object); + } + } + return hr; +} + +// IBindStatusCallback +HRESULT BSCBImpl::OnStartBinding(DWORD reserved, IBinding* binding) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (delegate_) + hr = delegate_->OnStartBinding(reserved, binding); + return hr; +} + +HRESULT BSCBImpl::GetPriority(LONG* priority) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (delegate_) + hr = delegate_->GetPriority(priority); + return hr; +} + +HRESULT BSCBImpl::OnLowResource(DWORD reserved) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (delegate_) + hr = delegate_->OnLowResource(reserved); + return hr; +} + +HRESULT BSCBImpl::OnProgress(ULONG progress, ULONG progress_max, + ULONG status_code, LPCWSTR status_text) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" status=%i tid=%i %ls", + status_code, PlatformThread::CurrentId(), status_text); + HRESULT hr = S_OK; + if (delegate_) + delegate_->OnProgress(progress, progress_max, status_code, status_text); + return hr; +} + +HRESULT BSCBImpl::OnStopBinding(HRESULT hresult, LPCWSTR error) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" hr=0x%08X '%ls' tid=%i", + hresult, error, PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (delegate_) + delegate_->OnStopBinding(hresult, error); + return hr; +} + +HRESULT BSCBImpl::GetBindInfo(DWORD* bindf, BINDINFO* bind_info) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (delegate_) + delegate_->GetBindInfo(bindf, bind_info); + return hr; +} + +HRESULT BSCBImpl::OnDataAvailable(DWORD bscf, DWORD size, + FORMATETC* format_etc, STGMEDIUM* stgmed) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (delegate_) + hr = delegate_->OnDataAvailable(bscf, size, format_etc, stgmed); + return hr; +} + +HRESULT BSCBImpl::OnObjectAvailable(REFIID iid, IUnknown* unk) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (delegate_) + delegate_->OnObjectAvailable(iid, unk); + return hr; +} + +// IBindStatusCallbackEx +HRESULT BSCBImpl::GetBindInfoEx(DWORD* bindf, BINDINFO* bind_info, + DWORD* bindf2, DWORD* reserved) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (delegate_) { + ScopedComPtr<IBindStatusCallbackEx> bscbex; + bscbex.QueryFrom(delegate_); + if (bscbex) + hr = bscbex->GetBindInfoEx(bindf, bind_info, bindf2, reserved); + } + return hr; +} + +HRESULT BSCBImpl::BeginningTransaction(LPCWSTR url, LPCWSTR headers, + DWORD reserved, + LPWSTR* additional_headers) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + + HRESULT hr = S_OK; + if (delegate_) { + ScopedComPtr<IHttpNegotiate> http_negotiate; + http_negotiate.QueryFrom(delegate_); + if (http_negotiate) { + hr = http_negotiate->BeginningTransaction(url, headers, reserved, + additional_headers); + } + } + + DLOG_IF(ERROR, FAILED(hr)) << __FUNCTION__; + return hr; +} + +HRESULT BSCBImpl::OnResponse(DWORD response_code, LPCWSTR response_headers, + LPCWSTR request_headers, + LPWSTR* additional_headers) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); + + HRESULT hr = S_OK; + if (delegate_) { + ScopedComPtr<IHttpNegotiate> http_negotiate; + http_negotiate.QueryFrom(delegate_); + if (http_negotiate) { + hr = http_negotiate->OnResponse(response_code, response_headers, + request_headers, additional_headers); + } + } + return hr; +} + +HRESULT BSCBImpl::GetRootSecurityId(BYTE* security_id, DWORD* security_id_size, + DWORD_PTR reserved) { + HRESULT hr = S_OK; + if (delegate_) { + ScopedComPtr<IHttpNegotiate2> http_negotiate; + http_negotiate.QueryFrom(delegate_); + if (http_negotiate) { + hr = http_negotiate->GetRootSecurityId(security_id, security_id_size, + reserved); + } + } + return hr; +} + +HRESULT BSCBImpl::GetSerializedClientCertContext(BYTE** cert, + DWORD* cert_size) { + HRESULT hr = S_OK; + if (delegate_) { + ScopedComPtr<IHttpNegotiate3> http_negotiate; + http_negotiate.QueryFrom(delegate_); + if (http_negotiate) { + return http_negotiate->GetSerializedClientCertContext(cert, cert_size); + } + } + return hr; +} + diff --git a/chrome_frame/bind_status_callback_impl.h b/chrome_frame/bind_status_callback_impl.h new file mode 100644 index 0000000..7066050 --- /dev/null +++ b/chrome_frame/bind_status_callback_impl.h @@ -0,0 +1,98 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_FRAME_BIND_STATUS_CALLBACK_IMPL_H_ +#define CHROME_FRAME_BIND_STATUS_CALLBACK_IMPL_H_ + +#include <atlbase.h> +#include <atlcom.h> +#include <string> +#include <urlmon.h> + +#include "base/scoped_comptr_win.h" +#include "chrome_frame/utils.h" + +// A generic base class for IBindStatus callback implementation. +// If initialized with delegate, it will hand over all the calls +// to the delegate. This can also be used as a base class to +// provide the base implementation by not providing any delegate. +class BSCBImpl + : public CComObjectRootEx<CComMultiThreadModel>, + public IBindStatusCallbackEx, + public IHttpNegotiate3, + public IServiceProvider { + public: + BSCBImpl(); + ~BSCBImpl(); + +BEGIN_COM_MAP(BSCBImpl) + COM_INTERFACE_ENTRY(IBindStatusCallback) + COM_INTERFACE_ENTRY(IHttpNegotiate) + COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(IBindStatusCallbackEx) + COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(IHttpNegotiate2) + COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(IHttpNegotiate3) + COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(IServiceProvider) + COM_INTERFACE_ENTRY_FUNC_BLIND(0, DelegateQI) +END_COM_MAP() + + static STDMETHODIMP DelegateQI(void* obj, REFIID iid, void** ret, + DWORD cookie); + + void Initialize(IBindStatusCallback* original); + HRESULT AttachToBind(IBindCtx* original); + HRESULT ReleaseBind(); + + // For the COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS macro. + IBindStatusCallback* delegate() const { + return delegate_; + } + + IBindCtx* bind_ctx() const { + return bind_ctx_; + } + + // IServiceProvider + STDMETHOD(QueryService)(REFGUID service, REFIID iid, void** object); + + // IBindStatusCallback + STDMETHOD(OnStartBinding)(DWORD reserved, IBinding* binding); + STDMETHOD(GetPriority)(LONG* priority); + STDMETHOD(OnLowResource)(DWORD reserved); + STDMETHOD(OnProgress)(ULONG progress, ULONG progress_max, ULONG status_code, + LPCWSTR status_text); + STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR error); + STDMETHOD(GetBindInfo)(DWORD* bindf, BINDINFO* bind_info); + STDMETHOD(OnDataAvailable)(DWORD bscf, DWORD size, FORMATETC* format_etc, + STGMEDIUM* stgmed); + STDMETHOD(OnObjectAvailable)(REFIID iid, IUnknown* unk); + + // IBindStatusCallbackEx + STDMETHOD(GetBindInfoEx)(DWORD* bindf, BINDINFO* bind_info, DWORD* bindf2, + DWORD* reserved); + + // IHttpNegotiate + STDMETHOD(BeginningTransaction)(LPCWSTR url, LPCWSTR headers, DWORD reserved, + LPWSTR* additional_headers); + STDMETHOD(OnResponse)(DWORD response_code, LPCWSTR response_headers, + LPCWSTR request_headers, LPWSTR* additional_headers); + + // IHttpNegotiate2 + STDMETHOD(GetRootSecurityId)(BYTE* security_id, DWORD* security_id_size, + DWORD_PTR reserved); + + // IHttpNegotiate3 + STDMETHOD(GetSerializedClientCertContext)(BYTE** cert, DWORD* cert_size); + + protected: + // used for logging. + std::string me(); + + ScopedComPtr<IBindStatusCallback> delegate_; + ScopedComPtr<IBindCtx> bind_ctx_; + + private: + DISALLOW_COPY_AND_ASSIGN(BSCBImpl); +}; + +#endif // CHROME_FRAME_BIND_STATUS_CALLBACK_IMPL_H_ diff --git a/chrome_frame/chrome_active_document.cc b/chrome_frame/chrome_active_document.cc index cec1b46..04d914e 100644 --- a/chrome_frame/chrome_active_document.cc +++ b/chrome_frame/chrome_active_document.cc @@ -85,7 +85,7 @@ HRESULT ChromeActiveDocument::FinalConstruct() { // and initializes it, which would spawn a new Chrome process, etc. // We don't want to be doing this if we have a cached document, whose // automation client instance can be reused. - HRESULT hr = Base::FinalConstruct(); + HRESULT hr = BaseActiveX::FinalConstruct(); if (FAILED(hr)) return hr; } @@ -119,7 +119,7 @@ ChromeActiveDocument::~ChromeActiveDocument() { find_dialog_.DestroyWindow(); } // ChromeFramePlugin - Base::Uninitialize(); + BaseActiveX::Uninitialize(); } // Override DoVerb @@ -193,8 +193,8 @@ STDMETHODIMP ChromeActiveDocument::IsDirty() { } void ChromeActiveDocument::OnAutomationServerReady() { - Base::OnAutomationServerReady(); - Base::GiveFocusToChrome(); + BaseActiveX::OnAutomationServerReady(); + BaseActiveX::GiveFocusToChrome(); } STDMETHODIMP ChromeActiveDocument::Load(BOOL fully_avalable, @@ -240,15 +240,11 @@ STDMETHODIMP ChromeActiveDocument::Load(BOOL fully_avalable, moniker_name, bind_context, mgr ? mgr->original_url_with_fragment() : std::wstring())); - scoped_refptr<RequestData> data; if (mgr) { + mgr->set_url(L""); mgr->set_original_url_with_fragment(L""); - data = mgr->GetActiveRequestData(url.c_str()); } - DLOG_IF(INFO, data) << "Got active request data"; - DLOG_IF(WARNING, data.get() == NULL) << "NO active request data"; - // The is_new_navigation variable indicates if this a navigation initiated // by typing in a URL for e.g. in the IE address bar, or from Chrome by // a window.open call from javascript, in which case the current IE tab @@ -467,7 +463,7 @@ HRESULT ChromeActiveDocument::IOleObject_SetClientSite( } if (client_site != m_spClientSite) { - return Base::IOleObject_SetClientSite(client_site); + return BaseActiveX::IOleObject_SetClientSite(client_site); } return S_OK; @@ -578,7 +574,7 @@ void ChromeActiveDocument::OnAcceleratorPressed(int tab_handle, // Handle the showing of the find dialog explicitly. OnFindInPage(); } else { - Base::OnAcceleratorPressed(tab_handle, accel_message); + BaseActiveX::OnAcceleratorPressed(tab_handle, accel_message); } } else { DLOG(INFO) << "IE handled accel key " << accel_message.wParam; @@ -665,7 +661,6 @@ void ChromeActiveDocument::UpdateNavigationState( DCHECK(mgr); if (mgr) { mgr->set_url(url_); - mgr->ReleaseRequestData(); } } @@ -789,7 +784,7 @@ void ChromeActiveDocument::OnOpenURL(int tab_handle, g_active_doc_cache.Set(this); } - Base::OnOpenURL(tab_handle, url_to_open, referrer, open_disposition); + BaseActiveX::OnOpenURL(tab_handle, url_to_open, referrer, open_disposition); } void ChromeActiveDocument::OnAttachExternalTab(int tab_handle, @@ -808,7 +803,7 @@ void ChromeActiveDocument::OnAttachExternalTab(int tab_handle, } // Allow popup if (hr == S_OK) { - Base::OnAttachExternalTab(tab_handle, params); + BaseActiveX::OnAttachExternalTab(tab_handle, params); return; } @@ -838,7 +833,7 @@ bool ChromeActiveDocument::PreProcessContextMenu(HMENU menu) { } // Call base class (adds 'About' item) - return Base::PreProcessContextMenu(menu); + return BaseActiveX::PreProcessContextMenu(menu); } bool ChromeActiveDocument::HandleContextMenuCommand(UINT cmd, @@ -860,7 +855,7 @@ bool ChromeActiveDocument::HandleContextMenuCommand(UINT cmd, break; default: - return Base::HandleContextMenuCommand(cmd, params); + return BaseActiveX::HandleContextMenuCommand(cmd, params); } return true; diff --git a/chrome_frame/chrome_active_document.h b/chrome_frame/chrome_active_document.h index 7a7d560..575c06a 100644 --- a/chrome_frame/chrome_active_document.h +++ b/chrome_frame/chrome_active_document.h @@ -208,7 +208,7 @@ class ATL_NO_VTABLE ChromeActiveDocument public HTMLPrivateWindowImpl<IHTMLPrivateWindow> { public: typedef ChromeFrameActivexBase<ChromeActiveDocument, - CLSID_ChromeActiveDocument> Base; + CLSID_ChromeActiveDocument> BaseActiveX; ChromeActiveDocument(); ~ChromeActiveDocument(); @@ -227,14 +227,14 @@ BEGIN_COM_MAP(ChromeActiveDocument) COM_INTERFACE_ENTRY(IHTMLWindow2) COM_INTERFACE_ENTRY(IHTMLPrivateWindow) COM_INTERFACE_ENTRY(IEnumPrivacyRecords) - COM_INTERFACE_ENTRY_CHAIN(Base) + COM_INTERFACE_ENTRY_CHAIN(BaseActiveX) END_COM_MAP() BEGIN_MSG_MAP(ChromeActiveDocument) MESSAGE_HANDLER(WM_FIRE_PRIVACY_CHANGE_NOTIFICATION, OnFirePrivacyChange) COMMAND_ID_HANDLER(IDC_FORWARD, OnForward) COMMAND_ID_HANDLER(IDC_BACK, OnBack) - CHAIN_MSG_MAP(Base) + CHAIN_MSG_MAP(BaseActiveX) END_MSG_MAP() HRESULT FinalConstruct(); diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index 0aacce7..7689d2c 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -154,9 +154,14 @@ ], 'conditions': [ ['OS=="win"', { + 'link_settings': { + 'libraries': [ + '-lshdocvw.lib', + ], + }, 'msvs_settings': { 'VCLinkerTool': { - 'DelayLoadDLLs': ['xpcom.dll', 'nspr4.dll'], + 'DelayLoadDLLs': ['xpcom.dll', 'nspr4.dll', 'shdocvw.dll'], }, }, 'dependencies': [ @@ -600,6 +605,8 @@ 'bho.cc', 'bho.h', 'bho.rgs', + 'bind_status_callback_impl.cc', + 'bind_status_callback_impl.h', 'chrome_active_document.cc', 'chrome_active_document.h', 'chrome_active_document.rgs', @@ -633,6 +640,8 @@ 'ole_document_impl.h', 'protocol_sink_wrap.cc', 'protocol_sink_wrap.h', + 'stream_impl.cc', + 'stream_impl.h', 'sync_msg_reply_dispatcher.cc', 'sync_msg_reply_dispatcher.h', 'extra_system_apis.h', diff --git a/chrome_frame/chrome_frame_activex_base.h b/chrome_frame/chrome_frame_activex_base.h index d43c4ea..c382f95 100644 --- a/chrome_frame/chrome_frame_activex_base.h +++ b/chrome_frame/chrome_frame_activex_base.h @@ -170,7 +170,7 @@ class ATL_NO_VTABLE ChromeFrameActivexBase : // NOLINT public ChromeFramePlugin<T> { protected: typedef std::set<ScopedComPtr<IDispatch> > EventHandlers; - typedef ChromeFrameActivexBase<T, class_id> Base; + typedef ChromeFrameActivexBase<T, class_id> BasePlugin; public: ChromeFrameActivexBase() @@ -450,7 +450,7 @@ END_MSG_MAP() virtual void OnHandleContextMenu(int tab_handle, HANDLE menu_handle, int align_flags, const IPC::ContextMenuParams& params) { - scoped_refptr<Base> ref(this); + scoped_refptr<BasePlugin> ref(this); ChromeFramePlugin<T>::OnHandleContextMenu(tab_handle, menu_handle, align_flags, params); } diff --git a/chrome_frame/stream_impl.cc b/chrome_frame/stream_impl.cc new file mode 100644 index 0000000..246e7ea --- /dev/null +++ b/chrome_frame/stream_impl.cc @@ -0,0 +1,95 @@ +// Copyright (c) 2010 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 "chrome_frame/stream_impl.h" + +#include "base/logging.h" + +void StreamImpl::Initialize(IStream* delegate) { + delegate_ = delegate; +} + +STDMETHODIMP StreamImpl::Write(const void * buffer, ULONG size, + ULONG* size_written) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->Write(buffer, size, size_written); + return hr; +} + +STDMETHODIMP StreamImpl::Read(void* pv, ULONG cb, ULONG* read) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->Read(pv, cb, read); + return hr; +} + +STDMETHODIMP StreamImpl::Seek(LARGE_INTEGER move, DWORD origin, + ULARGE_INTEGER* new_pos) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->Seek(move, origin, new_pos); + return hr; +} + +STDMETHODIMP StreamImpl::SetSize(ULARGE_INTEGER new_size) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->SetSize(new_size); + return hr; +} + +STDMETHODIMP StreamImpl::CopyTo(IStream* stream, ULARGE_INTEGER cb, + ULARGE_INTEGER* read, + ULARGE_INTEGER* written) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->CopyTo(stream, cb, read, written); + return hr; +} + +STDMETHODIMP StreamImpl::Commit(DWORD flags) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->Commit(flags); + return hr; +} + +STDMETHODIMP StreamImpl::Revert() { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->Revert(); + return hr; +} + +STDMETHODIMP StreamImpl::LockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER cb, + DWORD type) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->LockRegion(offset, cb, type); + return hr; +} + +STDMETHODIMP StreamImpl::UnlockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER cb, + DWORD type) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->UnlockRegion(offset, cb, type); + return hr; +} + +STDMETHODIMP StreamImpl::Stat(STATSTG* statstg, DWORD flag) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->Stat(statstg, flag); + return hr; +} + +STDMETHODIMP StreamImpl::Clone(IStream** stream) { + HRESULT hr = E_NOTIMPL; + if (delegate_) + hr = delegate_->Clone(stream); + return hr; +} + diff --git a/chrome_frame/stream_impl.h b/chrome_frame/stream_impl.h new file mode 100644 index 0000000..9cced3e --- /dev/null +++ b/chrome_frame/stream_impl.h @@ -0,0 +1,44 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_FRAME_STREAM_IMPL_H_ +#define CHROME_FRAME_STREAM_IMPL_H_ + +#include <atlbase.h> +#include <atlcom.h> + +#include "base/scoped_comptr_win.h" + +// A generic base class for IStream implementation. If provided +// with a delegate, it delegated calls to it otherwise can be +// used a as a base class for an IStream implementation. +class StreamImpl : public IStream { + public: + StreamImpl() {} + + void Initialize(IStream* delegate); + + STDMETHOD(Write)(const void * buffer, ULONG size, ULONG* size_written); + STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read); + STDMETHOD(Seek)(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER* new_pos); + STDMETHOD(SetSize)(ULARGE_INTEGER new_size); + STDMETHOD(CopyTo)(IStream* stream, ULARGE_INTEGER cb, ULARGE_INTEGER* read, + ULARGE_INTEGER* written); + STDMETHOD(Commit)(DWORD flags); + STDMETHOD(Revert)(); + STDMETHOD(LockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb, + DWORD type); + STDMETHOD(UnlockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb, + DWORD type); + STDMETHOD(Stat)(STATSTG* statstg, DWORD flag); + STDMETHOD(Clone)(IStream** stream); + + protected: + ScopedComPtr<IStream> delegate_; + + private: + DISALLOW_COPY_AND_ASSIGN(StreamImpl); +}; + +#endif // CHROME_FRAME_STREAM_IMPL_H_ diff --git a/chrome_frame/test/test_with_web_server.cc b/chrome_frame/test/test_with_web_server.cc index 6f4350d..637f37d 100644 --- a/chrome_frame/test/test_with_web_server.cc +++ b/chrome_frame/test/test_with_web_server.cc @@ -20,9 +20,9 @@ bool MonikerPatchEnabled() { ProtocolPatchMethod patch_method = static_cast<ProtocolPatchMethod>( GetConfigInt(PATCH_METHOD_IBROWSER, kPatchProtocols)); - LOG_IF(ERROR, patch_method != PATCH_METHOD_IBROWSER_AND_MONIKER) + LOG_IF(ERROR, patch_method != PATCH_METHOD_MONIKER) << "Not running test. Moniker patch not enabled."; - return patch_method == PATCH_METHOD_IBROWSER_AND_MONIKER; + return patch_method == PATCH_METHOD_MONIKER; } class ChromeFrameTestEnvironment: public testing::Environment { diff --git a/chrome_frame/test/urlmon_moniker_integration_test.cc b/chrome_frame/test/urlmon_moniker_integration_test.cc index 3103504..57cc03d 100644 --- a/chrome_frame/test/urlmon_moniker_integration_test.cc +++ b/chrome_frame/test/urlmon_moniker_integration_test.cc @@ -8,7 +8,7 @@ #include "base/scoped_comptr_win.h" #include "base/thread.h" #include "chrome_frame/bho.h" -#include "chrome_frame/urlmon_moniker.h" +//#include "chrome_frame/urlmon_moniker.h" #include "chrome_frame/test/test_server.h" #include "chrome_frame/test/chrome_frame_test_utils.h" #include "chrome_frame/test/urlmon_moniker_tests.h" @@ -25,9 +25,10 @@ using testing::Invoke; using testing::SetArgumentPointee; using testing::StrEq; using testing::Return; -using testing::WithArg; +using testing::DoAll; using testing::WithArgs; + static int kUrlmonMonikerTimeoutSec = 5; namespace { @@ -88,35 +89,45 @@ class RunTestServer : public base::Thread { class UrlmonMonikerTestManager { public: explicit UrlmonMonikerTestManager(const wchar_t* test_url) { - mock_mgr_.RegisterThreadInstance(); - mock_mgr_.set_url(test_url); EXPECT_EQ(true, MonikerPatch::Initialize()); } ~UrlmonMonikerTestManager() { MonikerPatch::Uninitialize(); - mock_mgr_.UnregisterThreadInstance(); } chrome_frame_test::TimedMsgLoop& loop() { return loop_; } - TestNavigationManager& nav_manager() { - return mock_mgr_; - } - protected: - TestNavigationManager mock_mgr_; chrome_frame_test::TimedMsgLoop loop_; }; +ACTION_P(SetBindInfo, is_async) { + DWORD* flags = arg0; + BINDINFO* bind_info = arg1; + + DCHECK(flags); + DCHECK(bind_info); + DCHECK(bind_info->cbSize >= sizeof(BINDINFO)); + + *flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA; + if (is_async) + *flags |= BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE; + + bind_info->dwBindVerb = BINDVERB_GET; + memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM)); + bind_info->grfBindInfoF = 0; + bind_info->szCustomVerb = NULL; +} + // Wraps the MockBindStatusCallbackImpl mock object and allows the user // to specify expectations on the callback object. class UrlmonMonikerTestCallback { public: explicit UrlmonMonikerTestCallback(UrlmonMonikerTestManager* mgr) - : mgr_(mgr) { + : mgr_(mgr), clip_format_(0) { } ~UrlmonMonikerTestCallback() { @@ -137,16 +148,10 @@ class UrlmonMonikerTestCallback { if (bind_info_handling == REQUEST_ASYNCHRONOUS) { EXPECT_CALL(callback_, GetBindInfo(_, _)) - .WillOnce(DoAll( - WithArgs<0, 1>( - Invoke(&MockBindStatusCallbackImpl::SetAsyncBindInfo)), - Return(S_OK))); + .WillOnce(DoAll(SetBindInfo(true), Return(S_OK))); } else if (bind_info_handling == REQUEST_SYNCHRONOUS) { EXPECT_CALL(callback_, GetBindInfo(_, _)) - .WillOnce(DoAll( - WithArgs<0, 1>( - Invoke(&MockBindStatusCallbackImpl::SetSyncBindInfo)), - Return(S_OK))); + .WillOnce(DoAll(SetBindInfo(false), Return(S_OK))); } else { DCHECK(bind_info_handling == EXPECT_NO_CALL); } @@ -192,6 +197,7 @@ class UrlmonMonikerTestCallback { protected: CComObjectStackEx<MockBindStatusCallbackImpl> callback_; UrlmonMonikerTestManager* mgr_; + CLIPFORMAT clip_format_; }; // Tests synchronously binding to a moniker and downloading the target. @@ -217,8 +223,6 @@ TEST_F(UrlmonMonikerTest, BindToStorageSynchronous) { EXPECT_EQ(0, release->Release()); server_thread.Stop(); - - EXPECT_FALSE(test.nav_manager().HasRequestData()); } // Tests asynchronously binding to a moniker and downloading the target. @@ -242,8 +246,6 @@ TEST_F(UrlmonMonikerTest, BindToStorageAsynchronous) { IBindCtx* release = bind_ctx.Detach(); EXPECT_EQ(0, release->Release()); - - EXPECT_FALSE(test.nav_manager().HasRequestData()); } // Responds with the Chrome mime type. @@ -259,6 +261,7 @@ class ResponseWithContentType : public test_server::SimpleResponse { } }; +/* // Downloads a document asynchronously and then verifies that the downloaded // contents were cached and the cache contents are correct. // TODO(tommi): Fix and re-enable. @@ -340,3 +343,4 @@ TEST_F(UrlmonMonikerTest, BindToStorageCachedContent) { } } +*/ diff --git a/chrome_frame/test/urlmon_moniker_tests.h b/chrome_frame/test/urlmon_moniker_tests.h index 4c5b832..ce58b03 100644 --- a/chrome_frame/test/urlmon_moniker_tests.h +++ b/chrome_frame/test/urlmon_moniker_tests.h @@ -8,51 +8,9 @@ #include <atlbase.h> #include <atlcom.h> -#include "chrome_frame/bho.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -class MockBindingImpl - : public SimpleBindingImpl, - public IServiceProvider { - public: -BEGIN_COM_MAP(MockBindingImpl) - COM_INTERFACE_ENTRY(IBinding) - COM_INTERFACE_ENTRY(IServiceProvider) -END_COM_MAP() - - MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Abort, - HRESULT ()); // NOLINT - MOCK_METHOD3_WITH_CALLTYPE(__stdcall, QueryService, - HRESULT (REFGUID svc, // NOLINT - REFIID riid, - void** obj)); - MOCK_METHOD4_WITH_CALLTYPE(__stdcall, GetBindResult, - HRESULT (CLSID* protocol, // NOLINT - DWORD* result_code, - LPOLESTR* result, - DWORD* reserved)); -}; - -class MockHttpNegotiateImpl - : public CComObjectRootEx<CComSingleThreadModel>, - public IHttpNegotiate { - public: -BEGIN_COM_MAP(MockHttpNegotiateImpl) - COM_INTERFACE_ENTRY(IHttpNegotiate) -END_COM_MAP() - MOCK_METHOD4_WITH_CALLTYPE(__stdcall, BeginningTransaction, - HRESULT (LPCWSTR url, // NOLINT - LPCWSTR headers, - DWORD reserved, - LPWSTR* additional)); - MOCK_METHOD4_WITH_CALLTYPE(__stdcall, OnResponse, - HRESULT (DWORD code, // NOLINT - LPCWSTR response_headers, - LPCWSTR request_headers, - LPWSTR* additional)); -}; - class MockBindStatusCallbackImpl : public CComObjectRootEx<CComSingleThreadModel>, public IBindStatusCallback { @@ -60,6 +18,7 @@ class MockBindStatusCallbackImpl BEGIN_COM_MAP(MockBindStatusCallbackImpl) COM_INTERFACE_ENTRY(IBindStatusCallback) END_COM_MAP() + MOCK_METHOD2_WITH_CALLTYPE(__stdcall, OnStartBinding, HRESULT (DWORD reserved, IBinding* binding)); // NOLINT @@ -92,51 +51,122 @@ END_COM_MAP() MOCK_METHOD2_WITH_CALLTYPE(__stdcall, OnObjectAvailable, HRESULT (REFIID riid, // NOLINT IUnknown* unk)); +}; + +class MockBindCtxImpl + : public CComObjectRootEx<CComSingleThreadModel>, + public IBindCtx { + public: +BEGIN_COM_MAP(MockBindCtxImpl) + COM_INTERFACE_ENTRY(IBindCtx) +END_COM_MAP() + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, RegisterObjectBound, + HRESULT (IUnknown* object)); // NOLINT + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, RevokeObjectBound, + HRESULT (IUnknown* object)); // NOLINT + + MOCK_METHOD0_WITH_CALLTYPE(__stdcall, ReleaseBoundObjects, + HRESULT ()); // NOLINT + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, SetBindOptions, + HRESULT (BIND_OPTS* options)); // NOLINT + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetBindOptions, + HRESULT (BIND_OPTS* options)); // NOLINT - static void ReadAllData(STGMEDIUM* storage) { - DCHECK(storage); - DCHECK(storage->tymed == TYMED_ISTREAM); - char buffer[0xff]; - HRESULT hr; - DWORD read = 0; - do { - hr = storage->pstm->Read(buffer, sizeof(buffer), &read); - } while (hr == S_OK && read); - } - - static void SetSyncBindInfo(DWORD* flags, BINDINFO* bind_info) { - DCHECK(flags); - DCHECK(bind_info); - *flags = BINDF_PULLDATA | BINDF_NOWRITECACHE; - DCHECK(bind_info->cbSize >= sizeof(BINDINFO)); - bind_info->dwBindVerb = BINDVERB_GET; - memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM)); - bind_info->grfBindInfoF = 0; - bind_info->szCustomVerb = NULL; - } - - static void SetAsyncBindInfo(DWORD* flags, BINDINFO* bind_info) { - DCHECK(flags); - DCHECK(bind_info); - *flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | - BINDF_NOWRITECACHE; - DCHECK(bind_info->cbSize >= sizeof(BINDINFO)); - bind_info->dwBindVerb = BINDVERB_GET; - memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM)); - bind_info->grfBindInfoF = 0; - bind_info->szCustomVerb = NULL; - } + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, GetRunningObjectTable, + HRESULT (IRunningObjectTable** rot)); // NOLINT + + MOCK_METHOD2_WITH_CALLTYPE(__stdcall, RegisterObjectParam, + HRESULT (LPOLESTR key, // NOLINT + IUnknown* param)); + + MOCK_METHOD2_WITH_CALLTYPE(__stdcall, GetObjectParam, + HRESULT (LPOLESTR key, // NOLINT + IUnknown** param)); + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, EnumObjectParam, + HRESULT (IEnumString** enum_params)); // NOLINT + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, RevokeObjectParam, + HRESULT (LPOLESTR key)); // NOLINT }; -class TestNavigationManager : public NavigationManager { +class MockMonikerImpl + : public CComObjectRootEx<CComSingleThreadModel>, + public IMoniker { public: - TestNavigationManager() { - } +BEGIN_COM_MAP(MockMonikerImpl) + COM_INTERFACE_ENTRY(IMoniker) +END_COM_MAP() - bool HasRequestData() const { - return request_data_.get() != NULL; - } + MOCK_METHOD4_WITH_CALLTYPE(__stdcall, BindToObject, + HRESULT (IBindCtx* bind_context, // NOLINT + IMoniker* left, + REFIID result_iid, + void** object)); + + MOCK_METHOD4_WITH_CALLTYPE(__stdcall, BindToStorage, + HRESULT (IBindCtx* bind_context, // NOLINT + IMoniker* left, + REFIID result_iid, + void** storage)); + + MOCK_METHOD4_WITH_CALLTYPE(__stdcall, Reduce, + HRESULT (IBindCtx* bind_context, // NOLINT + DWORD reduce_depth, + IMoniker* left, + IMoniker** reduced)); + + MOCK_METHOD3_WITH_CALLTYPE(__stdcall, ComposeWith, + HRESULT (IBindCtx* right, // NOLINT + BOOL is_not_generic, + IMoniker** composite)); + + MOCK_METHOD2_WITH_CALLTYPE(__stdcall, Enum, + HRESULT (BOOL is_forward, // NOLINT + IEnumMoniker** moniker_enum)); + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, IsEqual, + HRESULT (IMoniker* other)); // NOLINT + + MOCK_METHOD3_WITH_CALLTYPE(__stdcall, IsRunning, + HRESULT (IBindCtx* bind_context, // NOLINT + IMoniker* left, + IMoniker** newly_running)); + + MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetTimeOfLastChange, + HRESULT (IBindCtx* bind_context, // NOLINT + IMoniker* left, + FILETIME *pFileTime)); + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, Inverse, + HRESULT (IMoniker** inversed)); // NOLINT + + MOCK_METHOD2_WITH_CALLTYPE(__stdcall, CommonPrefixWith, + HRESULT (IMoniker* other, // NOLINT + IMoniker** prefix)); + + MOCK_METHOD2_WITH_CALLTYPE(__stdcall, RelativePathTo, + HRESULT (IMoniker* other, // NOLINT + IMoniker** relative)); + + MOCK_METHOD3_WITH_CALLTYPE(__stdcall, GetDisplayName, + HRESULT (IBindCtx* bind_context, // NOLINT + IMoniker* left, + LPOLESTR* display_name)); + + MOCK_METHOD5_WITH_CALLTYPE(__stdcall, ParseDisplayName, + HRESULT (IBindCtx* bind_context, // NOLINT + IMoniker* left, + LPOLESTR display_name, + ULONG *pchEaten, + IMoniker** ret)); + + MOCK_METHOD1_WITH_CALLTYPE(__stdcall, IsSystemMoniker, + HRESULT (DWORD* is_system)); // NOLINT }; #endif // CHROME_FRAME_TEST_URLMON_MONIKER_TESTS_H_ - diff --git a/chrome_frame/test/urlmon_moniker_unittest.cc b/chrome_frame/test/urlmon_moniker_unittest.cc index 08795fc..f50bd86 100644 --- a/chrome_frame/test/urlmon_moniker_unittest.cc +++ b/chrome_frame/test/urlmon_moniker_unittest.cc @@ -5,222 +5,245 @@ #include <atlbase.h> #include <atlcom.h> +#include "base/file_util.h" +#include "base/path_service.h" #include "base/scoped_comptr_win.h" -#include "chrome_frame/bho.h" -#include "chrome_frame/urlmon_moniker.h" +#include "chrome_frame/urlmon_bind_status_callback.h" #include "chrome_frame/test/urlmon_moniker_tests.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -using testing::_; using testing::Return; -using testing::WithArg; -using testing::WithArgs; -using testing::SetArgumentPointee; -using testing::StrEq; using testing::Eq; -class UrlmonMonikerTest : public testing::Test { +class MonikerPatchTest : public testing::Test { protected: - UrlmonMonikerTest() { + MonikerPatchTest() { } + + virtual void SetUp() { + PathService::Get(base::DIR_SOURCE_ROOT, &test_file_path_); + test_file_path_ = test_file_path_.Append(FILE_PATH_LITERAL("chrome_frame")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")); + } + + bool ReadFileAsString(const wchar_t* file_name, std::string* file_contents) { + EXPECT_TRUE(file_name); + FilePath file_path = test_file_path_.Append(file_name); + return file_util::ReadFileToString(file_path, file_contents); + } + + static bool StringToStream(const std::string& data, IStream** ret) { + EXPECT_TRUE(!data.empty()); + + ScopedComPtr<IStream> stream; + HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream.Receive()); + EXPECT_HRESULT_SUCCEEDED(hr); + if (FAILED(hr)) { + return false; + } + + DWORD written = 0; + hr = stream->Write(data.c_str(), data.size(), &written); + EXPECT_HRESULT_SUCCEEDED(hr); + EXPECT_EQ(data.size(), written); + + bool result = false; + if (SUCCEEDED(hr)) { + RewindStream(stream); + *ret = stream.Detach(); + result = true; + } + + return result; + } + + FilePath test_file_path_; }; -// Tests the ReadStreamCache class by writing content into a stream object -// and verify that reading that stream through the ReadStreamCache class -// reads the correct content and also verifies that ReadStreamCache caches -// all the reads. -TEST_F(UrlmonMonikerTest, ReadStreamCache) { - CComObjectStackEx<ReadStreamCache> read_stream; - EXPECT_EQ(NULL, read_stream.cache()); - - ScopedComPtr<IStream> test_stream; - ::CreateStreamOnHGlobal(NULL, TRUE, test_stream.Receive()); - EXPECT_TRUE(NULL != test_stream); - const char test_string[] = "ReadStreamCacheTest"; - DWORD written; - EXPECT_HRESULT_SUCCEEDED(test_stream->Write(test_string, sizeof(test_string), - &written)); - EXPECT_HRESULT_SUCCEEDED(RewindStream(test_stream)); - - read_stream.SetDelegate(test_stream); - - char buffer[0xff]; +// Tests the CacheStream class by writing content into a stream object +// and verify that reading that stream back +TEST_F(MonikerPatchTest, CacheStream) { + const char data[] = "ReadStreamCacheTest"; + char ret[2 * sizeof(data)] = {0}; DWORD read = 0; - EXPECT_HRESULT_SUCCEEDED(read_stream.Read(buffer, sizeof(buffer), &read)); - EXPECT_EQ(read, sizeof(test_string)); - EXPECT_EQ(read_stream.GetCacheSize(), sizeof(test_string)); - read_stream.RewindCache(); - IStream* cache = read_stream.cache(); - EXPECT_TRUE(NULL != cache); - if (cache) { - read = 0; - EXPECT_HRESULT_SUCCEEDED(cache->Read(buffer, sizeof(buffer), &read)); - EXPECT_EQ(read, sizeof(test_string)); - EXPECT_EQ(0, memcmp(test_string, buffer, sizeof(test_string))); - } + + // Test 1: empty stream reads nothing + CComObjectStackEx<CacheStream> cache_stream1; + EXPECT_EQ(E_PENDING, cache_stream1.Read(ret, sizeof(ret), &read)); + EXPECT_EQ(0, read); + + // Test 2: Read from initialized cache + CComObjectStackEx<CacheStream> cache_stream2; + cache_stream2.Initialize(data, sizeof(data)); + EXPECT_HRESULT_SUCCEEDED(cache_stream2.Read(ret, sizeof(ret), &read)); + EXPECT_EQ(sizeof(data), read); + EXPECT_EQ(std::string(data), std::string(ret)); + + read = 0; + EXPECT_EQ(E_PENDING, cache_stream2.Read(ret, sizeof(ret), &read)); + EXPECT_EQ(0, read); } -// Verifies that we can override bind results by using the SimpleBindingImpl -// class. -TEST_F(UrlmonMonikerTest, SimpleBindingImpl1) { - CComObjectStackEx<SimpleBindingImpl> test; - ScopedComPtr<IBinding> binding; - binding.QueryFrom(&test); - EXPECT_TRUE(binding != NULL); - test.OverrideBindResults(E_INVALIDARG); - DWORD hr = E_UNEXPECTED; - EXPECT_HRESULT_SUCCEEDED(binding->GetBindResult(NULL, &hr, NULL, NULL)); - EXPECT_EQ(E_INVALIDARG, hr); - test.OverrideBindResults(E_ACCESSDENIED); - // {1AF15145-104B-4bd8-AA4F-97CEFD40D370} - just something non-null. - GUID guid = { 0x1af15145, 0x104b, 0x4bd8, - { 0xaa, 0x4f, 0x97, 0xce, 0xfd, 0x40, 0xd3, 0x70 } }; - EXPECT_HRESULT_SUCCEEDED(binding->GetBindResult(&guid, &hr, NULL, NULL)); - EXPECT_EQ(E_ACCESSDENIED, hr); - EXPECT_TRUE(guid == GUID_NULL); +ACTION_P3(ReadStream, buffer, size, ret) { + EXPECT_EQ(TYMED_ISTREAM, arg3->tymed); + *ret = arg3->pstm->Read(buffer, *size, size); } -// Tests the SimpleBindingImpl class with a delegate. Verifies that the -// delegate gets called and also that we can override the bind results. -TEST_F(UrlmonMonikerTest, SimpleBindingImpl2) { - CComObjectStackEx<MockBindingImpl> mock; - CComObjectStackEx<SimpleBindingImpl> test; - - EXPECT_CALL(mock, QueryService(_, _, _)) - .WillOnce(Return(E_ACCESSDENIED)); - EXPECT_CALL(mock, GetBindResult(_, _, _, _)) - .WillRepeatedly(DoAll(SetArgumentPointee<1>(E_ACCESSDENIED), - Return(S_OK))); - EXPECT_CALL(mock, Abort()) - .WillOnce(Return(E_ACCESSDENIED)); - - ScopedComPtr<IServiceProvider> svc; - svc.QueryFrom(test.GetUnknown()); - EXPECT_TRUE(svc == NULL); - - test.SetDelegate(&mock); - - // Now we should have access to IServiceProvider - svc.QueryFrom(test.GetUnknown()); - EXPECT_TRUE(svc != NULL); - - ScopedComPtr<IUnknown> unk; - EXPECT_EQ(E_ACCESSDENIED, svc->QueryService(GUID_NULL, IID_NULL, - reinterpret_cast<void**>(unk.Receive()))); - - // Call through to the mock's GetBindResult implementation. - DWORD result; - test.GetBindResult(NULL, &result, NULL, NULL); - EXPECT_TRUE(result == E_ACCESSDENIED); - // Let the binding override the result code. - test.OverrideBindResults(INET_E_TERMINATED_BIND); - test.GetBindResult(NULL, &result, NULL, NULL); - EXPECT_TRUE(result == INET_E_TERMINATED_BIND); - - EXPECT_EQ(E_ACCESSDENIED, test.Abort()); +// Tests the implementation of BSCBFeedData to feed data to the +// specified IBindStatusCallback +TEST_F(MonikerPatchTest, BSCBFeedData) { + CComObjectStackEx<MockBindStatusCallbackImpl> mock; + const char data[] = "ReadStreamCacheTest"; + const DWORD size = sizeof(data); + const CLIPFORMAT cf = 0xd0d0; + const DWORD flags = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION; + const DWORD kArbitraryreadSize = 0xdeadbabe; + + char read_buffer1[size] = {0}, read_buffer2[size] = {0}; + DWORD read_size1 = size, read_size2 = kArbitraryreadSize; + HRESULT ret1 = E_FAIL, ret2 = E_FAIL; + + EXPECT_CALL(mock, OnDataAvailable(flags, size, + testing::Field(&FORMATETC::cfFormat, cf), + testing::Field(&STGMEDIUM::tymed, TYMED_ISTREAM))) + .WillOnce(testing::DoAll( + ReadStream(read_buffer1, &read_size1, &ret1), + ReadStream(read_buffer2, &read_size2, &ret2), + Return(S_OK))); + + EXPECT_HRESULT_SUCCEEDED(CacheStream::BSCBFeedData(&mock, data, size, cf, + flags)); + + EXPECT_HRESULT_SUCCEEDED(ret1); + EXPECT_STREQ(data, read_buffer1); + EXPECT_EQ(size, read_size1); + + EXPECT_EQ(E_PENDING, ret2); + EXPECT_STREQ("", read_buffer2); + EXPECT_EQ(kArbitraryreadSize, read_size2); } -// Tests the RequestData class. Content is fed to the object via OnX methods -// and then we verify that the cached content is correct by calling -// FireHttpNegotiateEvents and see if the notifications contain the correct -// content. -TEST_F(UrlmonMonikerTest, RequestHeaders) { - scoped_refptr<RequestData> test(new RequestData()); - test->Initialize(NULL); - - const wchar_t url[] = L"http://www.chromium.org"; - const wchar_t begin_headers[] = L"Cookie: endilega\r\n"; - const wchar_t request_headers[] = L"GET / HTTP/1.0\r\nHost: cough\r\n\rn"; - const wchar_t response_headers[] = L"HTTP 200 OK\r\nHave-a: good-day\r\n\r\n"; - const wchar_t additional_headers[] = L"Referer: http://foo.com\r\n"; - - // Null pointers should be ignored. - RequestHeaders* headers = test->headers(); - EXPECT_TRUE(NULL != headers); - headers->OnBeginningTransaction(NULL, NULL, NULL); - headers->OnBeginningTransaction(url, begin_headers, additional_headers); - headers->OnResponse(500, NULL, NULL); - headers->OnResponse(200, response_headers, request_headers); - - CComObjectStackEx<MockHttpNegotiateImpl> mock; - EXPECT_CALL(mock, BeginningTransaction(StrEq(url), StrEq(begin_headers), - _, _)) - .WillOnce(Return(S_OK)); - EXPECT_CALL(mock, OnResponse(Eq(200), StrEq(response_headers), - StrEq(request_headers), _)) - .WillOnce(Return(S_OK)); - - EXPECT_EQ(0, headers->request_url().compare(url)); - - headers->FireHttpNegotiateEvents(&mock); +const wchar_t kSmallHtmlMetaTag[] = L"sub_frame1.html"; +const wchar_t kSmallHtmlNoMetaTag[] = L"host_browser.html"; + +// Test various aspects of the SniffData class +TEST_F(MonikerPatchTest, SniffDataMetaTag) { + std::string small_html_meta_tag, small_html_no_meta_tag; + ASSERT_TRUE(ReadFileAsString(kSmallHtmlMetaTag, &small_html_meta_tag)); + ASSERT_TRUE(ReadFileAsString(kSmallHtmlNoMetaTag, &small_html_no_meta_tag)); + + ScopedComPtr<IStream> stream_with_meta, stream_no_meta; + ASSERT_TRUE(StringToStream(small_html_meta_tag, stream_with_meta.Receive())); + ASSERT_TRUE(StringToStream(small_html_no_meta_tag, + stream_no_meta.Receive())); + + // Initialize 2 sniffers 1 with meta tag and 1 without. + SniffData sniffer1, sniffer2; + EXPECT_TRUE(sniffer1.InitializeCache(std::wstring())); + EXPECT_TRUE(sniffer2.InitializeCache(std::wstring())); + EXPECT_HRESULT_SUCCEEDED(sniffer1.ReadIntoCache(stream_with_meta, true)); + EXPECT_HRESULT_SUCCEEDED(sniffer2.ReadIntoCache(stream_no_meta, true)); + + // Verify renderer type and size read. + EXPECT_TRUE(sniffer1.is_chrome()); + EXPECT_EQ(SniffData::OTHER, sniffer2.renderer_type()); + EXPECT_EQ(small_html_meta_tag.size(), sniffer1.size()); + EXPECT_EQ(small_html_no_meta_tag.size(), sniffer2.size()); } -// Tests the HTML content portion of the RequestData class. -// Here we provide content in the form of a stream object and call -// OnDataAvailable to make the object cache the contents. -// Then we fetch the cached content stream by calling -// GetResetCachedContentStream and verify that it's contents are correct. -// In order to also test when data is cached outside of OnDataAvailable -// calls, we also call DelegateDataRead. In this test we simply use the -// original content again which will make the end results that the cached -// content should be double the test content. -TEST_F(UrlmonMonikerTest, RequestDataContent) { - scoped_refptr<RequestData> test(new RequestData()); - test->Initialize(NULL); +// Now test how the data is fed back the the bind status callback. +// case 1: callback reads data in 1 read +TEST_F(MonikerPatchTest, SniffDataPlayback1) { + std::string small_html_meta_tag; + ScopedComPtr<IStream> stream_with_meta; + SniffData sniffer; - CComObjectStackEx<MockBindStatusCallbackImpl> mock; + EXPECT_TRUE(sniffer.InitializeCache(std::wstring())); + ASSERT_TRUE(ReadFileAsString(kSmallHtmlMetaTag, &small_html_meta_tag)); + ASSERT_TRUE(StringToStream(small_html_meta_tag, stream_with_meta.Receive())); + EXPECT_HRESULT_SUCCEEDED(sniffer.ReadIntoCache(stream_with_meta, true)); - ScopedComPtr<IStream> data; - ::CreateStreamOnHGlobal(NULL, TRUE, data.Receive()); - const char content[] = "<html><head>" - "<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />" - "</head><body>Test HTML content</body></html>"; - data->Write(content, sizeof(content) - 1, NULL); - STATSTG stat = {0}; - data->Stat(&stat, STATFLAG_NONAME); - DCHECK(stat.cbSize.LowPart == (sizeof(content) - 1)); - RewindStream(data); - - FORMATETC format = {0}; - format.cfFormat = ::RegisterClipboardFormat(L"application/chromepage"); - format.tymed = TYMED_ISTREAM; - STGMEDIUM storage = {0}; - storage.tymed = TYMED_ISTREAM; - storage.pstm = data; - - DWORD flags = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | - BSCF_DATAFULLYAVAILABLE; - - EXPECT_CALL(mock, OnDataAvailable(Eq(flags), Eq(stat.cbSize.LowPart), _, _)) - .WillOnce(DoAll( - WithArgs<3>(testing::Invoke( - &MockBindStatusCallbackImpl::ReadAllData)), + CComObjectStackEx<MockBindStatusCallbackImpl> mock; + const CLIPFORMAT cf = 0xd0d0; + const DWORD flags = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION; + const DWORD data_size = small_html_meta_tag.size(); + + DWORD read_size1 = data_size * 2; + scoped_ptr<char> read_buffer1(new char[read_size1]); + ZeroMemory(read_buffer1.get(), read_size1); + + char read_buffer2[10] = {0}; + DWORD read_size2 = sizeof(read_buffer2); + HRESULT ret1 = E_FAIL, ret2 = E_FAIL; + + EXPECT_CALL(mock, OnDataAvailable(flags, data_size, + testing::Field(&FORMATETC::cfFormat, cf), + testing::Field(&STGMEDIUM::tymed, TYMED_ISTREAM))) + .WillOnce(testing::DoAll( + ReadStream(read_buffer1.get(), &read_size1, &ret1), + ReadStream(read_buffer2, &read_size2, &ret2), Return(S_OK))); - size_t bytes_read = 0; - test->DelegateDataRead(&mock, flags, stat.cbSize.LowPart, &format, &storage, - &bytes_read); - - DCHECK(bytes_read == stat.cbSize.LowPart); - DCHECK(test->GetCachedContentSize() == bytes_read); - - // Also test that the CacheAll method appends the stream. - RewindStream(data); - test->CacheAll(data); - DCHECK(test->GetCachedContentSize() == (bytes_read * 2)); - - ScopedComPtr<IStream> cache; - EXPECT_HRESULT_SUCCEEDED(test->GetResetCachedContentStream(cache.Receive())); - if (cache) { - char buffer[0xffff]; - DCHECK((bytes_read * 2) <= sizeof(buffer)); - DWORD read = 0; - cache->Read(buffer, sizeof(buffer), &read); - EXPECT_EQ(read, bytes_read * 2); - EXPECT_EQ(0, memcmp(content, buffer, sizeof(content) - 1)); - EXPECT_EQ(0, memcmp(content, buffer + sizeof(content) - 1, - sizeof(content) - 1)); - } + EXPECT_HRESULT_SUCCEEDED(sniffer.DrainCache(&mock, flags, cf)); + + EXPECT_HRESULT_SUCCEEDED(ret1); + EXPECT_EQ(small_html_meta_tag, read_buffer1.get()); + EXPECT_EQ(data_size, read_size1); + + EXPECT_EQ(E_PENDING, ret2); + EXPECT_STREQ("", read_buffer2); + EXPECT_EQ(sizeof(read_buffer2), read_size2); } +// case 2: callback reads data in 2 reads. +TEST_F(MonikerPatchTest, SniffDataPlayback2) { + std::string small_html_meta_tag; + ScopedComPtr<IStream> stream_with_meta; + SniffData sniffer; + + EXPECT_TRUE(sniffer.InitializeCache(std::wstring())); + ASSERT_TRUE(ReadFileAsString(kSmallHtmlMetaTag, &small_html_meta_tag)); + ASSERT_TRUE(StringToStream(small_html_meta_tag, stream_with_meta.Receive())); + EXPECT_HRESULT_SUCCEEDED(sniffer.ReadIntoCache(stream_with_meta, true)); + + CComObjectStackEx<MockBindStatusCallbackImpl> mock; + const CLIPFORMAT cf = 0xd0d0; + const DWORD flags = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION; + const DWORD data_size = small_html_meta_tag.size(); + + DWORD read_size1 = data_size / 2; // First read is half the data. + DWORD read_size2 = data_size; // Second read, try to read past data. + scoped_ptr<char> read_buffer1(new char[read_size1]); + scoped_ptr<char> read_buffer2(new char[read_size2]); + ZeroMemory(read_buffer1.get(), read_size1); + ZeroMemory(read_buffer2.get(), read_size2); + + char read_buffer3[10] = {0}; + DWORD read_size3 = sizeof(read_buffer3); + HRESULT ret1 = E_FAIL, ret2 = E_FAIL, ret3 = E_FAIL; + + EXPECT_CALL(mock, OnDataAvailable(flags, data_size, + testing::Field(&FORMATETC::cfFormat, cf), + testing::Field(&STGMEDIUM::tymed, TYMED_ISTREAM))) + .WillOnce(testing::DoAll( + ReadStream(read_buffer1.get(), &read_size1, &ret1), + ReadStream(read_buffer2.get(), &read_size2, &ret2), + ReadStream(read_buffer3, &read_size3, &ret3), + Return(S_OK))); + + EXPECT_HRESULT_SUCCEEDED(sniffer.DrainCache(&mock, flags, cf)); + + EXPECT_HRESULT_SUCCEEDED(ret1); + EXPECT_HRESULT_SUCCEEDED(ret2); + EXPECT_EQ(data_size/2, read_size1); + EXPECT_EQ(data_size - read_size1, read_size2); + + std::string data_read; + data_read.append(read_buffer1.get(), read_size1); + data_read.append(read_buffer2.get(), read_size2); + EXPECT_EQ(small_html_meta_tag, data_read); + + EXPECT_EQ(E_PENDING, ret3); + EXPECT_STREQ("", read_buffer3); + EXPECT_EQ(sizeof(read_buffer3), read_size3); +} diff --git a/chrome_frame/urlmon_bind_status_callback.cc b/chrome_frame/urlmon_bind_status_callback.cc index ec51c57..cb90bf2 100644 --- a/chrome_frame/urlmon_bind_status_callback.cc +++ b/chrome_frame/urlmon_bind_status_callback.cc @@ -4,307 +4,312 @@ #include "chrome_frame/urlmon_bind_status_callback.h" +#include <mshtml.h> #include <shlguid.h> #include "base/logging.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" -CFUrlmonBindStatusCallback::CFUrlmonBindStatusCallback() - : only_buffer_(false), data_(new RequestData()) { - DLOG(INFO) << __FUNCTION__ << me(); -} - -CFUrlmonBindStatusCallback::~CFUrlmonBindStatusCallback() { - DLOG(INFO) << __FUNCTION__ << me(); -} +#include "chrome_frame/urlmon_moniker.h" +#include "chrome_tab.h" // NOLINT -std::string CFUrlmonBindStatusCallback::me() { - return StringPrintf(" obj=0x%08X", - static_cast<CFUrlmonBindStatusCallback*>(this)); -} -HRESULT CFUrlmonBindStatusCallback::DelegateQI(void* obj, REFIID iid, - void** ret, DWORD cookie) { - CFUrlmonBindStatusCallback* me = - reinterpret_cast<CFUrlmonBindStatusCallback*>(obj); - HRESULT hr = me->delegate_.QueryInterface(iid, ret); - return hr; -} +// A helper to given feed data to the specified |bscb| using +// CacheStream instance. +HRESULT CacheStream::BSCBFeedData(IBindStatusCallback* bscb, const char* data, + size_t size, CLIPFORMAT clip_format, + size_t flags) { + if (!bscb) { + NOTREACHED() << "invalid IBindStatusCallback"; + return E_INVALIDARG; + } -HRESULT CFUrlmonBindStatusCallback::Initialize(IBindCtx* bind_ctx, - RequestHeaders* headers) { - DLOG(INFO) << __FUNCTION__ << me(); - DCHECK(bind_ctx); - DCHECK(!binding_delegate_.get()); - // headers may be NULL. - - data_->Initialize(headers); - - bind_ctx_ = bind_ctx; - - // Replace the bind context callback with ours. - HRESULT hr = ::RegisterBindStatusCallback(bind_ctx, this, - delegate_.Receive(), 0); - if (!delegate_) { - NOTREACHED() << "Failed to find registered bind status callback"; - ::RevokeBindStatusCallback(bind_ctx_, this); - bind_ctx_.Release(); - hr = E_UNEXPECTED; + // We can't use a CComObjectStackEx here since mshtml will hold + // onto the stream pointer. + CComObject<CacheStream>* cache_stream = NULL; + HRESULT hr = CComObject<CacheStream>::CreateInstance(&cache_stream); + if (FAILED(hr)) { + NOTREACHED(); + return hr; } + cache_stream->AddRef(); + cache_stream->Initialize(data, size); + + FORMATETC format_etc = { clip_format, NULL, DVASPECT_CONTENT, -1, + TYMED_ISTREAM }; + STGMEDIUM medium = {0}; + medium.tymed = TYMED_ISTREAM; + medium.pstm = cache_stream; + + hr = bscb->OnDataAvailable(flags, size, &format_etc, &medium); + + cache_stream->Release(); return hr; } -HRESULT CFUrlmonBindStatusCallback::QueryService(REFGUID service, REFIID iid, - void** object) { - HRESULT hr = E_NOINTERFACE; - if (delegate_) { - ScopedComPtr<IServiceProvider> svc; - svc.QueryFrom(delegate_); - if (svc) { - hr = svc->QueryService(service, iid, object); - } +void CacheStream::Initialize(const char* cache, size_t size) { + cache_ = cache; + size_ = size; + position_ = 0; +} + +// Read is the only call that we expect. Return E_PENDING if there +// is no more data to serve. Otherwise this will result in a +// read with 0 bytes indicating that no more data is available. +STDMETHODIMP CacheStream::Read(void* pv, ULONG cb, ULONG* read) { + if (!pv || !read) + return E_INVALIDARG; + + // Default to E_PENDING to signal that this is a partial data. + HRESULT hr = E_PENDING; + if (position_ < size_) { + *read = std::min(size_ - position_, size_t(cb)); + memcpy(pv, cache_ + position_, *read); + position_ += *read; + hr = S_OK; } + return hr; } -// IBindStatusCallback -HRESULT CFUrlmonBindStatusCallback::OnStartBinding(DWORD reserved, - IBinding* binding) { - DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", - PlatformThread::CurrentId()); - DCHECK(!binding_delegate_.get()); - CComObject<SimpleBindingImpl>* binding_delegate; - HRESULT hr = CComObject<SimpleBindingImpl>::CreateInstance(&binding_delegate); +///////////////////////////////////////////////////////////////////// + +bool SniffData::InitializeCache(const std::wstring& url) { + url_ = url; + renderer_type_ = UNDETERMINED; + + const int kAllocationSize = 32 * 1024; + HGLOBAL memory = GlobalAlloc(0, kAllocationSize); + HRESULT hr = CreateStreamOnHGlobal(memory, TRUE, cache_.Receive()); if (FAILED(hr)) { + GlobalFree(memory); NOTREACHED(); - return hr; + return false; } - binding_delegate_ = binding_delegate; - DCHECK_EQ(binding_delegate->m_dwRef, 1); - binding_delegate_->SetDelegate(binding); - - return delegate_->OnStartBinding(reserved, binding_delegate_); + return true; } -HRESULT CFUrlmonBindStatusCallback::GetPriority(LONG* priority) { - DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", - PlatformThread::CurrentId()); - return delegate_->GetPriority(priority); -} - -HRESULT CFUrlmonBindStatusCallback::OnLowResource(DWORD reserved) { - DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", - PlatformThread::CurrentId()); - return delegate_->OnLowResource(reserved); -} - -HRESULT CFUrlmonBindStatusCallback::OnProgress(ULONG progress, - ULONG progress_max, - ULONG status_code, - LPCWSTR status_text) { - DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" status=%i tid=%i %ls", - status_code, PlatformThread::CurrentId(), status_text); - if (status_code == BINDSTATUS_REDIRECTING && status_text) { - redirected_url_ = status_text; +HRESULT SniffData::ReadIntoCache(IStream* stream, bool force_determination) { + if (!stream) { + NOTREACHED(); + return E_INVALIDARG; } - return delegate_->OnProgress(progress, progress_max, status_code, - status_text); -} -HRESULT CFUrlmonBindStatusCallback::OnStopBinding(HRESULT hresult, - LPCWSTR error) { - DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" hr=0x%08X '%ls' tid=%i", - hresult, error, PlatformThread::CurrentId()); - if (SUCCEEDED(hresult)) { - // Notify the BHO that this is the one and only RequestData object. - NavigationManager* mgr = NavigationManager::GetThreadInstance(); - DCHECK(mgr); - if (mgr && data_->GetCachedContentSize()) { - mgr->SetActiveRequestData(data_); - if (!redirected_url_.empty()) { - mgr->set_url(redirected_url_.c_str()); - } + HRESULT hr = S_OK; + while (SUCCEEDED(hr)) { + const size_t kChunkSize = 4 * 1024; + char buffer[kChunkSize]; + DWORD read = 0; + hr = stream->Read(buffer, sizeof(buffer), &read); + if (read) { + DWORD written = 0; + cache_->Write(buffer, read, &written); + size_ += written; } - if (only_buffer_) { - hresult = INET_E_TERMINATED_BIND; - DLOG(INFO) << " - changed to INET_E_TERMINATED_BIND"; - } + if ((S_FALSE == hr) || !read) + break; } - // Hold a reference to ourselves while we release the bind context - // and disconnect the callback. - AddRef(); + if (force_determination || (size() >= kMaxSniffSize)) { + DetermineRendererType(); + } - HRESULT hr = delegate_->OnStopBinding(hresult, error); + return hr; +} - if (bind_ctx_) { - ::RevokeBindStatusCallback(bind_ctx_, this); - bind_ctx_.Release(); +HRESULT SniffData::DrainCache(IBindStatusCallback* bscb, DWORD bscf, + CLIPFORMAT clip_format) { + if (!is_cache_valid()) { + return S_OK; } - binding_delegate_.Release(); - - // After this call, this object might be gone. - Release(); + // Ideally we could just use the cache_ IStream implementation but + // can't use it here since we have to return E_PENDING for the + // last call + HGLOBAL memory = NULL; + HRESULT hr = GetHGlobalFromStream(cache_, &memory); + if (SUCCEEDED(hr) && memory) { + char* buffer = reinterpret_cast<char*>(GlobalLock(memory)); + hr = CacheStream::BSCBFeedData(bscb, buffer, size_, clip_format, bscf); + GlobalUnlock(memory); + } + size_ = 0; + cache_.Release(); return hr; } -HRESULT CFUrlmonBindStatusCallback::GetBindInfo(DWORD* bindf, - BINDINFO* bind_info) { - DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", - PlatformThread::CurrentId()); - return delegate_->GetBindInfo(bindf, bind_info); -} - -HRESULT CFUrlmonBindStatusCallback::OnDataAvailable(DWORD bscf, DWORD size, - FORMATETC* format_etc, - STGMEDIUM* stgmed) { - DCHECK(format_etc); -#ifndef NDEBUG - wchar_t clip_fmt_name[MAX_PATH] = {0}; - if (format_etc) { - ::GetClipboardFormatNameW(format_etc->cfFormat, clip_fmt_name, - arraysize(clip_fmt_name)); +// Scan the buffer or OptIn URL list and decide if the renderer is +// to be switched +void SniffData::DetermineRendererType() { + if (is_undetermined()) { + if (IsOptInUrl(url_.c_str())) { + renderer_type_ = CHROME; + } else { + renderer_type_ = OTHER; + if (is_cache_valid() && cache_) { + HGLOBAL memory = NULL; + GetHGlobalFromStream(cache_, &memory); + char* buffer = reinterpret_cast<char*>(GlobalLock(memory)); + + std::wstring html_contents; + // TODO(joshia): detect and handle different content encodings + if (buffer && size_) { + UTF8ToWide(buffer, size_, &html_contents); + GlobalUnlock(memory); + } + + // 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; + UtilGetXUACompatContentValue(html_contents, &xua_compat_content); + if (StrStrI(xua_compat_content.c_str(), kChromeContentPrefix)) { + renderer_type_ = CHROME; + } + } + } } - DLOG(INFO) << __FUNCTION__ << me() - << StringPrintf(" tid=%i original fmt=%ls", - PlatformThread::CurrentId(), clip_fmt_name); +} - if (!stgmed) { - NOTREACHED() << "Invalid STGMEDIUM received"; - return delegate_->OnDataAvailable(bscf, size, format_etc, stgmed); - } +///////////////////////////////////////////////////////////////////// - if (stgmed->tymed != TYMED_ISTREAM) { - DLOG(INFO) << "Not handling medium:" << stgmed->tymed; - return delegate_->OnDataAvailable(bscf, size, format_etc, stgmed); - } +HRESULT BSCBStorageBind::Initialize(IMoniker* moniker, IBindCtx* bind_ctx) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", + PlatformThread::CurrentId()); - if (bscf & BSCF_FIRSTDATANOTIFICATION) { - DLOG(INFO) << "first data notification"; + HRESULT hr = AttachToBind(bind_ctx); + if (FAILED(hr)) { + NOTREACHED() << __FUNCTION__ << me() << "AttachToBind error: " << hr; + return hr; } -#endif - HRESULT hr = S_OK; - size_t bytes_read = 0; - if (!only_buffer_) { - hr = data_->DelegateDataRead(delegate_, bscf, size, format_etc, stgmed, - &bytes_read); + if (!delegate()) { + NOTREACHED() << __FUNCTION__ << me() << "No existing callback: " << hr; + return E_FAIL; } - DLOG(INFO) << __FUNCTION__ << StringPrintf(" - 0x%08x", hr); - if (hr == INET_E_TERMINATED_BIND) { - // Check if the content type is CF's mime type. - UINT cf_format = ::RegisterClipboardFormatW(kChromeMimeType); - bool override_bind_results = (format_etc->cfFormat == cf_format); - if (!override_bind_results) { - ScopedComPtr<IBrowserService> browser_service; - DoQueryService(SID_SShellBrowser, delegate_, browser_service.Receive()); - override_bind_results = (browser_service != NULL) && - CheckForCFNavigation(browser_service, false); - } + std::wstring url = GetActualUrlFromMoniker(moniker, bind_ctx, + std::wstring()); + data_sniffer_.InitializeCache(url); + return hr; +} - if (override_bind_results) { - // We want to complete fetching the entire document even though the - // delegate isn't interested in continuing. - // This happens when we switch from mshtml to CF. - // We take over and buffer the document and once we're done, we report - // INET_E_TERMINATED to mshtml so that it will continue as usual. - hr = S_OK; - only_buffer_ = true; - binding_delegate_->OverrideBindResults(INET_E_TERMINATED_BIND); - } - } +STDMETHODIMP BSCBStorageBind::OnProgress(ULONG progress, ULONG progress_max, + ULONG status_code, LPCWSTR status_text) { + DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" status=%i tid=%i %ls", + status_code, PlatformThread::CurrentId(), status_text); - if (only_buffer_) { - data_->CacheAll(stgmed->pstm); - DCHECK(hr == S_OK); + HRESULT hr = S_OK; + if (data_sniffer_.is_undetermined()) { + Progress new_progress = { progress, progress_max, status_code, + status_text ? status_text : std::wstring() }; + saved_progress_.push_back(new_progress); + } else { + hr = CallbackImpl::OnProgress(progress, progress_max, status_code, + status_text); } return hr; } -HRESULT CFUrlmonBindStatusCallback::OnObjectAvailable(REFIID iid, - IUnknown* unk) { +// Refer to urlmon_moniker.h for explanation of how things work. +STDMETHODIMP BSCBStorageBind::OnDataAvailable(DWORD flags, DWORD size, + FORMATETC* format_etc, + STGMEDIUM* stgmed) { DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", PlatformThread::CurrentId()); - return delegate_->OnObjectAvailable(iid, unk); -} + if (!stgmed || !format_etc) { + DLOG(INFO) << __FUNCTION__ << me() << "Invalid stgmed or format_etc"; + return CallbackImpl::OnDataAvailable(flags, size, format_etc, stgmed); + } -// IBindStatusCallbackEx -HRESULT CFUrlmonBindStatusCallback::GetBindInfoEx(DWORD* bindf, - BINDINFO* bind_info, - DWORD* bindf2, - DWORD* reserved) { - DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", - PlatformThread::CurrentId()); - ScopedComPtr<IBindStatusCallbackEx> bscbex; - bscbex.QueryFrom(delegate_); - return bscbex->GetBindInfoEx(bindf, bind_info, bindf2, reserved); -} + if ((stgmed->tymed != TYMED_ISTREAM) || !stgmed->pstm) { + DLOG(INFO) << __FUNCTION__ << me() << "stgmedium is not a valid stream"; + return CallbackImpl::OnDataAvailable(flags, size, format_etc, stgmed); + } -HRESULT CFUrlmonBindStatusCallback::BeginningTransaction(LPCWSTR url, - LPCWSTR headers, - DWORD reserved, - LPWSTR* additional_headers) { - DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", - PlatformThread::CurrentId()); + HRESULT hr = S_OK; + if (!clip_format_) + clip_format_ = format_etc->cfFormat; + + if (data_sniffer_.is_undetermined()) { + bool force_determination = !!(flags & + (BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE)); + hr = data_sniffer_.ReadIntoCache(stgmed->pstm, force_determination); + // If we don't have sufficient data to determine renderer type + // wait for the next data notification. + if (data_sniffer_.is_undetermined()) + return S_OK; + } + + DCHECK(!data_sniffer_.is_undetermined()); - ScopedComPtr<IHttpNegotiate> http_negotiate; - HRESULT hr = http_negotiate.QueryFrom(delegate_); - if (SUCCEEDED(hr)) { - hr = http_negotiate->BeginningTransaction(url, headers, reserved, - additional_headers); + if (data_sniffer_.is_cache_valid()) { + hr = MayPlayBack(flags); + DCHECK(!data_sniffer_.is_cache_valid()); } else { - hr = S_OK; + hr = CallbackImpl::OnDataAvailable(flags, size, format_etc, stgmed); } - data_->headers()->OnBeginningTransaction(url, headers, - additional_headers && *additional_headers ? *additional_headers : NULL); - - DLOG_IF(ERROR, FAILED(hr)) << __FUNCTION__; return hr; } -HRESULT CFUrlmonBindStatusCallback::OnResponse(DWORD response_code, - LPCWSTR response_headers, - LPCWSTR request_headers, - LPWSTR* additional_headers) { - DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", +STDMETHODIMP BSCBStorageBind::OnStopBinding(HRESULT hresult, LPCWSTR error) { + DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", PlatformThread::CurrentId()); + HRESULT hr = MayPlayBack(BSCF_LASTDATANOTIFICATION); + return CallbackImpl::OnStopBinding(hresult, error); +} - data_->headers()->OnResponse(response_code, response_headers, - request_headers); +// Play back the cached data to the delegate. Normally this would happen +// when we have read enough data to determine the renderer. In this case +// we first play back the data from the cache and then go into a 'pass +// through' mode. In some cases we may end up getting OnStopBinding +// before we get a chance to determine. Also it's possible that the +// BindToStorage call will return before OnStopBinding is sent. Hence +// This is called from 3 places and it's important to maintain the +// exact sequence of calls. +// Once the data is played back, calling this again is a no op. +HRESULT BSCBStorageBind::MayPlayBack(DWORD flags) { + // Force renderer type determination if not already done since + // we want to play back data now. + data_sniffer_.DetermineRendererType(); + DCHECK(!data_sniffer_.is_undetermined()); - ScopedComPtr<IHttpNegotiate> http_negotiate; - HRESULT hr = http_negotiate.QueryFrom(delegate_); - if (SUCCEEDED(hr)) { - hr = http_negotiate->OnResponse(response_code, response_headers, - request_headers, additional_headers); + HRESULT hr = S_OK; + if (data_sniffer_.is_chrome()) { + // Remember clip format. If we are switching to chrome, then in order + // to make mshtml return INET_E_TERMINATED_BIND and reissue navigation + // with the same bind context, we have to return a mime type that is + // special cased by mshtml. + static const CLIPFORMAT kMagicClipFormat = + RegisterClipboardFormat(CFSTR_MIME_MPEG); + clip_format_ = kMagicClipFormat; } else { - hr = S_OK; + if (!saved_progress_.empty()) { + for (std::vector<Progress>::iterator i = saved_progress_.begin(); + i != saved_progress_.end(); i++) { + const wchar_t* status_text = i->status_text_.empty() ? + NULL : i->status_text_.c_str(); + CallbackImpl::OnProgress(i->progress_, i->progress_max_, + i->status_code_, status_text); + } + saved_progress_.clear(); + } } - return hr; -} -HRESULT CFUrlmonBindStatusCallback::GetRootSecurityId(BYTE* security_id, - DWORD* security_id_size, - DWORD_PTR reserved) { - ScopedComPtr<IHttpNegotiate2> http_negotiate; - http_negotiate.QueryFrom(delegate_); - return http_negotiate->GetRootSecurityId(security_id, security_id_size, - reserved); -} + if (data_sniffer_.is_cache_valid()) { + hr = data_sniffer_.DrainCache(delegate(), + flags | BSCF_FIRSTDATANOTIFICATION, clip_format_); + if (data_sniffer_.is_chrome()) + NavigationManager::AttachCFObject(bind_ctx_); + } -HRESULT CFUrlmonBindStatusCallback::GetSerializedClientCertContext( - BYTE** cert, - DWORD* cert_size) { - ScopedComPtr<IHttpNegotiate3> http_negotiate; - http_negotiate.QueryFrom(delegate_); - return http_negotiate->GetSerializedClientCertContext(cert, cert_size); + return hr; } diff --git a/chrome_frame/urlmon_bind_status_callback.h b/chrome_frame/urlmon_bind_status_callback.h index d8ede6d..f1b1473 100644 --- a/chrome_frame/urlmon_bind_status_callback.h +++ b/chrome_frame/urlmon_bind_status_callback.h @@ -8,91 +8,122 @@ #include <atlbase.h> #include <atlcom.h> -#include "chrome_frame/urlmon_moniker.h" - -// Our implementation of IBindStatusCallback that allows us to sit on the -// sidelines and cache all the data that passes by (using the RequestData -// class). That cache can then be used by other monikers for the same URL -// that immediately follow. -class CFUrlmonBindStatusCallback - : public CComObjectRootEx<CComMultiThreadModel>, - public IBindStatusCallbackEx, - public IHttpNegotiate3, - public IServiceProvider { +#include "chrome_frame/bind_status_callback_impl.h" +#include "chrome_frame/stream_impl.h" + + +// A fake stream class to serve cached data to arbitrary +// IBindStatusCallback +class CacheStream : public CComObjectRoot, public StreamImpl { public: - CFUrlmonBindStatusCallback(); - ~CFUrlmonBindStatusCallback(); + BEGIN_COM_MAP(CacheStream) + COM_INTERFACE_ENTRY(IStream) + COM_INTERFACE_ENTRY(ISequentialStream) + END_COM_MAP() - // used for logging. - std::string me(); + CacheStream() : cache_(NULL), size_(0), position_(0) { + } + void Initialize(const char* cache, size_t size); + static HRESULT BSCBFeedData(IBindStatusCallback* bscb, const char* data, + size_t size, CLIPFORMAT clip_format, + size_t flags); -BEGIN_COM_MAP(CFUrlmonBindStatusCallback) - COM_INTERFACE_ENTRY(IBindStatusCallback) - COM_INTERFACE_ENTRY(IHttpNegotiate) - COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(IBindStatusCallbackEx) - COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(IHttpNegotiate2) - COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(IHttpNegotiate3) - COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS(IServiceProvider) - COM_INTERFACE_ENTRY_FUNC_BLIND(0, DelegateQI) -END_COM_MAP() + // IStream overrides + STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read); + + protected: + const char* cache_; + size_t size_; + size_t position_; + + private: + DISALLOW_COPY_AND_ASSIGN(CacheStream); +}; - static STDMETHODIMP DelegateQI(void* obj, REFIID iid, void** ret, - DWORD cookie); +// Utility class for data sniffing +class SniffData { + public: + SniffData() : renderer_type_(OTHER), size_(0) {} + + enum RendererType { + UNDETERMINED, + CHROME, + OTHER + }; + + bool InitializeCache(const std::wstring& url); + HRESULT ReadIntoCache(IStream* stream, bool force_determination); + HRESULT DrainCache(IBindStatusCallback* bscb, DWORD bscf, + CLIPFORMAT clip_format); + void DetermineRendererType(); + + bool is_undetermined() const { + return (UNDETERMINED == renderer_type_); + } + bool is_chrome() const { + return (CHROME == renderer_type_); + } - HRESULT Initialize(IBindCtx* bind_ctx, RequestHeaders* headers); + RendererType renderer_type() const { + return renderer_type_; + } - // For the COM_INTERFACE_ENTRY_IF_DELEGATE_SUPPORTS macro. - IBindStatusCallback* delegate() const { - return delegate_; + size_t size() const { + return size_; } - RequestData* request_data() { - return data_; + bool is_cache_valid() { + return (size_ != 0); } - // IServiceProvider - STDMETHOD(QueryService)(REFGUID service, REFIID iid, void** object); + ScopedComPtr<IStream> cache_; + std::wstring url_; + RendererType renderer_type_; + size_t size_; + + static const size_t kMaxSniffSize = 2 * 1024; + + private: + DISALLOW_COPY_AND_ASSIGN(SniffData); +}; + +// A wrapper for bind status callback in IMoniker::BindToStorage +class BSCBStorageBind : public BSCBImpl { + public: + typedef BSCBImpl CallbackImpl; + BSCBStorageBind() : clip_format_(CF_NULL) {} + +BEGIN_COM_MAP(BSCBStorageBind) + COM_INTERFACE_ENTRY(IBindStatusCallback) + COM_INTERFACE_ENTRY_CHAIN(CallbackImpl) +END_COM_MAP() + + HRESULT Initialize(IMoniker* moniker, IBindCtx* bind_ctx); + HRESULT MayPlayBack(DWORD flags); // IBindStatusCallback - STDMETHOD(OnStartBinding)(DWORD reserved, IBinding* binding); - STDMETHOD(GetPriority)(LONG* priority); - STDMETHOD(OnLowResource)(DWORD reserved); STDMETHOD(OnProgress)(ULONG progress, ULONG progress_max, ULONG status_code, LPCWSTR status_text); - STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR error); - STDMETHOD(GetBindInfo)(DWORD* bindf, BINDINFO* bind_info); - STDMETHOD(OnDataAvailable)(DWORD bscf, DWORD size, FORMATETC* format_etc, + STDMETHOD(OnDataAvailable)(DWORD flags, DWORD size, FORMATETC* format_etc, STGMEDIUM* stgmed); - STDMETHOD(OnObjectAvailable)(REFIID iid, IUnknown* unk); - - // IBindStatusCallbackEx - STDMETHOD(GetBindInfoEx)(DWORD* bindf, BINDINFO* bind_info, DWORD* bindf2, - DWORD* reserved); - - // IHttpNegotiate - STDMETHOD(BeginningTransaction)(LPCWSTR url, LPCWSTR headers, DWORD reserved, - LPWSTR* additional_headers); - STDMETHOD(OnResponse)(DWORD response_code, LPCWSTR response_headers, - LPCWSTR request_headers, LPWSTR* additional_headers); + STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR error); - // IHttpNegotiate2 - STDMETHOD(GetRootSecurityId)(BYTE* security_id, DWORD* security_id_size, - DWORD_PTR reserved); + protected: + SniffData data_sniffer_; - // IHttpNegotiate3 - STDMETHOD(GetSerializedClientCertContext)(BYTE** cert, DWORD* cert_size); + // A structure to cache the progress notifications + struct Progress { + ULONG progress_; + ULONG progress_max_; + ULONG status_code_; + std::wstring status_text_; + }; - protected: - ScopedComPtr<IBindStatusCallback> delegate_; - ScopedComPtr<IBindCtx> bind_ctx_; - ScopedComPtr<SimpleBindingImpl, &GUID_NULL> binding_delegate_; - bool only_buffer_; - scoped_refptr<RequestData> data_; - std::wstring redirected_url_; + std::vector<Progress> saved_progress_; + CLIPFORMAT clip_format_; private: - DISALLOW_COPY_AND_ASSIGN(CFUrlmonBindStatusCallback); + DISALLOW_COPY_AND_ASSIGN(BSCBStorageBind); }; #endif // CHROME_FRAME_URLMON_BIND_STATUS_CALLBACK_H_ - diff --git a/chrome_frame/urlmon_moniker.cc b/chrome_frame/urlmon_moniker.cc index 77c1fc6..fc66e9f 100644 --- a/chrome_frame/urlmon_moniker.cc +++ b/chrome_frame/urlmon_moniker.cc @@ -8,12 +8,14 @@ #include "base/string_util.h" #include "chrome_frame/bho.h" +#include "chrome_frame/chrome_active_document.h" #include "chrome_frame/urlmon_bind_status_callback.h" #include "chrome_frame/vtable_patch_manager.h" #include "net/http/http_util.h" static const int kMonikerBindToObject = 8; static const int kMonikerBindToStorage = kMonikerBindToObject + 1; +static wchar_t kBindContextParamName[] = L"_CHROMEFRAME_PRECREATE_"; base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > NavigationManager::thread_singleton_(base::LINKER_INITIALIZED); @@ -48,258 +50,6 @@ std::string FindReferrerFromHeaders(const wchar_t* headers, } // end namespace -void ReadStreamCache::RewindCache() const { - DCHECK(cache_); - RewindStream(cache_); -} - -HRESULT ReadStreamCache::WriteToCache(const void* data, ULONG size, - ULONG* written) { - HRESULT hr = S_OK; - if (!cache_) { - hr = ::CreateStreamOnHGlobal(NULL, TRUE, cache_.Receive()); - DCHECK(cache_); - } - - if (SUCCEEDED(hr)) - hr = cache_->Write(data, size, written); - - return hr; -} - -size_t ReadStreamCache::GetCacheSize() const { - size_t ret = 0; - if (cache_) { - STATSTG stg = {0}; - cache_->Stat(&stg, STATFLAG_NONAME); - DCHECK_EQ(stg.cbSize.HighPart, 0); - ret = stg.cbSize.LowPart; - } - - return ret; -} - -HRESULT ReadStreamCache::Read(void* pv, ULONG cb, ULONG* read) { - DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", - ::GetCurrentThreadId()); - DCHECK(delegate_); - DWORD read_bytes = 0; - HRESULT hr = delegate_->Read(pv, cb, &read_bytes); - if (read) - *read = read_bytes; - - // Note that Read() might return E_PENDING so we just check if anything - // was read rather than check hr. - if (read_bytes) { - DWORD written = 0; - WriteToCache(pv, read_bytes, &written); - DCHECK(read_bytes == written); - } - - return hr; -} - -///////////////////////////// - -HRESULT SimpleBindingImpl::GetBindResult(CLSID* protocol, DWORD* result_code, - LPOLESTR* result, DWORD* reserved) { - DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", - ::GetCurrentThreadId()); - - HRESULT hr = S_OK; - - if (protocol) - *protocol = GUID_NULL; - - if (result) - *result = NULL; - - if (delegate_) - hr = delegate_->GetBindResult(protocol, result_code, result, reserved); - - if (SUCCEEDED(hr) && bind_results_ != S_OK) - *result_code = bind_results_; - - return hr; -} - -HRESULT SimpleBindingImpl::DelegateQI(void* obj, REFIID iid, void** ret, - DWORD cookie) { - SimpleBindingImpl* me = reinterpret_cast<SimpleBindingImpl*>(obj); - HRESULT hr = E_NOINTERFACE; - if (me->delegate_) - hr = me->delegate_.QueryInterface(iid, ret); - DLOG(INFO) << __FUNCTION__ << " " << GuidToString(iid) - << StringPrintf(" 0x%08X", hr); - return hr; -} - -/////////////////////////////// - -void RequestHeaders::OnBeginningTransaction(const wchar_t* url, - const wchar_t* headers, - const wchar_t* additional_headers) { - if (url) - request_url_ = url; - - if (headers) - begin_request_headers_ = headers; - - if (additional_headers) - additional_request_headers_ = additional_headers; - - DLOG(INFO) << __FUNCTION__ << "\n " << request_url_ << "\n " - << begin_request_headers_ << "\n " - << additional_request_headers_; - - NavigationManager* mgr = NavigationManager::GetThreadInstance(); - if (mgr) { - DCHECK(mgr->IsTopLevelUrl(request_url_.c_str())); - mgr->SetActiveRequestHeaders(this); - } -} - -void RequestHeaders::OnResponse(DWORD response_code, - const wchar_t* response_headers, - const wchar_t* request_headers) { - response_code_ = response_code; - - if (response_headers) - response_headers_ = response_headers; - - if (request_headers) - request_headers_ = request_headers; - -#ifndef NDEBUG - NavigationManager* mgr = NavigationManager::GetThreadInstance(); - if (mgr) { - // For some reason we might not have gotten an OnBeginningTransaction call! - // DCHECK(mgr->IsTopLevelUrl(request_url_.c_str())); - // DCHECK(mgr->GetActiveRequestHeaders() == this); - mgr->SetActiveRequestHeaders(this); - } -#endif -} - -std::string RequestHeaders::GetReferrer() { - std::string referrer = FindReferrerFromHeaders(begin_request_headers_.c_str(), - additional_request_headers_.c_str()); - if (referrer.empty()) { - DLOG(INFO) << request_url_ << "\nbheaders: " << begin_request_headers_ - << "\nadd:" << additional_request_headers_ << "\nreq: " << request_headers_; - } - return referrer; -} - -HRESULT RequestHeaders::FireHttpNegotiateEvents(IHttpNegotiate* http) const { - DCHECK(http); - - LPOLESTR additional_headers = NULL; - HRESULT hr = http->BeginningTransaction(request_url_.c_str(), - begin_request_headers_.c_str(), 0, &additional_headers); - DCHECK(SUCCEEDED(hr)); - ::CoTaskMemFree(additional_headers); - - // Also notify the navigation manager on this thread if one exists. - NavigationManager* mgr = NavigationManager::GetThreadInstance(); - if (mgr) { - mgr->OnBeginningTransaction(true, request_url_.c_str(), - begin_request_headers_.c_str(), additional_request_headers_.c_str()); - } - - additional_headers = NULL; - hr = http->OnResponse(response_code_, response_headers_.c_str(), - request_headers_.c_str(), &additional_headers); - DCHECK(SUCCEEDED(hr)); - if (additional_headers) { - ::CoTaskMemFree(additional_headers); - additional_headers = NULL; - } - - return hr; -} - -/////////////////////////////// - -RequestData::RequestData() { - ZeroMemory(&format_, sizeof(format_)); - format_.dwAspect = 1; - format_.lindex = -1; - format_.tymed = TYMED_ISTREAM; - CComObject<ReadStreamCache>* stream = NULL; - CComObject<ReadStreamCache>::CreateInstance(&stream); - DCHECK(stream); - stream_delegate_ = stream; - DCHECK_EQ(stream->m_dwRef, 1); -} - -RequestData::~RequestData() { -} - -void RequestData::Initialize(RequestHeaders* headers) { - if (headers) { - headers_ = headers; - } else { - headers_ = new RequestHeaders(); - } -} - -HRESULT RequestData::DelegateDataRead(IBindStatusCallback* callback, - DWORD flags, DWORD size, - FORMATETC* format, STGMEDIUM* storage, - size_t* bytes_read) { - DCHECK(callback); - DCHECK(storage); - if ((flags & BSCF_FIRSTDATANOTIFICATION) && format) { - format_ = *format; - } - - IStream* original = storage->pstm; - DCHECK(original); - if (!original) - return E_POINTER; - - size_t size_before = stream_delegate_->GetCacheSize(); - stream_delegate_->SetDelegate(storage->pstm); - storage->pstm = stream_delegate_; - HRESULT hr = callback->OnDataAvailable(flags, size, format, storage); - storage->pstm = original; - if (bytes_read) - *bytes_read = stream_delegate_->GetCacheSize() - size_before; - return hr; -} - -void RequestData::CacheAll(IStream* data) { - DCHECK(data); - char buffer[4096]; - HRESULT hr = S_OK; - while (hr == S_OK) { - DWORD read = 0; - hr = data->Read(buffer, arraysize(buffer), &read); - if (read) { - DWORD written = 0; - stream_delegate_->WriteToCache(buffer, read, &written); - DCHECK(read == written); - } else { - // Just in case Read() completed with S_OK but zero bytes. - break; - } - } -} - -HRESULT RequestData::GetResetCachedContentStream(IStream** clone) { - DCHECK(clone); - IStream* cache = stream_delegate_->cache(); - if (!cache) - return HRESULT_FROM_WIN32(ERROR_EMPTY); - HRESULT hr = cache->Clone(clone); - if (SUCCEEDED(hr)) { - RewindStream(*clone); - } else { - NOTREACHED(); - } - return hr; -} //////////////////////////// @@ -309,9 +59,12 @@ HRESULT NavigationManager::NavigateToCurrentUrlInCF(IBrowserService* browser) { MarkBrowserOnThreadForCFNavigation(browser); + HRESULT hr = S_OK; + ScopedComPtr<IShellBrowser> shell_browser; ScopedComPtr<IBindCtx> bind_context; + hr = ::CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, bind_context.Receive(), 0); + ScopedComPtr<IMoniker> moniker; - HRESULT hr = ::CreateBindCtx(0, bind_context.Receive()); DCHECK(bind_context); if (SUCCEEDED(hr) && SUCCEEDED(hr = ::CreateURLMonikerEx(NULL, url_.c_str(), moniker.Receive(), @@ -341,21 +94,6 @@ HRESULT NavigationManager::NavigateToCurrentUrlInCF(IBrowserService* browser) { return hr; } -void NavigationManager::SetActiveRequestData(RequestData* request_data) { - DLOG(INFO) << __FUNCTION__ << StringPrintf(" 0x%08X", request_data); - DCHECK(request_data_ == NULL || request_data == NULL); - DLOG_IF(ERROR, url_.empty() && request_data) - << "attempt to cache data after url has been cleared"; - // Clear the current request headers as the request data must have the - // correct set of headers. - DCHECK(request_data == NULL || request_data->headers() != NULL); - request_headers_ = NULL; - - if (request_data == NULL || !url_.empty()) { - request_data_ = request_data; - } -} - void NavigationManager::OnBeginningTransaction(bool is_top_level, const wchar_t* url, const wchar_t* headers, const wchar_t* additional_headers) { @@ -397,6 +135,53 @@ void NavigationManager::UnregisterThreadInstance() { thread_singleton_.Pointer()->Set(NULL); } +// Mark a bind context for navigation by storing a bind context param. +HRESULT NavigationManager::AttachCFObject(IBindCtx* bind_context) { + if (!bind_context) { + NOTREACHED(); + return E_INVALIDARG; + } + + CComObject<ChromeActiveDocument>* cf_doc = NULL; + HRESULT hr = CComObject<ChromeActiveDocument>::CreateInstance(&cf_doc); + if (cf_doc) { + hr = bind_context->RegisterObjectParam(kBindContextParamName, + static_cast<IPersistMoniker*>(cf_doc)); + if (FAILED(hr)) + delete cf_doc; + } + + return hr; +} + +HRESULT NavigationManager::DetachCFObject(IMoniker* moniker, + IBindCtx* bind_context, + IUnknown** object) { + if (!bind_context || !moniker) { + NOTREACHED(); + return E_INVALIDARG; + } + + std::wstring url = GetActualUrlFromMoniker(moniker, bind_context, + std::wstring()); + NavigationManager* mgr = GetThreadInstance(); + if (mgr && !mgr->IsTopLevelUrl(url.c_str())) { + DLOG(WARNING) << "attempt to switch-in at non-top level.\n" << + "Top level url: " << mgr->url() << "\nUrl being loaded: " << url; + return E_FAIL; + } + + ScopedComPtr<IUnknown> cf_doc; + HRESULT hr = S_OK; + hr = bind_context->GetObjectParam(kBindContextParamName, cf_doc.Receive()); + if (cf_doc) { + hr = bind_context->RevokeObjectParam(kBindContextParamName); + *object = cf_doc.Detach(); + } + + return hr; +} + ///////////////////////////////////////// // static @@ -423,64 +208,46 @@ void MonikerPatch::Uninitialize() { vtable_patch::UnpatchInterfaceMethods(IMoniker_PatchInfo); } +bool ShouldWrapCallback(IMoniker* moniker, IBindCtx* bind_context) { + NavigationManager* mgr = NavigationManager::GetThreadInstance(); + if (!mgr) { + DLOG(INFO) << __FUNCTION__ << "No navitation manager to wrap"; + return false; + } + + CComHeapPtr<WCHAR> url; + HRESULT hr = moniker->GetDisplayName(bind_context, NULL, &url); + DCHECK(SUCCEEDED(hr)); + bool should_wrap = mgr->IsTopLevelUrl(url); + return should_wrap; +} + // static HRESULT MonikerPatch::BindToObject(IMoniker_BindToObject_Fn original, IMoniker* me, IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj) { DLOG(INFO) << __FUNCTION__; DCHECK(to_left == NULL); - CComHeapPtr<WCHAR> url; - HRESULT hr = me->GetDisplayName(bind_ctx, NULL, &url); - DCHECK(SUCCEEDED(hr)); - NavigationManager* mgr = NavigationManager::GetThreadInstance(); - if (mgr) { - bool interest = mgr ? mgr->IsTopLevelUrl(url) : false; - if (interest) { - scoped_refptr<RequestData> request_data(mgr->GetActiveRequestData(url)); - if (request_data) { - DLOG(INFO) << " got cached content"; - mgr->set_referrer(request_data->headers()->GetReferrer()); - DLOG(INFO) << "referrer is: " << mgr->referrer(); - // Create a new CF document object and initialize it with - // the already cached data. - ScopedComPtr<IUnknown> cf_doc; - hr = cf_doc.CreateInstance(CLSID_ChromeActiveDocument); - if (FAILED(hr)) { - NOTREACHED() << "Failed to cocreate active document. Error:" << hr; - return original(me, bind_ctx, to_left, iid, obj); - } - ScopedComPtr<IPersistMoniker> persist_moniker; - hr = persist_moniker.QueryFrom(cf_doc); - DCHECK(SUCCEEDED(hr)); - hr = persist_moniker->Load(TRUE, me, bind_ctx, STGM_READ); - if (SUCCEEDED(hr)) { - hr = persist_moniker.QueryInterface(iid, obj); - DCHECK(SUCCEEDED(hr)); - } + + HRESULT hr = S_OK; + ScopedComPtr<IUnknown> cf_doc; + NavigationManager::DetachCFObject(me, bind_ctx, cf_doc.Receive()); + if (cf_doc) { + ScopedComPtr<IPersistMoniker> persist_moniker; + hr = persist_moniker.QueryFrom(cf_doc); + if (persist_moniker) { + hr = persist_moniker->Load(TRUE, me, bind_ctx, STGM_READ); + if (SUCCEEDED(hr)) { + return persist_moniker.QueryInterface(iid, obj); } else { - DLOG(INFO) << " creating callback object"; - CComObject<CFUrlmonBindStatusCallback>* callback = NULL; - hr = CComObject<CFUrlmonBindStatusCallback>::CreateInstance( - &callback); - callback->AddRef(); - hr = callback->Initialize(bind_ctx, mgr->GetActiveRequestHeaders()); - DCHECK(SUCCEEDED(hr)); - hr = original(me, bind_ctx, to_left, iid, obj); - callback->Release(); - if (SUCCEEDED(hr) && (*obj) == NULL) { - DCHECK(hr == MK_S_ASYNCHRONOUS); - } + NOTREACHED() << StringPrintf("CF ActiveDoc::Load error: 0x%x", hr); } } else { - DLOG(INFO) << " -- calling original. (no interest)"; - hr = original(me, bind_ctx, to_left, iid, obj); + NOTREACHED() << StringPrintf("CF IPersistMoniker QI failed: 0x%x", hr); } - } else { - // We don't have a NavigationManager instance for cases like View Source - // etc. - hr = original(me, bind_ctx, to_left, iid, obj); } + hr = original(me, bind_ctx, to_left, iid, obj); return hr; } @@ -489,128 +256,24 @@ HRESULT MonikerPatch::BindToStorage(IMoniker_BindToStorage_Fn original, IMoniker* me, IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj) { DLOG(INFO) << __FUNCTION__; - DCHECK(iid == IID_IStream || iid == IID_IUnknown); DCHECK(to_left == NULL); - HRESULT hr = E_UNEXPECTED; - NavigationManager* mgr = NavigationManager::GetThreadInstance(); - if (mgr) { - CComHeapPtr<WCHAR> url; - hr = me->GetDisplayName(bind_ctx, NULL, &url); + HRESULT hr = S_OK; + CComObject<BSCBStorageBind>* callback = NULL; + if ((IsEqualIID(IID_IStream, iid)) && ShouldWrapCallback(me, bind_ctx)) { + hr = CComObject<BSCBStorageBind>::CreateInstance(&callback); + callback->AddRef(); + hr = callback->Initialize(me, bind_ctx); DCHECK(SUCCEEDED(hr)); - bool interest = mgr->IsTopLevelUrl(url); - DLOG(INFO) << "interest: " << interest << " url " << url; - if (interest) { - scoped_refptr<RequestData> request_data(mgr->GetActiveRequestData(url)); - if (request_data) { - CComObject<SimpleBindingImpl>* binding = NULL; - CComObject<SimpleBindingImpl>::CreateInstance(&binding); - binding->OverrideBindResults(INET_E_TERMINATED_BIND); - binding->AddRef(); - hr = BindToStorageFromCache(bind_ctx, kChromeMimeType, request_data, - binding, reinterpret_cast<IStream**>(obj)); - binding->Release(); - } else { - DLOG(INFO) << " creating callback object"; - CComObject<CFUrlmonBindStatusCallback>* callback = NULL; - hr = CComObject<CFUrlmonBindStatusCallback>::CreateInstance( - &callback); - callback->AddRef(); - hr = callback->Initialize(bind_ctx, mgr->GetActiveRequestHeaders()); - DCHECK(SUCCEEDED(hr)); - hr = original(me, bind_ctx, to_left, iid, obj); - callback->Release(); - if (SUCCEEDED(hr) && (*obj) == NULL) { - DCHECK(hr == MK_S_ASYNCHRONOUS); - } - } - } else { - hr = original(me, bind_ctx, to_left, iid, obj); - } - } else { - // We usually only get here when we're issuing requests from our - // worker thread. However there are some exceptions such as when - // windows media player is issuing requests, so we don't DCHECK. - hr = original(me, bind_ctx, to_left, iid, obj); - } - - return hr; -} - -HRESULT MonikerPatch::BindToStorageFromCache(IBindCtx* bind_ctx, - const wchar_t* mime_type, - RequestData* data, - SimpleBindingImpl* binding, - IStream** cache_out) { - DCHECK(bind_ctx); - // mime_type may be NULL - DCHECK(data); - DCHECK(binding); - // cache_out may be NULL - - ScopedComPtr<IUnknown> holder; - HRESULT hr = bind_ctx->GetObjectParam(L"_BSCB_Holder_", holder.Receive()); - DCHECK(holder); - ScopedComPtr<IBindStatusCallback> bscb; - ScopedComPtr<IHttpNegotiate> http_negotiate; - DoQueryService(IID_IBindStatusCallback, holder, bscb.Receive()); - DoQueryService(IID_IHttpNegotiate, bscb, http_negotiate.Receive()); - if (!bscb) { - NOTREACHED(); - return E_NOINTERFACE; } - hr = bscb->OnStartBinding(0, binding); - DCHECK(SUCCEEDED(hr)); - if (SUCCEEDED(hr)) { - // Fire BeginningTransaction and OnResponse events. - if (http_negotiate) - data->headers()->FireHttpNegotiateEvents(http_negotiate); - - FORMATETC format_etc = data->format(); - if (mime_type) { - // If a non NULL mime type was specified, override the clipboard format. - format_etc.cfFormat = ::RegisterClipboardFormatW(mime_type); - DCHECK(format_etc.cfFormat); - } + hr = original(me, bind_ctx, to_left, iid, obj); - const wchar_t* mime_type_available = mime_type; - wchar_t format_name[MAX_PATH] = {0}; - if (!mime_type_available) { - ::GetClipboardFormatName(format_etc.cfFormat, format_name, - arraysize(format_name)); - mime_type_available = format_name; - } - DCHECK(mime_type_available); - hr = bscb->OnProgress(0, 0, BINDSTATUS_MIMETYPEAVAILABLE, - mime_type_available); - - ScopedComPtr<IStream> cache; - data->GetResetCachedContentStream(cache.Receive()); - - STGMEDIUM medium = {0}; - medium.tymed = TYMED_ISTREAM; - medium.pstm = cache; - DLOG(INFO) << __FUNCTION__ << " total bytes available: " - << data->GetCachedContentSize(); - hr = bscb->OnDataAvailable( - BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | - BSCF_DATAFULLYAVAILABLE, data->GetCachedContentSize(), &format_etc, - &medium); - - // OnDataAvailable might report a failure such as INET_E_TERMINATED_BIND - // when mshtml decides not to handle the request. - // We ignore the return value from OnStopBinding and return whatever - // OnDataAvailable gave us. - if (FAILED(hr)) { - binding->OverrideBindResults(hr); - } else if (cache_out) { - *cache_out = cache.Detach(); - } - - bscb->OnStopBinding(hr, NULL); - } + // If the binding terminates before the data could be played back + // now is the chance. Sometimes OnStopBinding happens after this returns + // and then it's too late. + if ((S_OK == hr) && callback) + callback->MayPlayBack(BSCF_LASTDATANOTIFICATION); return hr; -} - +}
\ No newline at end of file diff --git a/chrome_frame/urlmon_moniker.h b/chrome_frame/urlmon_moniker.h index d4b6bbb..885b7fe 100644 --- a/chrome_frame/urlmon_moniker.h +++ b/chrome_frame/urlmon_moniker.h @@ -14,8 +14,6 @@ #include "base/logging.h" #include "base/scoped_comptr_win.h" #include "base/thread_local.h" - -#include "chrome_frame/urlmon_moniker_base.h" #include "chrome_frame/utils.h" // This file contains classes that are used to cache the contents of a top-level @@ -29,14 +27,12 @@ // - Bho::BeforeNavigate - top level url = www.msn.com // - MSHTML -> MonikerPatch::BindToStorage. // (IEFrame starts this by calling mshtml!*SuperNavigate*) -// - request_data is NULL // - check if the url is a top level url // - iff the url is a top level url, we switch in our own callback object -// and hook it up to the bind context (CFUrlmonBindStatusCallback) -// The callback object caches the document in memory. +// and hook it up to the bind context (BSCBStorageBind) // - otherwise just call the original -// - Bho::OnHttpEquiv is called with done == TRUE. At this point we determine -// that the page did not have the CF meta tag in it and we delete the cache. +// - BSCBStorageBind::OnDataAvailable - sniffs data and determines that the +// renderer is not chrome. Goes into pass through mode. // - The page loads in mshtml. // @@ -47,41 +43,18 @@ // (IEFrame starts this by calling mshtml!*SuperNavigate*) // - request_data is NULL // - check if the url is a top level url -// - iff the url is a top level url (in this case, yes), we switch in our own -// callback object and hook it up to the bind context -// (CFUrlmonBindStatusCallback) -// - As the document is fetched, the callback object caches the -// document in memory. -// - Bho::OnHttpEquiv (done == FALSE) -// - mgr->NavigateToCurrentUrlInCF -// - Set TLS (MarkBrowserOnThreadForCFNavigation) -// - Create new bind context, moniker for the URL -// - NavigateBrowserToMoniker with new moniker, bind context -// - Our callback _may_ get an error from mshtml indicating that mshtml -// isn't interested in the data anymore (since we started a new -// navigation). If that happens, our callback class -// (CFUrlmonBindStatusCallback) will continue to cache the document -// until all of it has been retrieved successfully. When the data -// is all read, we report INET_E_TERMINATE_BIND (see SimpleBindingImpl) -// as the end result. -// - In the case where all of the data has been downloaded before -// - OnHttpEquiv is called, we will already have the cache but the -// end bind status in the callback will be S_OK. -// - Bho::BeforeNavigate2 - top level url = http://wave.google.com/ +// - iff the url is a top level url, we switch in our own callback object +// and hook it up to the bind context (BSCBStorageBind) +// - BSCBStorageBind::OnDataAvailable - sniffs data and determines that the +// renderer is chrome. It then registers a special bind context param and +// sets a magic clip format in the format_etc. Then goes into pass through +// mode. +// - mshtml looks at the clip format and re-issues the navigation with the +// same bind context. Also returns INET_E_TERMINATED_BIND so that same +// underlying transaction objects are used. // - IEFrame -> MonikerPatch::BindToStorage -// - request_data is not NULL since we now have a cached copy of the content. -// - We call BindToStorageFromCache. -// - HttpNegotiatePatch::ReportProgress -// - Check TLS (CheckForCFNavigation) and report chrome mime type -// - IEFrame does the following: -// - Creates a new moniker -// - Calls MonikerPatch::BindToObject -// - We create an instance of ChromeActiveDocument and initialize it -// with the cached document. -// - ChromeActiveDocument gives the UrlmonUrlRequestManager the cached -// contents (RequestData) which the manager will use as the content -// when serving up content for the CF document. -// +// - We check for the special bind context param and instantiate and +// return our ActiveDoc // // Scenario 3: CF navigation through mshtml link @@ -102,169 +75,6 @@ // - [Scenario 2] // -// An implementation of IStream that delegates to another source -// but caches all data that is read from the source. -class ReadStreamCache - : public CComObjectRootEx<CComSingleThreadModel>, - public DelegatingReadStream { - public: - ReadStreamCache() { - DLOG(INFO) << __FUNCTION__; - } - - ~ReadStreamCache() { - DLOG(INFO) << __FUNCTION__ << " cache: " << GetCacheSize(); - } - -BEGIN_COM_MAP(ReadStreamCache) - COM_INTERFACE_ENTRY(IStream) - COM_INTERFACE_ENTRY(ISequentialStream) -END_COM_MAP() - - IStream* cache() const { - return cache_; - } - - void RewindCache() const; - - HRESULT WriteToCache(const void* data, ULONG size, ULONG* written); - - // Returns how many bytes we've cached. Used for logging. - size_t GetCacheSize() const; - - // ISequentialStream. - STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read); - - protected: - ScopedComPtr<IStream> cache_; - - private: - DISALLOW_COPY_AND_ASSIGN(ReadStreamCache); -}; - -// Basic implementation of IBinding with the option of offering a way -// to override the bind result error value. -class SimpleBindingImpl - : public CComObjectRootEx<CComSingleThreadModel>, - public DelegatingBinding { - public: - SimpleBindingImpl() : bind_results_(S_OK) { - } - - ~SimpleBindingImpl() { - } - -BEGIN_COM_MAP(SimpleBindingImpl) - COM_INTERFACE_ENTRY(IBinding) - COM_INTERFACE_ENTRY_FUNC_BLIND(0, DelegateQI) -END_COM_MAP() - - static STDMETHODIMP DelegateQI(void* obj, REFIID iid, void** ret, - DWORD cookie); - - STDMETHOD(GetBindResult)(CLSID* protocol, DWORD* result_code, - LPOLESTR* result, DWORD* reserved); - - void OverrideBindResults(HRESULT results) { - bind_results_ = results; - } - - protected: - HRESULT bind_results_; - - private: - DISALLOW_COPY_AND_ASSIGN(SimpleBindingImpl); -}; - -class RequestHeaders - : public base::RefCountedThreadSafe<RequestHeaders> { - public: - RequestHeaders() : response_code_(-1) { - } - - ~RequestHeaders() { - } - - void OnBeginningTransaction(const wchar_t* url, const wchar_t* headers, - const wchar_t* additional_headers); - - void OnResponse(DWORD response_code, const wchar_t* response_headers, - const wchar_t* request_headers); - - const std::wstring& request_url() const { - return request_url_; - } - - // Invokes BeginningTransaction and OnResponse on the |http| object - // providing already cached headers and status values. - // Any additional headers returned from either of the two methods are ignored. - HRESULT FireHttpNegotiateEvents(IHttpNegotiate* http) const; - - std::string GetReferrer(); - - protected: - std::wstring request_url_; - std::wstring begin_request_headers_; - std::wstring additional_request_headers_; - std::wstring request_headers_; - std::wstring response_headers_; - DWORD response_code_; - - private: - DISALLOW_COPY_AND_ASSIGN(RequestHeaders); -}; - -// Holds cached data for a urlmon request. -class RequestData - : public base::RefCountedThreadSafe<RequestData> { - public: - RequestData(); - ~RequestData(); - - void Initialize(RequestHeaders* headers); - - // Calls IBindStatusCallback::OnDataAvailable and caches any data that is - // read during that operation. - // We also cache the format of the data stream if available during the first - // call to this method. - HRESULT DelegateDataRead(IBindStatusCallback* callback, DWORD flags, - DWORD size, FORMATETC* format, STGMEDIUM* storage, - size_t* bytes_read); - - // Reads everything that's available from |data| into a cached stream. - void CacheAll(IStream* data); - - // Returns a new stream object to read the cache. - // The returned stream object's seek pointer is at pos 0. - HRESULT GetResetCachedContentStream(IStream** clone); - - size_t GetCachedContentSize() const { - return stream_delegate_->GetCacheSize(); - } - - const FORMATETC& format() const { - return format_; - } - - RequestHeaders* headers() const { - return headers_; - } - - void set_headers(RequestHeaders* headers) { - DCHECK(headers); - DCHECK(headers_ == NULL); - headers_ = headers; - } - - protected: - ScopedComPtr<ReadStreamCache, &GUID_NULL> stream_delegate_; - FORMATETC format_; - scoped_refptr<RequestHeaders> headers_; - - private: - DISALLOW_COPY_AND_ASSIGN(RequestData); -}; - // This class is the link between a few static, moniker related functions to // the bho. The specific services needed by those functions are abstracted into // this interface for easier testability. @@ -277,6 +87,11 @@ class NavigationManager { // TLS. Returns NULL if no instance exists on the current thread. static NavigationManager* GetThreadInstance(); + // Mark a bind context for navigation by storing a bind context param. + static HRESULT AttachCFObject(IBindCtx* bind_context); + static HRESULT DetachCFObject(IMoniker* moniker, IBindCtx* bind_context, + IUnknown** object); + void RegisterThreadInstance(); void UnregisterThreadInstance(); @@ -313,14 +128,6 @@ class NavigationManager { referrer_ = referrer; } - // Called when a top level navigation has finished and we don't need to - // keep the cached content around anymore. - virtual void ReleaseRequestData() { - DLOG(INFO) << __FUNCTION__; - url_.clear(); - SetActiveRequestData(NULL); - } - // Return true if this is a URL that represents a top-level // document that might have to be rendered in CF. virtual bool IsTopLevelUrl(const wchar_t* url) { @@ -338,30 +145,10 @@ class NavigationManager { // and need to switch over from mshtml to CF. virtual HRESULT NavigateToCurrentUrlInCF(IBrowserService* browser); - virtual void SetActiveRequestData(RequestData* request_data); - - // When BindToObject is called on a URL before BindToStorage is called, - // the request and response headers are reported on that moniker. - // Later BindToStorage is called to fetch the content. We use the - // RequestHeaders class to carry over the headers to the RequestData object - // that will be created to hold the content. - virtual void SetActiveRequestHeaders(RequestHeaders* request_headers) { - request_headers_ = request_headers; - } - - virtual RequestHeaders* GetActiveRequestHeaders() { - return request_headers_; - } - - virtual RequestData* GetActiveRequestData(const wchar_t* url) { - return IsTopLevelUrl(url) ? request_data_.get() : NULL; - } - protected: std::string referrer_; std::wstring url_; - scoped_refptr<RequestData> request_data_; - scoped_refptr<RequestHeaders> request_headers_; + static base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > thread_singleton_; @@ -404,14 +191,6 @@ class MonikerPatch { IMoniker* me, IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj); - // Reads content from cache (owned by RequestData) and simulates a regular - // binding by calling the expected methods on the callback object bound to - // the bind context. - static HRESULT BindToStorageFromCache(IBindCtx* bind_ctx, - const wchar_t* mime_type, - RequestData* data, - SimpleBindingImpl* binding, - IStream** cache_out); }; #endif // CHROME_FRAME_URLMON_MONIKER_H_ diff --git a/chrome_frame/urlmon_moniker_base.h b/chrome_frame/urlmon_moniker_base.h deleted file mode 100644 index 6dd022c..0000000 --- a/chrome_frame/urlmon_moniker_base.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_FRAME_URLMON_MONIKER_BASE_H_ -#define CHROME_FRAME_URLMON_MONIKER_BASE_H_ - -#include <atlbase.h> -#include <atlcom.h> - -// An implementation of IStream (minus IUnknown) that delegates to -// another source. Most of the methods contain a NOTREACHED() as the -// class is used by the ReadStreamCache class where we don't expect those -// calls under normal circumstances. -class DelegatingReadStream : public IStream { - public: - DelegatingReadStream() { - } - - ~DelegatingReadStream() { - } - - void SetDelegate(IStream* delegate) { - delegate_ = delegate; - } - - // ISequentialStream. - STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read) { - return delegate_->Read(pv, cb, read); - } - - STDMETHOD(Write)(const void* pv, ULONG cb, ULONG* written) { - NOTREACHED(); - return delegate_->Write(pv, cb, written); - } - - // IStream. - STDMETHOD(Seek)(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER* new_pos) { - NOTREACHED(); - return delegate_->Seek(move, origin, new_pos); - } - - STDMETHOD(SetSize)(ULARGE_INTEGER new_size) { - NOTREACHED(); - return delegate_->SetSize(new_size); - } - - STDMETHOD(CopyTo)(IStream* stream, ULARGE_INTEGER cb, ULARGE_INTEGER* read, - ULARGE_INTEGER* written) { - NOTREACHED(); - return delegate_->CopyTo(stream, cb, read, written); - } - - STDMETHOD(Commit)(DWORD commit_flags) { - NOTREACHED(); - return delegate_->Commit(commit_flags); - } - - STDMETHOD(Revert)() { - NOTREACHED(); - return delegate_->Revert(); - } - - STDMETHOD(LockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb, - DWORD lock_type) { - NOTREACHED(); - return delegate_->LockRegion(offset, cb, lock_type); - } - - STDMETHOD(UnlockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb, - DWORD lock_type) { - NOTREACHED(); - return delegate_->UnlockRegion(offset, cb, lock_type); - } - - STDMETHOD(Stat)(STATSTG* stat_stg, DWORD stat_flag) { - DCHECK(delegate_); - return delegate_->Stat(stat_stg, stat_flag); - } - - STDMETHOD(Clone)(IStream** stream) { - NOTREACHED(); - return delegate_->Clone(stream); - } - - protected: - ScopedComPtr<IStream> delegate_; -}; - -// A very basic implementation of IBinding that forwards all calls -// to a delegate. -class DelegatingBinding : public IBinding { - public: - DelegatingBinding() { - } - - ~DelegatingBinding() { - } - - IBinding* delegate() const { - return delegate_; - } - - void SetDelegate(IBinding* delegate) { - delegate_ = delegate; - } - - STDMETHOD(Abort)() { - DCHECK(delegate_); - if (!delegate_) - return S_OK; - return delegate_->Abort(); - } - - STDMETHOD(Suspend)() { - DCHECK(delegate_); - if (!delegate_) - return E_FAIL; - return delegate_->Suspend(); - } - - STDMETHOD(Resume)() { - DCHECK(delegate_); - if (!delegate_) - return E_FAIL; - return delegate_->Resume(); - } - - STDMETHOD(SetPriority)(LONG priority) { - DCHECK(delegate_); - if (!delegate_) - return E_FAIL; - return delegate_->SetPriority(priority); - } - - STDMETHOD(GetPriority)(LONG* priority) { - DCHECK(delegate_); - if (!delegate_) - return E_FAIL; - return delegate_->GetPriority(priority); - } - - STDMETHOD(GetBindResult)(CLSID* protocol, DWORD* result_code, - LPOLESTR* result, DWORD* reserved) { - DCHECK(delegate_); - if (!delegate_) - return E_FAIL; - return delegate_->GetBindResult(protocol, result_code, result, reserved); - } - - protected: - ScopedComPtr<IBinding> delegate_; -}; - -#endif // CHROME_FRAME_URLMON_MONIKER_BASE_H_ - diff --git a/chrome_frame/urlmon_upload_data_stream.h b/chrome_frame/urlmon_upload_data_stream.h index 30b7271..03316b7 100644 --- a/chrome_frame/urlmon_upload_data_stream.h +++ b/chrome_frame/urlmon_upload_data_stream.h @@ -11,14 +11,14 @@ #include "base/logging.h" #include "base/ref_counted.h" - +#include "chrome_frame/stream_impl.h" #include "net/base/upload_data.h" #include "net/base/upload_data_stream.h" // Provides an IStream interface to the very different UploadDataStream // implementation. class UrlmonUploadDataStream : public CComObjectRootEx<CComMultiThreadModel>, - public IStream { + public StreamImpl { public: UrlmonUploadDataStream() {} @@ -31,56 +31,9 @@ class UrlmonUploadDataStream : public CComObjectRootEx<CComMultiThreadModel>, // Partial implementation of IStream. STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read); - - // E_NOTIMPL the rest and DCHECK if they get called (could also use - // IStreamImpl but we'd lose the DCHECKS(). - STDMETHOD(Write)(const void * buffer, ULONG size, ULONG* size_written) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(CopyTo)(IStream* stream, ULARGE_INTEGER cb, ULARGE_INTEGER* read, - ULARGE_INTEGER* written) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - STDMETHOD(Seek)(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER* new_pos); - - STDMETHOD(SetSize)(ULARGE_INTEGER new_size) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(Commit)(DWORD flags) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(Revert)() { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(LockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb, - DWORD type) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - - STDMETHOD(UnlockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb, - DWORD type) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - STDMETHOD(Stat)(STATSTG *pstatstg, DWORD grfStatFlag); - STDMETHOD(Clone)(IStream** stream) { - DCHECK(false) << __FUNCTION__; - return E_NOTIMPL; - } - private: scoped_refptr<net::UploadData> upload_data_; scoped_ptr<net::UploadDataStream> request_body_stream_; diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc index c2ac15d..364f01d 100644 --- a/chrome_frame/urlmon_url_request.cc +++ b/chrome_frame/urlmon_url_request.cc @@ -41,21 +41,7 @@ bool UrlmonUrlRequest::Start() { // StartAsyncDownload if BindToStorage finishes synchronously with an error. // Grab a reference to protect against this. scoped_refptr<UrlmonUrlRequest> ref(this); - HRESULT hr = E_UNEXPECTED; - if (request_data_) { - DCHECK(bind_context_ == NULL); - hr = CreateAsyncBindCtxEx(NULL, 0, this, NULL, bind_context_.Receive(), 0); - DCHECK(SUCCEEDED(hr)); - CComObject<SimpleBindingImpl>* binding = NULL; - CComObject<SimpleBindingImpl>::CreateInstance(&binding); - binding->AddRef(); - hr = MonikerPatch::BindToStorageFromCache(bind_context_, NULL, - request_data_, binding, NULL); - binding->Release(); - } else { - hr = StartAsyncDownload(); - } - + HRESULT hr = StartAsyncDownload(); if (FAILED(hr) && status_.get_state() != UrlmonUrlRequest::Status::DONE) { status_.Done(); status_.set_result(URLRequestStatus::FAILED, HresultToNetError(hr)); diff --git a/chrome_frame/urlmon_url_request_private.h b/chrome_frame/urlmon_url_request_private.h index 239deb5..4dc973f 100644 --- a/chrome_frame/urlmon_url_request_private.h +++ b/chrome_frame/urlmon_url_request_private.h @@ -245,7 +245,6 @@ class UrlmonUrlRequest ScopedComPtr<IBinding> binding_; ScopedComPtr<IMoniker> moniker_; ScopedComPtr<IBindCtx> bind_context_; - scoped_refptr<RequestData> request_data_; Cache cached_data_; size_t pending_read_size_; PlatformThreadId thread_; diff --git a/chrome_frame/utils.h b/chrome_frame/utils.h index bf8a33ec..ad5a335 100644 --- a/chrome_frame/utils.h +++ b/chrome_frame/utils.h @@ -31,7 +31,7 @@ extern const wchar_t kChromeMimeType[]; typedef enum ProtocolPatchMethod { PATCH_METHOD_IBROWSER = 0, PATCH_METHOD_INET_PROTOCOL, // 1 - PATCH_METHOD_IBROWSER_AND_MONIKER, // 2 + PATCH_METHOD_MONIKER, // 2 }; // A REG_DWORD config value that maps to the ProtocolPatchMethod enum. @@ -248,10 +248,6 @@ HRESULT DoQueryService(const IID& service_id, IUnknown* unk, T** service) { return hr; } -// Get url (display name) from a moniker, |bind_context| is optional -HRESULT GetUrlFromMoniker(IMoniker* moniker, IBindCtx* bind_context, - std::wstring* url); - // Navigates an IWebBrowser2 object to a moniker. // |headers| can be NULL. HRESULT NavigateBrowserToMoniker(IUnknown* browser, IMoniker* moniker, |