| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome_frame/urlmon_moniker.h" | |
| 6 | |
| 7 #include <exdisp.h> | |
| 8 #include <shlguid.h> | |
| 9 | |
| 10 #include "base/strings/string_util.h" | |
| 11 #include "base/strings/stringprintf.h" | |
| 12 #include "base/strings/utf_string_conversions.h" | |
| 13 #include "chrome_frame/bho.h" | |
| 14 #include "chrome_frame/bind_context_info.h" | |
| 15 #include "chrome_frame/chrome_active_document.h" | |
| 16 #include "chrome_frame/exception_barrier.h" | |
| 17 #include "chrome_frame/urlmon_bind_status_callback.h" | |
| 18 #include "chrome_frame/utils.h" | |
| 19 #include "chrome_frame/vtable_patch_manager.h" | |
| 20 #include "net/http/http_util.h" | |
| 21 | |
| 22 static const int kMonikerBindToObject = 8; | |
| 23 static const int kMonikerBindToStorage = kMonikerBindToObject + 1; | |
| 24 | |
| 25 base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > | |
| 26 NavigationManager::thread_singleton_ = LAZY_INSTANCE_INITIALIZER; | |
| 27 | |
| 28 BEGIN_VTABLE_PATCHES(IMoniker) | |
| 29 VTABLE_PATCH_ENTRY(kMonikerBindToObject, MonikerPatch::BindToObject) | |
| 30 VTABLE_PATCH_ENTRY(kMonikerBindToStorage, MonikerPatch::BindToStorage) | |
| 31 END_VTABLE_PATCHES() | |
| 32 | |
| 33 //////////////////////////// | |
| 34 | |
| 35 HRESULT NavigationManager::NavigateToCurrentUrlInCF(IBrowserService* browser) { | |
| 36 DCHECK(browser); | |
| 37 DVLOG(1) << __FUNCTION__ << " " << url(); | |
| 38 | |
| 39 MarkBrowserOnThreadForCFNavigation(browser); | |
| 40 | |
| 41 HRESULT hr = S_OK; | |
| 42 base::win::ScopedComPtr<IShellBrowser> shell_browser; | |
| 43 base::win::ScopedComPtr<IBindCtx> bind_context; | |
| 44 hr = ::CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, bind_context.Receive(), 0); | |
| 45 | |
| 46 base::win::ScopedComPtr<IMoniker> moniker; | |
| 47 DCHECK(bind_context); | |
| 48 if (SUCCEEDED(hr) && | |
| 49 SUCCEEDED(hr = ::CreateURLMonikerEx(NULL, url_.c_str(), moniker.Receive(), | |
| 50 URL_MK_UNIFORM))) { | |
| 51 if (SUCCEEDED(hr)) { | |
| 52 // If there's a referrer, preserve it. | |
| 53 std::wstring headers; | |
| 54 if (!referrer_.empty()) { | |
| 55 headers = base::StringPrintf(L"Referer: %ls\r\n\r\n", | |
| 56 base::ASCIIToWide(referrer_).c_str()); | |
| 57 } | |
| 58 | |
| 59 // Pass in URL fragments if applicable. | |
| 60 std::wstring fragment; | |
| 61 GURL parsed_moniker_url(url_); | |
| 62 if (parsed_moniker_url.has_ref()) { | |
| 63 fragment = base::UTF8ToWide(parsed_moniker_url.ref()); | |
| 64 } | |
| 65 | |
| 66 VARIANT flags = { VT_I4 }; | |
| 67 V_VT(&flags) = navNoHistory | navOpenInNewWindow; | |
| 68 | |
| 69 hr = NavigateBrowserToMoniker(browser, moniker, headers.c_str(), | |
| 70 bind_context, fragment.c_str(), NULL, &flags); | |
| 71 DVLOG(1) << base::StringPrintf("NavigateBrowserToMoniker: 0x%08X", hr); | |
| 72 } | |
| 73 } | |
| 74 | |
| 75 return hr; | |
| 76 } | |
| 77 | |
| 78 bool NavigationManager::IsTopLevelUrl(const wchar_t* url) { | |
| 79 return CompareUrlsWithoutFragment(url_.c_str(), url); | |
| 80 } | |
| 81 | |
| 82 ///////////////////////////////////////// | |
| 83 | |
| 84 NavigationManager* NavigationManager::GetThreadInstance() { | |
| 85 return thread_singleton_.Pointer()->Get(); | |
| 86 } | |
| 87 | |
| 88 void NavigationManager::RegisterThreadInstance() { | |
| 89 DCHECK(GetThreadInstance() == NULL); | |
| 90 thread_singleton_.Pointer()->Set(this); | |
| 91 } | |
| 92 | |
| 93 void NavigationManager::UnregisterThreadInstance() { | |
| 94 DCHECK(GetThreadInstance() == this); | |
| 95 thread_singleton_.Pointer()->Set(NULL); | |
| 96 } | |
| 97 | |
| 98 ///////////////////////////////////////// | |
| 99 | |
| 100 // static | |
| 101 bool MonikerPatch::Initialize() { | |
| 102 if (IS_PATCHED(IMoniker)) { | |
| 103 DLOG(WARNING) << __FUNCTION__ << " called more than once."; | |
| 104 return true; | |
| 105 } | |
| 106 | |
| 107 base::win::ScopedComPtr<IMoniker> moniker; | |
| 108 HRESULT hr = ::CreateURLMoniker(NULL, L"http://localhost/", | |
| 109 moniker.Receive()); | |
| 110 DCHECK(SUCCEEDED(hr)); | |
| 111 if (SUCCEEDED(hr)) { | |
| 112 hr = vtable_patch::PatchInterfaceMethods(moniker, IMoniker_PatchInfo); | |
| 113 DLOG_IF(ERROR, FAILED(hr)) << base::StringPrintf( | |
| 114 "patch failed 0x%08X", hr); | |
| 115 } | |
| 116 | |
| 117 return SUCCEEDED(hr); | |
| 118 } | |
| 119 | |
| 120 // static | |
| 121 void MonikerPatch::Uninitialize() { | |
| 122 vtable_patch::UnpatchInterfaceMethods(IMoniker_PatchInfo); | |
| 123 } | |
| 124 | |
| 125 bool ShouldWrapCallback(IMoniker* moniker, REFIID iid, IBindCtx* bind_context) { | |
| 126 CComHeapPtr<WCHAR> url; | |
| 127 HRESULT hr = moniker->GetDisplayName(bind_context, NULL, &url); | |
| 128 if (!url) { | |
| 129 DVLOG(1) << __FUNCTION__ | |
| 130 << base::StringPrintf(" GetDisplayName failed. Error: 0x%x", hr); | |
| 131 return false; | |
| 132 } | |
| 133 | |
| 134 if (!IsEqualIID(IID_IStream, iid)) { | |
| 135 DVLOG(1) << __FUNCTION__ << " Url: " << url | |
| 136 << " Not wrapping: IID is not IStream."; | |
| 137 return false; | |
| 138 } | |
| 139 | |
| 140 base::win::ScopedComPtr<BindContextInfo> info; | |
| 141 BindContextInfo::FromBindContext(bind_context, info.Receive()); | |
| 142 DCHECK(info); | |
| 143 if (info && info->chrome_request()) { | |
| 144 DVLOG(1) << __FUNCTION__ << " Url: " << url | |
| 145 << " Not wrapping: request from chrome frame."; | |
| 146 return false; | |
| 147 } | |
| 148 | |
| 149 NavigationManager* mgr = NavigationManager::GetThreadInstance(); | |
| 150 if (!mgr) { | |
| 151 DVLOG(1) << __FUNCTION__ << " Url: " << url | |
| 152 << " No navigation manager to wrap"; | |
| 153 return false; | |
| 154 } | |
| 155 | |
| 156 // Check whether request comes from MSHTML by checking for IInternetBindInfo. | |
| 157 // We prefer to avoid wrapping if BindToStorage is called from AcroPDF.dll | |
| 158 // (as a result of OnObjectAvailable) | |
| 159 base::win::ScopedComPtr<IUnknown> bscb_holder; | |
| 160 if (S_OK == bind_context->GetObjectParam(L"_BSCB_Holder_", | |
| 161 bscb_holder.Receive())) { | |
| 162 base::win::ScopedComPtr<IBindStatusCallback> bscb; | |
| 163 if (S_OK != DoQueryService(IID_IBindStatusCallback, bscb_holder, | |
| 164 bscb.Receive())) | |
| 165 return false; | |
| 166 | |
| 167 if (!bscb.get()) | |
| 168 return false; | |
| 169 | |
| 170 base::win::ScopedComPtr<IInternetBindInfo> bind_info; | |
| 171 if (S_OK != bind_info.QueryFrom(bscb)) | |
| 172 return false; | |
| 173 } | |
| 174 | |
| 175 // TODO(ananta) | |
| 176 // Use the IsSubFrameRequest function to determine if a request is a top | |
| 177 // level request. Something like this. | |
| 178 // base::win::ScopedComPtr<IUnknown> bscb_holder; | |
| 179 // bind_context->GetObjectParam(L"_BSCB_Holder_", bscb_holder.Receive()); | |
| 180 // if (bscb_holder) { | |
| 181 // base::win::ScopedComPtr<IHttpNegotiate> http_negotiate; | |
| 182 // http_negotiate.QueryFrom(bscb_holder); | |
| 183 // if (http_negotiate && !IsSubFrameRequest(http_negotiate)) | |
| 184 // return true; | |
| 185 // } | |
| 186 // There are some cases where the IsSubFrameRequest function can return | |
| 187 // incorrect results. | |
| 188 bool should_wrap = mgr->IsTopLevelUrl(url); | |
| 189 if (!should_wrap) { | |
| 190 DVLOG(1) << __FUNCTION__ << " Url: " << url | |
| 191 << " Not wrapping: Not top level url."; | |
| 192 } | |
| 193 return should_wrap; | |
| 194 } | |
| 195 | |
| 196 // static | |
| 197 HRESULT MonikerPatch::BindToObject(IMoniker_BindToObject_Fn original, | |
| 198 IMoniker* me, IBindCtx* bind_ctx, | |
| 199 IMoniker* to_left, REFIID iid, void** obj) { | |
| 200 DVLOG(1) << __FUNCTION__; | |
| 201 DCHECK(to_left == NULL); | |
| 202 | |
| 203 ExceptionBarrierReportOnlyModule barrier; | |
| 204 | |
| 205 HRESULT hr = S_OK; | |
| 206 // Bind context is marked for switch when we sniff data in BSCBStorageBind | |
| 207 // and determine that the renderer to be used is Chrome. | |
| 208 base::win::ScopedComPtr<BindContextInfo> info; | |
| 209 BindContextInfo::FromBindContext(bind_ctx, info.Receive()); | |
| 210 DCHECK(info); | |
| 211 if (info) { | |
| 212 if (info->is_switching()) { | |
| 213 // We could implement the BindToObject ourselves here but instead we | |
| 214 // simply register Chrome Frame ActiveDoc as a handler for 'text/html' | |
| 215 // in this bind context. This makes urlmon instantiate CF Active doc | |
| 216 // instead of mshtml. | |
| 217 const char* media_types[] = { "text/html" }; | |
| 218 CLSID classes[] = { CLSID_ChromeActiveDocument }; | |
| 219 hr = RegisterMediaTypeClass(bind_ctx, arraysize(media_types), media_types, | |
| 220 classes, 0); | |
| 221 } else { | |
| 222 // In case the binding begins with BindToObject we do not need | |
| 223 // to cache the data in the sniffing code. | |
| 224 info->set_no_cache(true); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 hr = original(me, bind_ctx, to_left, iid, obj); | |
| 229 return hr; | |
| 230 } | |
| 231 | |
| 232 // static | |
| 233 HRESULT MonikerPatch::BindToStorage(IMoniker_BindToStorage_Fn original, | |
| 234 IMoniker* me, IBindCtx* bind_ctx, | |
| 235 IMoniker* to_left, REFIID iid, void** obj) { | |
| 236 DCHECK(to_left == NULL); | |
| 237 | |
| 238 // Report a crash if the crash is in our own module. | |
| 239 ExceptionBarrierReportOnlyModule barrier; | |
| 240 | |
| 241 HRESULT hr = S_OK; | |
| 242 scoped_refptr<BSCBStorageBind> auto_release_callback; | |
| 243 CComObject<BSCBStorageBind>* callback = NULL; | |
| 244 if (ShouldWrapCallback(me, iid, bind_ctx)) { | |
| 245 hr = CComObject<BSCBStorageBind>::CreateInstance(&callback); | |
| 246 DCHECK(SUCCEEDED(hr)); | |
| 247 auto_release_callback = callback; | |
| 248 DCHECK_EQ(callback->m_dwRef, 1); | |
| 249 hr = callback->Initialize(me, bind_ctx); | |
| 250 DCHECK(SUCCEEDED(hr)); | |
| 251 } | |
| 252 | |
| 253 hr = original(me, bind_ctx, to_left, iid, obj); | |
| 254 | |
| 255 // If the binding terminates before the data could be played back | |
| 256 // now is the chance. Sometimes OnStopBinding happens after this returns | |
| 257 // and then it's too late. | |
| 258 if ((S_OK == hr) && callback) | |
| 259 callback->MayPlayBack(BSCF_LASTDATANOTIFICATION); | |
| 260 | |
| 261 return hr; | |
| 262 } | |
| 263 | |
| OLD | NEW |