| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome_frame/urlmon_moniker.h" | 5 #include "chrome_frame/urlmon_moniker.h" |
| 6 | 6 |
| 7 #include <shlguid.h> | 7 #include <shlguid.h> |
| 8 | 8 |
| 9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 10 #include "chrome_frame/bho.h" | 10 #include "chrome_frame/bho.h" |
| 11 #include "chrome_frame/chrome_active_document.h" |
| 11 #include "chrome_frame/urlmon_bind_status_callback.h" | 12 #include "chrome_frame/urlmon_bind_status_callback.h" |
| 12 #include "chrome_frame/vtable_patch_manager.h" | 13 #include "chrome_frame/vtable_patch_manager.h" |
| 13 #include "net/http/http_util.h" | 14 #include "net/http/http_util.h" |
| 14 | 15 |
| 15 static const int kMonikerBindToObject = 8; | 16 static const int kMonikerBindToObject = 8; |
| 16 static const int kMonikerBindToStorage = kMonikerBindToObject + 1; | 17 static const int kMonikerBindToStorage = kMonikerBindToObject + 1; |
| 18 static wchar_t kBindContextParamName[] = L"_CHROMEFRAME_PRECREATE_"; |
| 17 | 19 |
| 18 base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > | 20 base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > |
| 19 NavigationManager::thread_singleton_(base::LINKER_INITIALIZED); | 21 NavigationManager::thread_singleton_(base::LINKER_INITIALIZED); |
| 20 | 22 |
| 21 BEGIN_VTABLE_PATCHES(IMoniker) | 23 BEGIN_VTABLE_PATCHES(IMoniker) |
| 22 VTABLE_PATCH_ENTRY(kMonikerBindToObject, MonikerPatch::BindToObject) | 24 VTABLE_PATCH_ENTRY(kMonikerBindToObject, MonikerPatch::BindToObject) |
| 23 VTABLE_PATCH_ENTRY(kMonikerBindToStorage, MonikerPatch::BindToStorage) | 25 VTABLE_PATCH_ENTRY(kMonikerBindToStorage, MonikerPatch::BindToStorage) |
| 24 END_VTABLE_PATCHES() | 26 END_VTABLE_PATCHES() |
| 25 | 27 |
| 26 namespace { | 28 namespace { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 41 break; | 43 break; |
| 42 } | 44 } |
| 43 } | 45 } |
| 44 } | 46 } |
| 45 | 47 |
| 46 return referrer; | 48 return referrer; |
| 47 } | 49 } |
| 48 | 50 |
| 49 } // end namespace | 51 } // end namespace |
| 50 | 52 |
| 51 void ReadStreamCache::RewindCache() const { | |
| 52 DCHECK(cache_); | |
| 53 RewindStream(cache_); | |
| 54 } | |
| 55 | |
| 56 HRESULT ReadStreamCache::WriteToCache(const void* data, ULONG size, | |
| 57 ULONG* written) { | |
| 58 HRESULT hr = S_OK; | |
| 59 if (!cache_) { | |
| 60 hr = ::CreateStreamOnHGlobal(NULL, TRUE, cache_.Receive()); | |
| 61 DCHECK(cache_); | |
| 62 } | |
| 63 | |
| 64 if (SUCCEEDED(hr)) | |
| 65 hr = cache_->Write(data, size, written); | |
| 66 | |
| 67 return hr; | |
| 68 } | |
| 69 | |
| 70 size_t ReadStreamCache::GetCacheSize() const { | |
| 71 size_t ret = 0; | |
| 72 if (cache_) { | |
| 73 STATSTG stg = {0}; | |
| 74 cache_->Stat(&stg, STATFLAG_NONAME); | |
| 75 DCHECK_EQ(stg.cbSize.HighPart, 0); | |
| 76 ret = stg.cbSize.LowPart; | |
| 77 } | |
| 78 | |
| 79 return ret; | |
| 80 } | |
| 81 | |
| 82 HRESULT ReadStreamCache::Read(void* pv, ULONG cb, ULONG* read) { | |
| 83 DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", | |
| 84 ::GetCurrentThreadId()); | |
| 85 DCHECK(delegate_); | |
| 86 DWORD read_bytes = 0; | |
| 87 HRESULT hr = delegate_->Read(pv, cb, &read_bytes); | |
| 88 if (read) | |
| 89 *read = read_bytes; | |
| 90 | |
| 91 // Note that Read() might return E_PENDING so we just check if anything | |
| 92 // was read rather than check hr. | |
| 93 if (read_bytes) { | |
| 94 DWORD written = 0; | |
| 95 WriteToCache(pv, read_bytes, &written); | |
| 96 DCHECK(read_bytes == written); | |
| 97 } | |
| 98 | |
| 99 return hr; | |
| 100 } | |
| 101 | |
| 102 ///////////////////////////// | |
| 103 | |
| 104 HRESULT SimpleBindingImpl::GetBindResult(CLSID* protocol, DWORD* result_code, | |
| 105 LPOLESTR* result, DWORD* reserved) { | |
| 106 DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", | |
| 107 ::GetCurrentThreadId()); | |
| 108 | |
| 109 HRESULT hr = S_OK; | |
| 110 | |
| 111 if (protocol) | |
| 112 *protocol = GUID_NULL; | |
| 113 | |
| 114 if (result) | |
| 115 *result = NULL; | |
| 116 | |
| 117 if (delegate_) | |
| 118 hr = delegate_->GetBindResult(protocol, result_code, result, reserved); | |
| 119 | |
| 120 if (SUCCEEDED(hr) && bind_results_ != S_OK) | |
| 121 *result_code = bind_results_; | |
| 122 | |
| 123 return hr; | |
| 124 } | |
| 125 | |
| 126 HRESULT SimpleBindingImpl::DelegateQI(void* obj, REFIID iid, void** ret, | |
| 127 DWORD cookie) { | |
| 128 SimpleBindingImpl* me = reinterpret_cast<SimpleBindingImpl*>(obj); | |
| 129 HRESULT hr = E_NOINTERFACE; | |
| 130 if (me->delegate_) | |
| 131 hr = me->delegate_.QueryInterface(iid, ret); | |
| 132 DLOG(INFO) << __FUNCTION__ << " " << GuidToString(iid) | |
| 133 << StringPrintf(" 0x%08X", hr); | |
| 134 return hr; | |
| 135 } | |
| 136 | |
| 137 /////////////////////////////// | |
| 138 | |
| 139 void RequestHeaders::OnBeginningTransaction(const wchar_t* url, | |
| 140 const wchar_t* headers, | |
| 141 const wchar_t* additional_headers) { | |
| 142 if (url) | |
| 143 request_url_ = url; | |
| 144 | |
| 145 if (headers) | |
| 146 begin_request_headers_ = headers; | |
| 147 | |
| 148 if (additional_headers) | |
| 149 additional_request_headers_ = additional_headers; | |
| 150 | |
| 151 DLOG(INFO) << __FUNCTION__ << "\n " << request_url_ << "\n " | |
| 152 << begin_request_headers_ << "\n " | |
| 153 << additional_request_headers_; | |
| 154 | |
| 155 NavigationManager* mgr = NavigationManager::GetThreadInstance(); | |
| 156 if (mgr) { | |
| 157 DCHECK(mgr->IsTopLevelUrl(request_url_.c_str())); | |
| 158 mgr->SetActiveRequestHeaders(this); | |
| 159 } | |
| 160 } | |
| 161 | |
| 162 void RequestHeaders::OnResponse(DWORD response_code, | |
| 163 const wchar_t* response_headers, | |
| 164 const wchar_t* request_headers) { | |
| 165 response_code_ = response_code; | |
| 166 | |
| 167 if (response_headers) | |
| 168 response_headers_ = response_headers; | |
| 169 | |
| 170 if (request_headers) | |
| 171 request_headers_ = request_headers; | |
| 172 | |
| 173 #ifndef NDEBUG | |
| 174 NavigationManager* mgr = NavigationManager::GetThreadInstance(); | |
| 175 if (mgr) { | |
| 176 // For some reason we might not have gotten an OnBeginningTransaction call! | |
| 177 // DCHECK(mgr->IsTopLevelUrl(request_url_.c_str())); | |
| 178 // DCHECK(mgr->GetActiveRequestHeaders() == this); | |
| 179 mgr->SetActiveRequestHeaders(this); | |
| 180 } | |
| 181 #endif | |
| 182 } | |
| 183 | |
| 184 std::string RequestHeaders::GetReferrer() { | |
| 185 std::string referrer = FindReferrerFromHeaders(begin_request_headers_.c_str(), | |
| 186 additional_request_headers_.c_str()); | |
| 187 if (referrer.empty()) { | |
| 188 DLOG(INFO) << request_url_ << "\nbheaders: " << begin_request_headers_ | |
| 189 << "\nadd:" << additional_request_headers_ << "\nreq: " << request_heade
rs_; | |
| 190 } | |
| 191 return referrer; | |
| 192 } | |
| 193 | |
| 194 HRESULT RequestHeaders::FireHttpNegotiateEvents(IHttpNegotiate* http) const { | |
| 195 DCHECK(http); | |
| 196 | |
| 197 LPOLESTR additional_headers = NULL; | |
| 198 HRESULT hr = http->BeginningTransaction(request_url_.c_str(), | |
| 199 begin_request_headers_.c_str(), 0, &additional_headers); | |
| 200 DCHECK(SUCCEEDED(hr)); | |
| 201 ::CoTaskMemFree(additional_headers); | |
| 202 | |
| 203 // Also notify the navigation manager on this thread if one exists. | |
| 204 NavigationManager* mgr = NavigationManager::GetThreadInstance(); | |
| 205 if (mgr) { | |
| 206 mgr->OnBeginningTransaction(true, request_url_.c_str(), | |
| 207 begin_request_headers_.c_str(), additional_request_headers_.c_str()); | |
| 208 } | |
| 209 | |
| 210 additional_headers = NULL; | |
| 211 hr = http->OnResponse(response_code_, response_headers_.c_str(), | |
| 212 request_headers_.c_str(), &additional_headers); | |
| 213 DCHECK(SUCCEEDED(hr)); | |
| 214 if (additional_headers) { | |
| 215 ::CoTaskMemFree(additional_headers); | |
| 216 additional_headers = NULL; | |
| 217 } | |
| 218 | |
| 219 return hr; | |
| 220 } | |
| 221 | |
| 222 /////////////////////////////// | |
| 223 | |
| 224 RequestData::RequestData() { | |
| 225 ZeroMemory(&format_, sizeof(format_)); | |
| 226 format_.dwAspect = 1; | |
| 227 format_.lindex = -1; | |
| 228 format_.tymed = TYMED_ISTREAM; | |
| 229 CComObject<ReadStreamCache>* stream = NULL; | |
| 230 CComObject<ReadStreamCache>::CreateInstance(&stream); | |
| 231 DCHECK(stream); | |
| 232 stream_delegate_ = stream; | |
| 233 DCHECK_EQ(stream->m_dwRef, 1); | |
| 234 } | |
| 235 | |
| 236 RequestData::~RequestData() { | |
| 237 } | |
| 238 | |
| 239 void RequestData::Initialize(RequestHeaders* headers) { | |
| 240 if (headers) { | |
| 241 headers_ = headers; | |
| 242 } else { | |
| 243 headers_ = new RequestHeaders(); | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 HRESULT RequestData::DelegateDataRead(IBindStatusCallback* callback, | |
| 248 DWORD flags, DWORD size, | |
| 249 FORMATETC* format, STGMEDIUM* storage, | |
| 250 size_t* bytes_read) { | |
| 251 DCHECK(callback); | |
| 252 DCHECK(storage); | |
| 253 if ((flags & BSCF_FIRSTDATANOTIFICATION) && format) { | |
| 254 format_ = *format; | |
| 255 } | |
| 256 | |
| 257 IStream* original = storage->pstm; | |
| 258 DCHECK(original); | |
| 259 if (!original) | |
| 260 return E_POINTER; | |
| 261 | |
| 262 size_t size_before = stream_delegate_->GetCacheSize(); | |
| 263 stream_delegate_->SetDelegate(storage->pstm); | |
| 264 storage->pstm = stream_delegate_; | |
| 265 HRESULT hr = callback->OnDataAvailable(flags, size, format, storage); | |
| 266 storage->pstm = original; | |
| 267 if (bytes_read) | |
| 268 *bytes_read = stream_delegate_->GetCacheSize() - size_before; | |
| 269 return hr; | |
| 270 } | |
| 271 | |
| 272 void RequestData::CacheAll(IStream* data) { | |
| 273 DCHECK(data); | |
| 274 char buffer[4096]; | |
| 275 HRESULT hr = S_OK; | |
| 276 while (hr == S_OK) { | |
| 277 DWORD read = 0; | |
| 278 hr = data->Read(buffer, arraysize(buffer), &read); | |
| 279 if (read) { | |
| 280 DWORD written = 0; | |
| 281 stream_delegate_->WriteToCache(buffer, read, &written); | |
| 282 DCHECK(read == written); | |
| 283 } else { | |
| 284 // Just in case Read() completed with S_OK but zero bytes. | |
| 285 break; | |
| 286 } | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 HRESULT RequestData::GetResetCachedContentStream(IStream** clone) { | |
| 291 DCHECK(clone); | |
| 292 IStream* cache = stream_delegate_->cache(); | |
| 293 if (!cache) | |
| 294 return HRESULT_FROM_WIN32(ERROR_EMPTY); | |
| 295 HRESULT hr = cache->Clone(clone); | |
| 296 if (SUCCEEDED(hr)) { | |
| 297 RewindStream(*clone); | |
| 298 } else { | |
| 299 NOTREACHED(); | |
| 300 } | |
| 301 return hr; | |
| 302 } | |
| 303 | 53 |
| 304 //////////////////////////// | 54 //////////////////////////// |
| 305 | 55 |
| 306 HRESULT NavigationManager::NavigateToCurrentUrlInCF(IBrowserService* browser) { | 56 HRESULT NavigationManager::NavigateToCurrentUrlInCF(IBrowserService* browser) { |
| 307 DCHECK(browser); | 57 DCHECK(browser); |
| 308 DLOG(INFO) << __FUNCTION__ << " " << url(); | 58 DLOG(INFO) << __FUNCTION__ << " " << url(); |
| 309 | 59 |
| 310 MarkBrowserOnThreadForCFNavigation(browser); | 60 MarkBrowserOnThreadForCFNavigation(browser); |
| 311 | 61 |
| 62 HRESULT hr = S_OK; |
| 63 ScopedComPtr<IShellBrowser> shell_browser; |
| 312 ScopedComPtr<IBindCtx> bind_context; | 64 ScopedComPtr<IBindCtx> bind_context; |
| 65 hr = ::CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, bind_context.Receive(), 0); |
| 66 |
| 313 ScopedComPtr<IMoniker> moniker; | 67 ScopedComPtr<IMoniker> moniker; |
| 314 HRESULT hr = ::CreateBindCtx(0, bind_context.Receive()); | |
| 315 DCHECK(bind_context); | 68 DCHECK(bind_context); |
| 316 if (SUCCEEDED(hr) && | 69 if (SUCCEEDED(hr) && |
| 317 SUCCEEDED(hr = ::CreateURLMonikerEx(NULL, url_.c_str(), moniker.Receive(), | 70 SUCCEEDED(hr = ::CreateURLMonikerEx(NULL, url_.c_str(), moniker.Receive(), |
| 318 URL_MK_UNIFORM))) { | 71 URL_MK_UNIFORM))) { |
| 319 if (SUCCEEDED(hr)) { | 72 if (SUCCEEDED(hr)) { |
| 320 // If there's a referrer, preserve it. | 73 // If there's a referrer, preserve it. |
| 321 std::wstring headers; | 74 std::wstring headers; |
| 322 if (!referrer_.empty()) { | 75 if (!referrer_.empty()) { |
| 323 headers = StringPrintf(L"Referer: %ls\r\n\r\n", | 76 headers = StringPrintf(L"Referer: %ls\r\n\r\n", |
| 324 ASCIIToWide(referrer_).c_str()); | 77 ASCIIToWide(referrer_).c_str()); |
| 325 } | 78 } |
| 326 | 79 |
| 327 // Pass in URL fragments if applicable. | 80 // Pass in URL fragments if applicable. |
| 328 std::wstring fragment; | 81 std::wstring fragment; |
| 329 GURL parsed_moniker_url(url_); | 82 GURL parsed_moniker_url(url_); |
| 330 if (parsed_moniker_url.has_ref()) { | 83 if (parsed_moniker_url.has_ref()) { |
| 331 fragment = UTF8ToWide(parsed_moniker_url.ref()); | 84 fragment = UTF8ToWide(parsed_moniker_url.ref()); |
| 332 set_original_url_with_fragment(url_.c_str()); | 85 set_original_url_with_fragment(url_.c_str()); |
| 333 } | 86 } |
| 334 | 87 |
| 335 hr = NavigateBrowserToMoniker(browser, moniker, headers.c_str(), | 88 hr = NavigateBrowserToMoniker(browser, moniker, headers.c_str(), |
| 336 bind_context, fragment.c_str()); | 89 bind_context, fragment.c_str()); |
| 337 DLOG(INFO) << StringPrintf("NavigateBrowserToMoniker: 0x%08X", hr); | 90 DLOG(INFO) << StringPrintf("NavigateBrowserToMoniker: 0x%08X", hr); |
| 338 } | 91 } |
| 339 } | 92 } |
| 340 | 93 |
| 341 return hr; | 94 return hr; |
| 342 } | 95 } |
| 343 | 96 |
| 344 void NavigationManager::SetActiveRequestData(RequestData* request_data) { | |
| 345 DLOG(INFO) << __FUNCTION__ << StringPrintf(" 0x%08X", request_data); | |
| 346 DCHECK(request_data_ == NULL || request_data == NULL); | |
| 347 DLOG_IF(ERROR, url_.empty() && request_data) | |
| 348 << "attempt to cache data after url has been cleared"; | |
| 349 // Clear the current request headers as the request data must have the | |
| 350 // correct set of headers. | |
| 351 DCHECK(request_data == NULL || request_data->headers() != NULL); | |
| 352 request_headers_ = NULL; | |
| 353 | |
| 354 if (request_data == NULL || !url_.empty()) { | |
| 355 request_data_ = request_data; | |
| 356 } | |
| 357 } | |
| 358 | |
| 359 void NavigationManager::OnBeginningTransaction(bool is_top_level, | 97 void NavigationManager::OnBeginningTransaction(bool is_top_level, |
| 360 const wchar_t* url, const wchar_t* headers, | 98 const wchar_t* url, const wchar_t* headers, |
| 361 const wchar_t* additional_headers) { | 99 const wchar_t* additional_headers) { |
| 362 DCHECK(url != NULL); | 100 DCHECK(url != NULL); |
| 363 | 101 |
| 364 // We've seen this happen on the first page load in IE8 (it might also happen | 102 // We've seen this happen on the first page load in IE8 (it might also happen |
| 365 // in other versions of IE) and for some reason we did not get a call to | 103 // in other versions of IE) and for some reason we did not get a call to |
| 366 // BeginningTransaction the first time it happened and this time around we're | 104 // BeginningTransaction the first time it happened and this time around we're |
| 367 // using a cached data object that didn't get the request headers or URL. | 105 // using a cached data object that didn't get the request headers or URL. |
| 368 DLOG_IF(ERROR, lstrlenW(url) == 0) << __FUNCTION__ << "Invalid URL."; | 106 DLOG_IF(ERROR, lstrlenW(url) == 0) << __FUNCTION__ << "Invalid URL."; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 390 void NavigationManager::RegisterThreadInstance() { | 128 void NavigationManager::RegisterThreadInstance() { |
| 391 DCHECK(GetThreadInstance() == NULL); | 129 DCHECK(GetThreadInstance() == NULL); |
| 392 thread_singleton_.Pointer()->Set(this); | 130 thread_singleton_.Pointer()->Set(this); |
| 393 } | 131 } |
| 394 | 132 |
| 395 void NavigationManager::UnregisterThreadInstance() { | 133 void NavigationManager::UnregisterThreadInstance() { |
| 396 DCHECK(GetThreadInstance() == this); | 134 DCHECK(GetThreadInstance() == this); |
| 397 thread_singleton_.Pointer()->Set(NULL); | 135 thread_singleton_.Pointer()->Set(NULL); |
| 398 } | 136 } |
| 399 | 137 |
| 138 // Mark a bind context for navigation by storing a bind context param. |
| 139 HRESULT NavigationManager::AttachCFObject(IBindCtx* bind_context) { |
| 140 if (!bind_context) { |
| 141 NOTREACHED(); |
| 142 return E_INVALIDARG; |
| 143 } |
| 144 |
| 145 CComObject<ChromeActiveDocument>* cf_doc = NULL; |
| 146 HRESULT hr = CComObject<ChromeActiveDocument>::CreateInstance(&cf_doc); |
| 147 if (cf_doc) { |
| 148 hr = bind_context->RegisterObjectParam(kBindContextParamName, |
| 149 static_cast<IPersistMoniker*>(cf_doc)); |
| 150 if (FAILED(hr)) |
| 151 delete cf_doc; |
| 152 } |
| 153 |
| 154 return hr; |
| 155 } |
| 156 |
| 157 HRESULT NavigationManager::DetachCFObject(IMoniker* moniker, |
| 158 IBindCtx* bind_context, |
| 159 IUnknown** object) { |
| 160 if (!bind_context || !moniker) { |
| 161 NOTREACHED(); |
| 162 return E_INVALIDARG; |
| 163 } |
| 164 |
| 165 std::wstring url = GetActualUrlFromMoniker(moniker, bind_context, |
| 166 std::wstring()); |
| 167 NavigationManager* mgr = GetThreadInstance(); |
| 168 if (mgr && !mgr->IsTopLevelUrl(url.c_str())) { |
| 169 DLOG(WARNING) << "attempt to switch-in at non-top level.\n" << |
| 170 "Top level url: " << mgr->url() << "\nUrl being loaded: " << url; |
| 171 return E_FAIL; |
| 172 } |
| 173 |
| 174 ScopedComPtr<IUnknown> cf_doc; |
| 175 HRESULT hr = S_OK; |
| 176 hr = bind_context->GetObjectParam(kBindContextParamName, cf_doc.Receive()); |
| 177 if (cf_doc) { |
| 178 hr = bind_context->RevokeObjectParam(kBindContextParamName); |
| 179 *object = cf_doc.Detach(); |
| 180 } |
| 181 |
| 182 return hr; |
| 183 } |
| 184 |
| 400 ///////////////////////////////////////// | 185 ///////////////////////////////////////// |
| 401 | 186 |
| 402 // static | 187 // static |
| 403 bool MonikerPatch::Initialize() { | 188 bool MonikerPatch::Initialize() { |
| 404 if (IS_PATCHED(IMoniker)) { | 189 if (IS_PATCHED(IMoniker)) { |
| 405 DLOG(WARNING) << __FUNCTION__ << " called more than once."; | 190 DLOG(WARNING) << __FUNCTION__ << " called more than once."; |
| 406 return true; | 191 return true; |
| 407 } | 192 } |
| 408 | 193 |
| 409 ScopedComPtr<IMoniker> moniker; | 194 ScopedComPtr<IMoniker> moniker; |
| 410 HRESULT hr = ::CreateURLMoniker(NULL, L"http://localhost/", | 195 HRESULT hr = ::CreateURLMoniker(NULL, L"http://localhost/", |
| 411 moniker.Receive()); | 196 moniker.Receive()); |
| 412 DCHECK(SUCCEEDED(hr)); | 197 DCHECK(SUCCEEDED(hr)); |
| 413 if (SUCCEEDED(hr)) { | 198 if (SUCCEEDED(hr)) { |
| 414 hr = vtable_patch::PatchInterfaceMethods(moniker, IMoniker_PatchInfo); | 199 hr = vtable_patch::PatchInterfaceMethods(moniker, IMoniker_PatchInfo); |
| 415 DLOG_IF(ERROR, FAILED(hr)) << StringPrintf("patch failed 0x%08X", hr); | 200 DLOG_IF(ERROR, FAILED(hr)) << StringPrintf("patch failed 0x%08X", hr); |
| 416 } | 201 } |
| 417 | 202 |
| 418 return SUCCEEDED(hr); | 203 return SUCCEEDED(hr); |
| 419 } | 204 } |
| 420 | 205 |
| 421 // static | 206 // static |
| 422 void MonikerPatch::Uninitialize() { | 207 void MonikerPatch::Uninitialize() { |
| 423 vtable_patch::UnpatchInterfaceMethods(IMoniker_PatchInfo); | 208 vtable_patch::UnpatchInterfaceMethods(IMoniker_PatchInfo); |
| 424 } | 209 } |
| 425 | 210 |
| 211 bool ShouldWrapCallback(IMoniker* moniker, IBindCtx* bind_context) { |
| 212 NavigationManager* mgr = NavigationManager::GetThreadInstance(); |
| 213 if (!mgr) { |
| 214 DLOG(INFO) << __FUNCTION__ << "No navitation manager to wrap"; |
| 215 return false; |
| 216 } |
| 217 |
| 218 CComHeapPtr<WCHAR> url; |
| 219 HRESULT hr = moniker->GetDisplayName(bind_context, NULL, &url); |
| 220 DCHECK(SUCCEEDED(hr)); |
| 221 bool should_wrap = mgr->IsTopLevelUrl(url); |
| 222 return should_wrap; |
| 223 } |
| 224 |
| 426 // static | 225 // static |
| 427 HRESULT MonikerPatch::BindToObject(IMoniker_BindToObject_Fn original, | 226 HRESULT MonikerPatch::BindToObject(IMoniker_BindToObject_Fn original, |
| 428 IMoniker* me, IBindCtx* bind_ctx, | 227 IMoniker* me, IBindCtx* bind_ctx, |
| 429 IMoniker* to_left, REFIID iid, void** obj) { | 228 IMoniker* to_left, REFIID iid, void** obj) { |
| 430 DLOG(INFO) << __FUNCTION__; | 229 DLOG(INFO) << __FUNCTION__; |
| 431 DCHECK(to_left == NULL); | 230 DCHECK(to_left == NULL); |
| 432 CComHeapPtr<WCHAR> url; | 231 |
| 433 HRESULT hr = me->GetDisplayName(bind_ctx, NULL, &url); | 232 HRESULT hr = S_OK; |
| 434 DCHECK(SUCCEEDED(hr)); | 233 ScopedComPtr<IUnknown> cf_doc; |
| 435 NavigationManager* mgr = NavigationManager::GetThreadInstance(); | 234 NavigationManager::DetachCFObject(me, bind_ctx, cf_doc.Receive()); |
| 436 if (mgr) { | 235 if (cf_doc) { |
| 437 bool interest = mgr ? mgr->IsTopLevelUrl(url) : false; | 236 ScopedComPtr<IPersistMoniker> persist_moniker; |
| 438 if (interest) { | 237 hr = persist_moniker.QueryFrom(cf_doc); |
| 439 scoped_refptr<RequestData> request_data(mgr->GetActiveRequestData(url)); | 238 if (persist_moniker) { |
| 440 if (request_data) { | 239 hr = persist_moniker->Load(TRUE, me, bind_ctx, STGM_READ); |
| 441 DLOG(INFO) << " got cached content"; | 240 if (SUCCEEDED(hr)) { |
| 442 mgr->set_referrer(request_data->headers()->GetReferrer()); | 241 return persist_moniker.QueryInterface(iid, obj); |
| 443 DLOG(INFO) << "referrer is: " << mgr->referrer(); | |
| 444 // Create a new CF document object and initialize it with | |
| 445 // the already cached data. | |
| 446 ScopedComPtr<IUnknown> cf_doc; | |
| 447 hr = cf_doc.CreateInstance(CLSID_ChromeActiveDocument); | |
| 448 if (FAILED(hr)) { | |
| 449 NOTREACHED() << "Failed to cocreate active document. Error:" << hr; | |
| 450 return original(me, bind_ctx, to_left, iid, obj); | |
| 451 } | |
| 452 ScopedComPtr<IPersistMoniker> persist_moniker; | |
| 453 hr = persist_moniker.QueryFrom(cf_doc); | |
| 454 DCHECK(SUCCEEDED(hr)); | |
| 455 hr = persist_moniker->Load(TRUE, me, bind_ctx, STGM_READ); | |
| 456 if (SUCCEEDED(hr)) { | |
| 457 hr = persist_moniker.QueryInterface(iid, obj); | |
| 458 DCHECK(SUCCEEDED(hr)); | |
| 459 } | |
| 460 } else { | 242 } else { |
| 461 DLOG(INFO) << " creating callback object"; | 243 NOTREACHED() << StringPrintf("CF ActiveDoc::Load error: 0x%x", hr); |
| 462 CComObject<CFUrlmonBindStatusCallback>* callback = NULL; | |
| 463 hr = CComObject<CFUrlmonBindStatusCallback>::CreateInstance( | |
| 464 &callback); | |
| 465 callback->AddRef(); | |
| 466 hr = callback->Initialize(bind_ctx, mgr->GetActiveRequestHeaders()); | |
| 467 DCHECK(SUCCEEDED(hr)); | |
| 468 hr = original(me, bind_ctx, to_left, iid, obj); | |
| 469 callback->Release(); | |
| 470 if (SUCCEEDED(hr) && (*obj) == NULL) { | |
| 471 DCHECK(hr == MK_S_ASYNCHRONOUS); | |
| 472 } | |
| 473 } | 244 } |
| 474 } else { | 245 } else { |
| 475 DLOG(INFO) << " -- calling original. (no interest)"; | 246 NOTREACHED() << StringPrintf("CF IPersistMoniker QI failed: 0x%x", hr); |
| 476 hr = original(me, bind_ctx, to_left, iid, obj); | |
| 477 } | 247 } |
| 478 } else { | |
| 479 // We don't have a NavigationManager instance for cases like View Source | |
| 480 // etc. | |
| 481 hr = original(me, bind_ctx, to_left, iid, obj); | |
| 482 } | 248 } |
| 483 | 249 |
| 250 hr = original(me, bind_ctx, to_left, iid, obj); |
| 484 return hr; | 251 return hr; |
| 485 } | 252 } |
| 486 | 253 |
| 487 // static | 254 // static |
| 488 HRESULT MonikerPatch::BindToStorage(IMoniker_BindToStorage_Fn original, | 255 HRESULT MonikerPatch::BindToStorage(IMoniker_BindToStorage_Fn original, |
| 489 IMoniker* me, IBindCtx* bind_ctx, | 256 IMoniker* me, IBindCtx* bind_ctx, |
| 490 IMoniker* to_left, REFIID iid, void** obj) { | 257 IMoniker* to_left, REFIID iid, void** obj) { |
| 491 DLOG(INFO) << __FUNCTION__; | 258 DLOG(INFO) << __FUNCTION__; |
| 492 DCHECK(iid == IID_IStream || iid == IID_IUnknown); | |
| 493 DCHECK(to_left == NULL); | 259 DCHECK(to_left == NULL); |
| 494 | 260 |
| 495 HRESULT hr = E_UNEXPECTED; | 261 HRESULT hr = S_OK; |
| 496 NavigationManager* mgr = NavigationManager::GetThreadInstance(); | 262 CComObject<BSCBStorageBind>* callback = NULL; |
| 497 if (mgr) { | 263 if ((IsEqualIID(IID_IStream, iid)) && ShouldWrapCallback(me, bind_ctx)) { |
| 498 CComHeapPtr<WCHAR> url; | 264 hr = CComObject<BSCBStorageBind>::CreateInstance(&callback); |
| 499 hr = me->GetDisplayName(bind_ctx, NULL, &url); | 265 callback->AddRef(); |
| 266 hr = callback->Initialize(me, bind_ctx); |
| 500 DCHECK(SUCCEEDED(hr)); | 267 DCHECK(SUCCEEDED(hr)); |
| 501 bool interest = mgr->IsTopLevelUrl(url); | |
| 502 DLOG(INFO) << "interest: " << interest << " url " << url; | |
| 503 if (interest) { | |
| 504 scoped_refptr<RequestData> request_data(mgr->GetActiveRequestData(url)); | |
| 505 if (request_data) { | |
| 506 CComObject<SimpleBindingImpl>* binding = NULL; | |
| 507 CComObject<SimpleBindingImpl>::CreateInstance(&binding); | |
| 508 binding->OverrideBindResults(INET_E_TERMINATED_BIND); | |
| 509 binding->AddRef(); | |
| 510 hr = BindToStorageFromCache(bind_ctx, kChromeMimeType, request_data, | |
| 511 binding, reinterpret_cast<IStream**>(obj)); | |
| 512 binding->Release(); | |
| 513 } else { | |
| 514 DLOG(INFO) << " creating callback object"; | |
| 515 CComObject<CFUrlmonBindStatusCallback>* callback = NULL; | |
| 516 hr = CComObject<CFUrlmonBindStatusCallback>::CreateInstance( | |
| 517 &callback); | |
| 518 callback->AddRef(); | |
| 519 hr = callback->Initialize(bind_ctx, mgr->GetActiveRequestHeaders()); | |
| 520 DCHECK(SUCCEEDED(hr)); | |
| 521 hr = original(me, bind_ctx, to_left, iid, obj); | |
| 522 callback->Release(); | |
| 523 if (SUCCEEDED(hr) && (*obj) == NULL) { | |
| 524 DCHECK(hr == MK_S_ASYNCHRONOUS); | |
| 525 } | |
| 526 } | |
| 527 } else { | |
| 528 hr = original(me, bind_ctx, to_left, iid, obj); | |
| 529 } | |
| 530 } else { | |
| 531 // We usually only get here when we're issuing requests from our | |
| 532 // worker thread. However there are some exceptions such as when | |
| 533 // windows media player is issuing requests, so we don't DCHECK. | |
| 534 hr = original(me, bind_ctx, to_left, iid, obj); | |
| 535 } | 268 } |
| 536 | 269 |
| 537 return hr; | 270 hr = original(me, bind_ctx, to_left, iid, obj); |
| 538 } | |
| 539 | 271 |
| 540 HRESULT MonikerPatch::BindToStorageFromCache(IBindCtx* bind_ctx, | 272 // If the binding terminates before the data could be played back |
| 541 const wchar_t* mime_type, | 273 // now is the chance. Sometimes OnStopBinding happens after this returns |
| 542 RequestData* data, | 274 // and then it's too late. |
| 543 SimpleBindingImpl* binding, | 275 if ((S_OK == hr) && callback) |
| 544 IStream** cache_out) { | 276 callback->MayPlayBack(BSCF_LASTDATANOTIFICATION); |
| 545 DCHECK(bind_ctx); | |
| 546 // mime_type may be NULL | |
| 547 DCHECK(data); | |
| 548 DCHECK(binding); | |
| 549 // cache_out may be NULL | |
| 550 | |
| 551 ScopedComPtr<IUnknown> holder; | |
| 552 HRESULT hr = bind_ctx->GetObjectParam(L"_BSCB_Holder_", holder.Receive()); | |
| 553 DCHECK(holder); | |
| 554 ScopedComPtr<IBindStatusCallback> bscb; | |
| 555 ScopedComPtr<IHttpNegotiate> http_negotiate; | |
| 556 DoQueryService(IID_IBindStatusCallback, holder, bscb.Receive()); | |
| 557 DoQueryService(IID_IHttpNegotiate, bscb, http_negotiate.Receive()); | |
| 558 if (!bscb) { | |
| 559 NOTREACHED(); | |
| 560 return E_NOINTERFACE; | |
| 561 } | |
| 562 | |
| 563 hr = bscb->OnStartBinding(0, binding); | |
| 564 DCHECK(SUCCEEDED(hr)); | |
| 565 if (SUCCEEDED(hr)) { | |
| 566 // Fire BeginningTransaction and OnResponse events. | |
| 567 if (http_negotiate) | |
| 568 data->headers()->FireHttpNegotiateEvents(http_negotiate); | |
| 569 | |
| 570 FORMATETC format_etc = data->format(); | |
| 571 if (mime_type) { | |
| 572 // If a non NULL mime type was specified, override the clipboard format. | |
| 573 format_etc.cfFormat = ::RegisterClipboardFormatW(mime_type); | |
| 574 DCHECK(format_etc.cfFormat); | |
| 575 } | |
| 576 | |
| 577 const wchar_t* mime_type_available = mime_type; | |
| 578 wchar_t format_name[MAX_PATH] = {0}; | |
| 579 if (!mime_type_available) { | |
| 580 ::GetClipboardFormatName(format_etc.cfFormat, format_name, | |
| 581 arraysize(format_name)); | |
| 582 mime_type_available = format_name; | |
| 583 } | |
| 584 DCHECK(mime_type_available); | |
| 585 hr = bscb->OnProgress(0, 0, BINDSTATUS_MIMETYPEAVAILABLE, | |
| 586 mime_type_available); | |
| 587 | |
| 588 ScopedComPtr<IStream> cache; | |
| 589 data->GetResetCachedContentStream(cache.Receive()); | |
| 590 | |
| 591 STGMEDIUM medium = {0}; | |
| 592 medium.tymed = TYMED_ISTREAM; | |
| 593 medium.pstm = cache; | |
| 594 DLOG(INFO) << __FUNCTION__ << " total bytes available: " | |
| 595 << data->GetCachedContentSize(); | |
| 596 hr = bscb->OnDataAvailable( | |
| 597 BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | | |
| 598 BSCF_DATAFULLYAVAILABLE, data->GetCachedContentSize(), &format_etc, | |
| 599 &medium); | |
| 600 | |
| 601 // OnDataAvailable might report a failure such as INET_E_TERMINATED_BIND | |
| 602 // when mshtml decides not to handle the request. | |
| 603 // We ignore the return value from OnStopBinding and return whatever | |
| 604 // OnDataAvailable gave us. | |
| 605 if (FAILED(hr)) { | |
| 606 binding->OverrideBindResults(hr); | |
| 607 } else if (cache_out) { | |
| 608 *cache_out = cache.Detach(); | |
| 609 } | |
| 610 | |
| 611 bscb->OnStopBinding(hr, NULL); | |
| 612 } | |
| 613 | 277 |
| 614 return hr; | 278 return hr; |
| 615 } | 279 } |
| 616 | |
| OLD | NEW |