| Index: chrome_frame/bho.cc
|
| ===================================================================
|
| --- chrome_frame/bho.cc (revision 0)
|
| +++ chrome_frame/bho.cc (revision 0)
|
| @@ -0,0 +1,247 @@
|
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome_frame/bho.h"
|
| +
|
| +#include <shlguid.h>
|
| +#include <shobjidl.h>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/registry.h"
|
| +#include "base/scoped_bstr_win.h"
|
| +#include "base/scoped_comptr_win.h"
|
| +#include "base/scoped_variant_win.h"
|
| +#include "base/string_util.h"
|
| +#include "chrome_tab.h" // NOLINT
|
| +#include "chrome_frame/protocol_sink_wrap.h"
|
| +#include "chrome_frame/utils.h"
|
| +#include "chrome_frame/vtable_patch_manager.h"
|
| +
|
| +const wchar_t kUrlMonDllName[] = L"urlmon.dll";
|
| +const wchar_t kPatchProtocols[] = L"PatchProtocols";
|
| +static const int kIBrowserServiceOnHttpEquivIndex = 30;
|
| +
|
| +PatchHelper g_patch_helper;
|
| +
|
| +BEGIN_VTABLE_PATCHES(IBrowserService)
|
| + VTABLE_PATCH_ENTRY(kIBrowserServiceOnHttpEquivIndex, Bho::OnHttpEquiv)
|
| +END_VTABLE_PATCHES()
|
| +
|
| +_ATL_FUNC_INFO Bho::kBeforeNavigate2Info = {
|
| + CC_STDCALL, VT_EMPTY, 7, {
|
| + VT_DISPATCH,
|
| + VT_VARIANT | VT_BYREF,
|
| + VT_VARIANT | VT_BYREF,
|
| + VT_VARIANT | VT_BYREF,
|
| + VT_VARIANT | VT_BYREF,
|
| + VT_VARIANT | VT_BYREF,
|
| + VT_BOOL | VT_BYREF
|
| + }
|
| +};
|
| +
|
| +Bho::Bho() {
|
| +}
|
| +
|
| +STDMETHODIMP Bho::SetSite(IUnknown* site) {
|
| + HRESULT hr = S_OK;
|
| + if (site) {
|
| + ScopedComPtr<IWebBrowser2> web_browser2;
|
| + web_browser2.QueryFrom(site);
|
| + if (web_browser2) {
|
| + hr = DispEventAdvise(web_browser2, &DIID_DWebBrowserEvents2);
|
| + DCHECK(SUCCEEDED(hr)) << "DispEventAdvise failed. Error: " << hr;
|
| + }
|
| +
|
| + if (g_patch_helper.state() == PatchHelper::PATCH_IBROWSER) {
|
| + ScopedComPtr<IBrowserService> browser_service;
|
| + hr = DoQueryService(SID_SShellBrowser, site, browser_service.Receive());
|
| + DCHECK(browser_service) << "DoQueryService - SID_SShellBrowser failed."
|
| + << " Site: " << site << " Error: " << hr;
|
| + if (browser_service) {
|
| + g_patch_helper.PatchBrowserService(browser_service);
|
| + DCHECK(SUCCEEDED(hr)) << "vtable_patch::PatchInterfaceMethods failed."
|
| + << " Site: " << site << " Error: " << hr;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return IObjectWithSiteImpl<Bho>::SetSite(site);
|
| +}
|
| +
|
| +STDMETHODIMP Bho::BeforeNavigate2(IDispatch* dispatch, VARIANT* url,
|
| + VARIANT* flags, VARIANT* target_frame_name, VARIANT* post_data,
|
| + VARIANT* headers, VARIANT_BOOL* cancel) {
|
| + ScopedComPtr<IWebBrowser2> web_browser2;
|
| + if (dispatch)
|
| + web_browser2.QueryFrom(dispatch);
|
| +
|
| + if (!web_browser2) {
|
| + NOTREACHED() << "Can't find WebBrowser2 with given dispatch";
|
| + return S_OK; // Return success, we operate on best effort basis.
|
| + }
|
| +
|
| + DLOG(INFO) << "BeforeNavigate2: " << url->bstrVal;
|
| +
|
| + if (g_patch_helper.state() == PatchHelper::PATCH_IBROWSER) {
|
| + VARIANT_BOOL is_top_level = VARIANT_FALSE;
|
| + web_browser2->get_TopLevelContainer(&is_top_level);
|
| +
|
| + std::wstring current_url;
|
| + bool is_chrome_protocol = false;
|
| + if (is_top_level && IsValidUrlScheme(url->bstrVal)) {
|
| + current_url.assign(url->bstrVal, SysStringLen(url->bstrVal));
|
| + is_chrome_protocol = StartsWith(current_url, kChromeProtocolPrefix,
|
| + false);
|
| +
|
| + if (!is_chrome_protocol && IsOptInUrl(current_url.c_str())) {
|
| + DLOG(INFO) << "Canceling navigation and switching to cf";
|
| + // Cancel original navigation
|
| + *cancel = VARIANT_TRUE;
|
| +
|
| + // Issue new request with 'cf:'
|
| + current_url.insert(0, kChromeProtocolPrefix);
|
| + ScopedVariant new_url(current_url.c_str());
|
| + HRESULT hr = web_browser2->Navigate2(new_url.AsInput(), flags,
|
| + target_frame_name, post_data,
|
| + headers);
|
| + DCHECK(SUCCEEDED(hr)) << "web_browser2->Navigate2 failed. Error: " << hr
|
| + << std::endl << "Url: " << current_url
|
| + << std::endl << "flags: " << flags
|
| + << std::endl << "post data: " << post_data
|
| + << std::endl << "headers: " << headers;
|
| + }
|
| + }
|
| + }
|
| + return S_OK;
|
| +}
|
| +
|
| +HRESULT Bho::FinalConstruct() {
|
| + return S_OK;
|
| +}
|
| +
|
| +void Bho::FinalRelease() {
|
| +}
|
| +
|
| +HRESULT STDMETHODCALLTYPE Bho::OnHttpEquiv(
|
| + IBrowserService_OnHttpEquiv_Fn original_httpequiv,
|
| + IBrowserService* browser, IShellView* shell_view, BOOL done,
|
| + VARIANT* in_arg, VARIANT* out_arg) {
|
| + if (!done && in_arg && (VT_BSTR == V_VT(in_arg))) {
|
| + if (StrStrI(V_BSTR(in_arg), kChromeContentPrefix)) {
|
| + // OnHttpEquiv is invoked for meta tags within sub frames as well.
|
| + // We want to switch renderers only for the top level frame. Since
|
| + // the same |browser| and |shell_view| are passed in to OnHttpEquiv
|
| + // even for sub iframes, we determine if this is the top one by
|
| + // checking if there are any sub frames created or not.
|
| + ScopedComPtr<IWebBrowser2> web_browser2;
|
| + DoQueryService(SID_SWebBrowserApp, browser, web_browser2.Receive());
|
| + if (web_browser2 && !HasSubFrames(web_browser2))
|
| + SwitchRenderer(web_browser2, browser, shell_view, V_BSTR(in_arg));
|
| + }
|
| + }
|
| +
|
| + return original_httpequiv(browser, shell_view, done, in_arg, out_arg);
|
| +}
|
| +
|
| +bool Bho::HasSubFrames(IWebBrowser2* web_browser2) {
|
| + bool has_sub_frames = false;
|
| + ScopedComPtr<IDispatch> doc_dispatch;
|
| + if (web_browser2) {
|
| + HRESULT hr = web_browser2->get_Document(doc_dispatch.Receive());
|
| + DCHECK(SUCCEEDED(hr) && doc_dispatch) <<
|
| + "web_browser2->get_Document failed. Error: " << hr;
|
| + ScopedComPtr<IOleContainer> container;
|
| + if (SUCCEEDED(hr) && doc_dispatch) {
|
| + container.QueryFrom(doc_dispatch);
|
| + ScopedComPtr<IEnumUnknown> enumerator;
|
| + if (container) {
|
| + container->EnumObjects(OLECONTF_EMBEDDINGS, enumerator.Receive());
|
| + ScopedComPtr<IUnknown> unk;
|
| + ULONG items_retrieved = 0;
|
| + if (enumerator)
|
| + enumerator->Next(1, unk.Receive(), &items_retrieved);
|
| + has_sub_frames = (items_retrieved != 0);
|
| + }
|
| + }
|
| + }
|
| +
|
| + return has_sub_frames;
|
| +}
|
| +
|
| +HRESULT Bho::SwitchRenderer(IWebBrowser2* web_browser2,
|
| + IBrowserService* browser, IShellView* shell_view,
|
| + const wchar_t* meta_tag) {
|
| + DCHECK(web_browser2 && browser && shell_view && meta_tag);
|
| +
|
| + // Get access to the mshtml instance and the moniker
|
| + ScopedComPtr<IOleObject> mshtml_ole_object;
|
| + HRESULT hr = shell_view->GetItemObject(SVGIO_BACKGROUND, IID_IOleObject,
|
| + reinterpret_cast<void**>(mshtml_ole_object.Receive()));
|
| + if (!mshtml_ole_object) {
|
| + NOTREACHED() << "shell_view->GetItemObject failed. Error: " << hr;
|
| + return hr;
|
| + }
|
| +
|
| + std::wstring url;
|
| + ScopedComPtr<IMoniker> moniker;
|
| + hr = mshtml_ole_object->GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
|
| + OLEWHICHMK_OBJFULL, moniker.Receive());
|
| + DCHECK(moniker) << "mshtml_ole_object->GetMoniker failed. Error: " << hr;
|
| +
|
| + if (moniker)
|
| + hr = GetUrlFromMoniker(moniker, NULL, &url);
|
| +
|
| + DCHECK(!url.empty()) << "GetUrlFromMoniker failed. Error: " << hr;
|
| + DCHECK(!StartsWith(url, kChromeProtocolPrefix, false));
|
| +
|
| + if (!url.empty()) {
|
| + url.insert(0, kChromeProtocolPrefix);
|
| + // Navigate to new url
|
| + VARIANT empty = ScopedVariant::kEmptyVariant;
|
| + VARIANT flags = { VT_I4 };
|
| + V_I4(&flags) = 0;
|
| + ScopedVariant url_var(url.c_str());
|
| + hr = web_browser2->Navigate2(url_var.AsInput(), &flags, &empty, &empty,
|
| + &empty);
|
| + DCHECK(SUCCEEDED(hr)) << "web_browser2->Navigate2 failed. Error: " << hr
|
| + << std::endl << "Url: " << url;
|
| + }
|
| +
|
| + return S_OK;
|
| +}
|
| +
|
| +void PatchHelper::InitializeAndPatchProtocolsIfNeeded() {
|
| + if (state_ != UNKNOWN)
|
| + return;
|
| +
|
| + bool patch_protocol = GetConfigBool(true, kPatchProtocols);
|
| + if (patch_protocol) {
|
| + ProtocolSinkWrap::PatchProtocolHandler(kUrlMonDllName, CLSID_HttpProtocol);
|
| + ProtocolSinkWrap::PatchProtocolHandler(kUrlMonDllName, CLSID_HttpSProtocol);
|
| + state_ = PATCH_PROTOCOL;
|
| + } else {
|
| + state_ = PATCH_IBROWSER;
|
| + }
|
| +}
|
| +
|
| +void PatchHelper::PatchBrowserService(IBrowserService* browser_service) {
|
| + DCHECK(state_ == PATCH_IBROWSER);
|
| + state_ = PATCH_IBROWSER_OK;
|
| + vtable_patch::PatchInterfaceMethods(browser_service,
|
| + IBrowserService_PatchInfo);
|
| +}
|
| +
|
| +extern vtable_patch::MethodPatchInfo IInternetProtocol_PatchInfo[];
|
| +extern vtable_patch::MethodPatchInfo IInternetProtocolEx_PatchInfo[];
|
| +void PatchHelper::UnpatchIfNeeded() {
|
| + if (state_ == PATCH_PROTOCOL) {
|
| + vtable_patch::UnpatchInterfaceMethods(IInternetProtocol_PatchInfo);
|
| + vtable_patch::UnpatchInterfaceMethods(IInternetProtocolEx_PatchInfo);
|
| + } else if (state_ == PATCH_IBROWSER_OK) {
|
| + vtable_patch::UnpatchInterfaceMethods(IBrowserService_PatchInfo);
|
| + }
|
| +
|
| + state_ = UNKNOWN;
|
| +}
|
| +
|
|
|