summaryrefslogtreecommitdiffstats
path: root/chrome_frame/urlmon_moniker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome_frame/urlmon_moniker.cc')
-rw-r--r--chrome_frame/urlmon_moniker.cc525
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