OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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/bho.h" |
| 6 |
| 7 #include <shlguid.h> |
| 8 #include <shobjidl.h> |
| 9 |
| 10 #include "base/logging.h" |
| 11 #include "base/registry.h" |
| 12 #include "base/scoped_bstr_win.h" |
| 13 #include "base/scoped_comptr_win.h" |
| 14 #include "base/scoped_variant_win.h" |
| 15 #include "base/string_util.h" |
| 16 #include "chrome_tab.h" // NOLINT |
| 17 #include "chrome_frame/protocol_sink_wrap.h" |
| 18 #include "chrome_frame/utils.h" |
| 19 #include "chrome_frame/vtable_patch_manager.h" |
| 20 |
| 21 const wchar_t kUrlMonDllName[] = L"urlmon.dll"; |
| 22 const wchar_t kPatchProtocols[] = L"PatchProtocols"; |
| 23 static const int kIBrowserServiceOnHttpEquivIndex = 30; |
| 24 |
| 25 PatchHelper g_patch_helper; |
| 26 |
| 27 BEGIN_VTABLE_PATCHES(IBrowserService) |
| 28 VTABLE_PATCH_ENTRY(kIBrowserServiceOnHttpEquivIndex, Bho::OnHttpEquiv) |
| 29 END_VTABLE_PATCHES() |
| 30 |
| 31 _ATL_FUNC_INFO Bho::kBeforeNavigate2Info = { |
| 32 CC_STDCALL, VT_EMPTY, 7, { |
| 33 VT_DISPATCH, |
| 34 VT_VARIANT | VT_BYREF, |
| 35 VT_VARIANT | VT_BYREF, |
| 36 VT_VARIANT | VT_BYREF, |
| 37 VT_VARIANT | VT_BYREF, |
| 38 VT_VARIANT | VT_BYREF, |
| 39 VT_BOOL | VT_BYREF |
| 40 } |
| 41 }; |
| 42 |
| 43 Bho::Bho() { |
| 44 } |
| 45 |
| 46 STDMETHODIMP Bho::SetSite(IUnknown* site) { |
| 47 HRESULT hr = S_OK; |
| 48 if (site) { |
| 49 ScopedComPtr<IWebBrowser2> web_browser2; |
| 50 web_browser2.QueryFrom(site); |
| 51 if (web_browser2) { |
| 52 hr = DispEventAdvise(web_browser2, &DIID_DWebBrowserEvents2); |
| 53 DCHECK(SUCCEEDED(hr)) << "DispEventAdvise failed. Error: " << hr; |
| 54 } |
| 55 |
| 56 if (g_patch_helper.state() == PatchHelper::PATCH_IBROWSER) { |
| 57 ScopedComPtr<IBrowserService> browser_service; |
| 58 hr = DoQueryService(SID_SShellBrowser, site, browser_service.Receive()); |
| 59 DCHECK(browser_service) << "DoQueryService - SID_SShellBrowser failed." |
| 60 << " Site: " << site << " Error: " << hr; |
| 61 if (browser_service) { |
| 62 g_patch_helper.PatchBrowserService(browser_service); |
| 63 DCHECK(SUCCEEDED(hr)) << "vtable_patch::PatchInterfaceMethods failed." |
| 64 << " Site: " << site << " Error: " << hr; |
| 65 } |
| 66 } |
| 67 } |
| 68 |
| 69 return IObjectWithSiteImpl<Bho>::SetSite(site); |
| 70 } |
| 71 |
| 72 STDMETHODIMP Bho::BeforeNavigate2(IDispatch* dispatch, VARIANT* url, |
| 73 VARIANT* flags, VARIANT* target_frame_name, VARIANT* post_data, |
| 74 VARIANT* headers, VARIANT_BOOL* cancel) { |
| 75 ScopedComPtr<IWebBrowser2> web_browser2; |
| 76 if (dispatch) |
| 77 web_browser2.QueryFrom(dispatch); |
| 78 |
| 79 if (!web_browser2) { |
| 80 NOTREACHED() << "Can't find WebBrowser2 with given dispatch"; |
| 81 return S_OK; // Return success, we operate on best effort basis. |
| 82 } |
| 83 |
| 84 DLOG(INFO) << "BeforeNavigate2: " << url->bstrVal; |
| 85 |
| 86 if (g_patch_helper.state() == PatchHelper::PATCH_IBROWSER) { |
| 87 VARIANT_BOOL is_top_level = VARIANT_FALSE; |
| 88 web_browser2->get_TopLevelContainer(&is_top_level); |
| 89 |
| 90 std::wstring current_url; |
| 91 bool is_chrome_protocol = false; |
| 92 if (is_top_level && IsValidUrlScheme(url->bstrVal)) { |
| 93 current_url.assign(url->bstrVal, SysStringLen(url->bstrVal)); |
| 94 is_chrome_protocol = StartsWith(current_url, kChromeProtocolPrefix, |
| 95 false); |
| 96 |
| 97 if (!is_chrome_protocol && IsOptInUrl(current_url.c_str())) { |
| 98 DLOG(INFO) << "Canceling navigation and switching to cf"; |
| 99 // Cancel original navigation |
| 100 *cancel = VARIANT_TRUE; |
| 101 |
| 102 // Issue new request with 'cf:' |
| 103 current_url.insert(0, kChromeProtocolPrefix); |
| 104 ScopedVariant new_url(current_url.c_str()); |
| 105 HRESULT hr = web_browser2->Navigate2(new_url.AsInput(), flags, |
| 106 target_frame_name, post_data, |
| 107 headers); |
| 108 DCHECK(SUCCEEDED(hr)) << "web_browser2->Navigate2 failed. Error: " << hr |
| 109 << std::endl << "Url: " << current_url |
| 110 << std::endl << "flags: " << flags |
| 111 << std::endl << "post data: " << post_data |
| 112 << std::endl << "headers: " << headers; |
| 113 } |
| 114 } |
| 115 } |
| 116 return S_OK; |
| 117 } |
| 118 |
| 119 HRESULT Bho::FinalConstruct() { |
| 120 return S_OK; |
| 121 } |
| 122 |
| 123 void Bho::FinalRelease() { |
| 124 } |
| 125 |
| 126 HRESULT STDMETHODCALLTYPE Bho::OnHttpEquiv( |
| 127 IBrowserService_OnHttpEquiv_Fn original_httpequiv, |
| 128 IBrowserService* browser, IShellView* shell_view, BOOL done, |
| 129 VARIANT* in_arg, VARIANT* out_arg) { |
| 130 if (!done && in_arg && (VT_BSTR == V_VT(in_arg))) { |
| 131 if (StrStrI(V_BSTR(in_arg), kChromeContentPrefix)) { |
| 132 // OnHttpEquiv is invoked for meta tags within sub frames as well. |
| 133 // We want to switch renderers only for the top level frame. Since |
| 134 // the same |browser| and |shell_view| are passed in to OnHttpEquiv |
| 135 // even for sub iframes, we determine if this is the top one by |
| 136 // checking if there are any sub frames created or not. |
| 137 ScopedComPtr<IWebBrowser2> web_browser2; |
| 138 DoQueryService(SID_SWebBrowserApp, browser, web_browser2.Receive()); |
| 139 if (web_browser2 && !HasSubFrames(web_browser2)) |
| 140 SwitchRenderer(web_browser2, browser, shell_view, V_BSTR(in_arg)); |
| 141 } |
| 142 } |
| 143 |
| 144 return original_httpequiv(browser, shell_view, done, in_arg, out_arg); |
| 145 } |
| 146 |
| 147 bool Bho::HasSubFrames(IWebBrowser2* web_browser2) { |
| 148 bool has_sub_frames = false; |
| 149 ScopedComPtr<IDispatch> doc_dispatch; |
| 150 if (web_browser2) { |
| 151 HRESULT hr = web_browser2->get_Document(doc_dispatch.Receive()); |
| 152 DCHECK(SUCCEEDED(hr) && doc_dispatch) << |
| 153 "web_browser2->get_Document failed. Error: " << hr; |
| 154 ScopedComPtr<IOleContainer> container; |
| 155 if (SUCCEEDED(hr) && doc_dispatch) { |
| 156 container.QueryFrom(doc_dispatch); |
| 157 ScopedComPtr<IEnumUnknown> enumerator; |
| 158 if (container) { |
| 159 container->EnumObjects(OLECONTF_EMBEDDINGS, enumerator.Receive()); |
| 160 ScopedComPtr<IUnknown> unk; |
| 161 ULONG items_retrieved = 0; |
| 162 if (enumerator) |
| 163 enumerator->Next(1, unk.Receive(), &items_retrieved); |
| 164 has_sub_frames = (items_retrieved != 0); |
| 165 } |
| 166 } |
| 167 } |
| 168 |
| 169 return has_sub_frames; |
| 170 } |
| 171 |
| 172 HRESULT Bho::SwitchRenderer(IWebBrowser2* web_browser2, |
| 173 IBrowserService* browser, IShellView* shell_view, |
| 174 const wchar_t* meta_tag) { |
| 175 DCHECK(web_browser2 && browser && shell_view && meta_tag); |
| 176 |
| 177 // Get access to the mshtml instance and the moniker |
| 178 ScopedComPtr<IOleObject> mshtml_ole_object; |
| 179 HRESULT hr = shell_view->GetItemObject(SVGIO_BACKGROUND, IID_IOleObject, |
| 180 reinterpret_cast<void**>(mshtml_ole_object.Receive())); |
| 181 if (!mshtml_ole_object) { |
| 182 NOTREACHED() << "shell_view->GetItemObject failed. Error: " << hr; |
| 183 return hr; |
| 184 } |
| 185 |
| 186 std::wstring url; |
| 187 ScopedComPtr<IMoniker> moniker; |
| 188 hr = mshtml_ole_object->GetMoniker(OLEGETMONIKER_ONLYIFTHERE, |
| 189 OLEWHICHMK_OBJFULL, moniker.Receive()); |
| 190 DCHECK(moniker) << "mshtml_ole_object->GetMoniker failed. Error: " << hr; |
| 191 |
| 192 if (moniker) |
| 193 hr = GetUrlFromMoniker(moniker, NULL, &url); |
| 194 |
| 195 DCHECK(!url.empty()) << "GetUrlFromMoniker failed. Error: " << hr; |
| 196 DCHECK(!StartsWith(url, kChromeProtocolPrefix, false)); |
| 197 |
| 198 if (!url.empty()) { |
| 199 url.insert(0, kChromeProtocolPrefix); |
| 200 // Navigate to new url |
| 201 VARIANT empty = ScopedVariant::kEmptyVariant; |
| 202 VARIANT flags = { VT_I4 }; |
| 203 V_I4(&flags) = 0; |
| 204 ScopedVariant url_var(url.c_str()); |
| 205 hr = web_browser2->Navigate2(url_var.AsInput(), &flags, &empty, &empty, |
| 206 &empty); |
| 207 DCHECK(SUCCEEDED(hr)) << "web_browser2->Navigate2 failed. Error: " << hr |
| 208 << std::endl << "Url: " << url; |
| 209 } |
| 210 |
| 211 return S_OK; |
| 212 } |
| 213 |
| 214 void PatchHelper::InitializeAndPatchProtocolsIfNeeded() { |
| 215 if (state_ != UNKNOWN) |
| 216 return; |
| 217 |
| 218 bool patch_protocol = GetConfigBool(true, kPatchProtocols); |
| 219 if (patch_protocol) { |
| 220 ProtocolSinkWrap::PatchProtocolHandler(kUrlMonDllName, CLSID_HttpProtocol); |
| 221 ProtocolSinkWrap::PatchProtocolHandler(kUrlMonDllName, CLSID_HttpSProtocol); |
| 222 state_ = PATCH_PROTOCOL; |
| 223 } else { |
| 224 state_ = PATCH_IBROWSER; |
| 225 } |
| 226 } |
| 227 |
| 228 void PatchHelper::PatchBrowserService(IBrowserService* browser_service) { |
| 229 DCHECK(state_ == PATCH_IBROWSER); |
| 230 state_ = PATCH_IBROWSER_OK; |
| 231 vtable_patch::PatchInterfaceMethods(browser_service, |
| 232 IBrowserService_PatchInfo); |
| 233 } |
| 234 |
| 235 extern vtable_patch::MethodPatchInfo IInternetProtocol_PatchInfo[]; |
| 236 extern vtable_patch::MethodPatchInfo IInternetProtocolEx_PatchInfo[]; |
| 237 void PatchHelper::UnpatchIfNeeded() { |
| 238 if (state_ == PATCH_PROTOCOL) { |
| 239 vtable_patch::UnpatchInterfaceMethods(IInternetProtocol_PatchInfo); |
| 240 vtable_patch::UnpatchInterfaceMethods(IInternetProtocolEx_PatchInfo); |
| 241 } else if (state_ == PATCH_IBROWSER_OK) { |
| 242 vtable_patch::UnpatchInterfaceMethods(IBrowserService_PatchInfo); |
| 243 } |
| 244 |
| 245 state_ = UNKNOWN; |
| 246 } |
| 247 |
OLD | NEW |