diff options
Diffstat (limited to 'chrome_frame/urlmon_moniker.cc')
-rw-r--r-- | chrome_frame/urlmon_moniker.cc | 525 |
1 files changed, 94 insertions, 431 deletions
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 |