| // Copyright (c) 2012 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/chrome_frame_activex.h" |
| |
| #include <wininet.h> |
| |
| #include <algorithm> |
| #include <map> |
| |
| #include "base/basictypes.h" |
| #include "base/command_line.h" |
| #include "base/debug/trace_event.h" |
| #include "base/logging.h" |
| #include "base/memory/singleton.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/win/scoped_bstr.h" |
| #include "base/win/scoped_variant.h" |
| #include "chrome/common/automation_messages.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/test/automation/tab_proxy.h" |
| #include "chrome_frame/utils.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| |
| // Class used to maintain a mapping from top-level windows to ChromeFrameActivex |
| // instances. |
| class TopLevelWindowMapping { |
| public: |
| typedef std::vector<HWND> WindowList; |
| |
| static TopLevelWindowMapping* GetInstance() { |
| return Singleton<TopLevelWindowMapping>::get(); |
| } |
| |
| // Add |cf_window| to the set of windows registered under |top_window|. |
| void AddMapping(HWND top_window, HWND cf_window) { |
| top_window_map_lock_.Lock(); |
| top_window_map_[top_window].push_back(cf_window); |
| top_window_map_lock_.Unlock(); |
| } |
| |
| // Return the set of Chrome-Frame instances under |window|. |
| WindowList GetInstances(HWND window) { |
| top_window_map_lock_.Lock(); |
| WindowList list = top_window_map_[window]; |
| top_window_map_lock_.Unlock(); |
| return list; |
| } |
| |
| private: |
| // Constructor is private as this class it to be used as a singleton. |
| // See static method instance(). |
| TopLevelWindowMapping() {} |
| |
| friend struct DefaultSingletonTraits<TopLevelWindowMapping>; |
| |
| typedef std::map<HWND, WindowList> TopWindowMap; |
| TopWindowMap top_window_map_; |
| |
| CComAutoCriticalSection top_window_map_lock_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TopLevelWindowMapping); |
| }; |
| |
| // Message pump hook function that monitors for WM_MOVE and WM_MOVING |
| // messages on a top-level window, and passes notification to the appropriate |
| // Chrome-Frame instances. |
| LRESULT CALLBACK TopWindowProc(int code, WPARAM wparam, LPARAM lparam) { |
| CWPSTRUCT* info = reinterpret_cast<CWPSTRUCT*>(lparam); |
| const UINT &message = info->message; |
| const HWND &message_hwnd = info->hwnd; |
| |
| switch (message) { |
| case WM_MOVE: |
| case WM_MOVING: { |
| TopLevelWindowMapping::WindowList cf_instances = |
| TopLevelWindowMapping::GetInstance()->GetInstances(message_hwnd); |
| TopLevelWindowMapping::WindowList::iterator |
| iter(cf_instances.begin()), end(cf_instances.end()); |
| for (; iter != end; ++iter) { |
| PostMessage(*iter, WM_HOST_MOVED_NOTIFICATION, NULL, NULL); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| |
| return CallNextHookEx(0, code, wparam, lparam); |
| } |
| |
| HHOOK InstallLocalWindowHook(HWND window) { |
| if (!window) |
| return NULL; |
| |
| DWORD proc_thread = ::GetWindowThreadProcessId(window, NULL); |
| if (!proc_thread) |
| return NULL; |
| |
| // Note that this hook is installed as a LOCAL hook. |
| return ::SetWindowsHookEx(WH_CALLWNDPROC, |
| TopWindowProc, |
| NULL, |
| proc_thread); |
| } |
| |
| } // unnamed namespace |
| |
| namespace chrome_frame { |
| std::string ActiveXCreateUrl(const GURL& parsed_url, |
| const AttachExternalTabParams& params) { |
| return base::StringPrintf( |
| "%hs?attach_external_tab&%I64u&%d&%d&%d&%d&%d&%hs", |
| parsed_url.GetOrigin().spec().c_str(), |
| params.cookie, |
| params.disposition, |
| params.dimensions.x(), |
| params.dimensions.y(), |
| params.dimensions.width(), |
| params.dimensions.height(), |
| params.profile_name.c_str()); |
| } |
| |
| int GetDisposition(const AttachExternalTabParams& params) { |
| return params.disposition; |
| } |
| |
| void GetMiniContextMenuData(UINT cmd, |
| const MiniContextMenuParams& params, |
| GURL* referrer, |
| GURL* url) { |
| *referrer = params.frame_url.is_empty() ? params.page_url : params.frame_url; |
| *url = (cmd == IDS_CONTENT_CONTEXT_SAVELINKAS ? |
| params.link_url : params.src_url); |
| } |
| |
| } // namespace chrome_frame |
| |
| ChromeFrameActivex::ChromeFrameActivex() |
| : chrome_wndproc_hook_(NULL), |
| attaching_to_existing_cf_tab_(false) { |
| TRACE_EVENT_BEGIN_ETW("chromeframe.createactivex", this, ""); |
| } |
| |
| HRESULT ChromeFrameActivex::FinalConstruct() { |
| HRESULT hr = Base::FinalConstruct(); |
| if (FAILED(hr)) |
| return hr; |
| |
| // No need to call FireOnChanged at this point since nobody will be listening. |
| ready_state_ = READYSTATE_LOADING; |
| return S_OK; |
| } |
| |
| ChromeFrameActivex::~ChromeFrameActivex() { |
| // We expect these to be released during a call to SetClientSite(NULL). |
| DCHECK_EQ(0u, onmessage_.size()); |
| DCHECK_EQ(0u, onloaderror_.size()); |
| DCHECK_EQ(0u, onload_.size()); |
| DCHECK_EQ(0u, onreadystatechanged_.size()); |
| DCHECK_EQ(0u, onextensionready_.size()); |
| |
| if (chrome_wndproc_hook_) { |
| BOOL unhook_success = ::UnhookWindowsHookEx(chrome_wndproc_hook_); |
| DCHECK(unhook_success); |
| } |
| |
| // ChromeFramePlugin::Uninitialize() |
| Base::Uninitialize(); |
| |
| TRACE_EVENT_END_ETW("chromeframe.createactivex", this, ""); |
| } |
| |
| LRESULT ChromeFrameActivex::OnCreate(UINT message, WPARAM wparam, LPARAM lparam, |
| BOOL& handled) { |
| Base::OnCreate(message, wparam, lparam, handled); |
| // Install the notification hook on the top-level window, so that we can |
| // be notified on move events. Note that the return value is not checked. |
| // This hook is installed here, as opposed to during IOleObject_SetClientSite |
| // because m_hWnd has not yet been assigned during the SetSite call. |
| InstallTopLevelHook(m_spClientSite); |
| return 0; |
| } |
| |
| LRESULT ChromeFrameActivex::OnHostMoved(UINT message, WPARAM wparam, |
| LPARAM lparam, BOOL& handled) { |
| Base::OnHostMoved(); |
| return 0; |
| } |
| |
| HRESULT ChromeFrameActivex::GetContainingDocument(IHTMLDocument2** doc) { |
| base::win::ScopedComPtr<IOleContainer> container; |
| HRESULT hr = m_spClientSite->GetContainer(container.Receive()); |
| if (container) |
| hr = container.QueryInterface(doc); |
| return hr; |
| } |
| |
| HRESULT ChromeFrameActivex::GetDocumentWindow(IHTMLWindow2** window) { |
| base::win::ScopedComPtr<IHTMLDocument2> document; |
| HRESULT hr = GetContainingDocument(document.Receive()); |
| if (document) |
| hr = document->get_parentWindow(window); |
| return hr; |
| } |
| |
| void ChromeFrameActivex::OnLoad(const GURL& gurl) { |
| base::win::ScopedComPtr<IDispatch> event; |
| std::string url = gurl.spec(); |
| if (SUCCEEDED(CreateDomEvent("event", url, "", event.Receive()))) |
| Fire_onload(event); |
| |
| FireEvent(onload_, url); |
| Base::OnLoad(gurl); |
| } |
| |
| void ChromeFrameActivex::OnLoadFailed(int error_code, const std::string& url) { |
| base::win::ScopedComPtr<IDispatch> event; |
| if (SUCCEEDED(CreateDomEvent("event", url, "", event.Receive()))) |
| Fire_onloaderror(event); |
| |
| FireEvent(onloaderror_, url); |
| Base::OnLoadFailed(error_code, url); |
| } |
| |
| void ChromeFrameActivex::OnMessageFromChromeFrame(const std::string& message, |
| const std::string& origin, |
| const std::string& target) { |
| DVLOG(1) << __FUNCTION__; |
| |
| if (target.compare("*") != 0) { |
| bool drop = true; |
| |
| if (is_privileged()) { |
| // Forward messages if the control is in privileged mode. |
| base::win::ScopedComPtr<IDispatch> message_event; |
| if (SUCCEEDED(CreateDomEvent("message", message, origin, |
| message_event.Receive()))) { |
| base::win::ScopedBstr target_bstr(UTF8ToWide(target).c_str()); |
| Fire_onprivatemessage(message_event, target_bstr); |
| |
| FireEvent(onprivatemessage_, message_event, target_bstr); |
| } |
| } else { |
| if (HaveSameOrigin(target, document_url_)) { |
| drop = false; |
| } else { |
| DLOG(WARNING) << "Dropping posted message since target doesn't match " |
| "the current document's origin. target=" << target; |
| } |
| } |
| |
| if (drop) |
| return; |
| } |
| |
| base::win::ScopedComPtr<IDispatch> message_event; |
| if (SUCCEEDED(CreateDomEvent("message", message, origin, |
| message_event.Receive()))) { |
| Fire_onmessage(message_event); |
| |
| FireEvent(onmessage_, message_event); |
| |
| base::win::ScopedVariant event_var; |
| event_var.Set(static_cast<IDispatch*>(message_event)); |
| InvokeScriptFunction(onmessage_handler_, event_var.AsInput()); |
| } |
| } |
| |
| bool ChromeFrameActivex::ShouldShowVersionMismatchDialog( |
| bool is_privileged, |
| IOleClientSite* client_site) { |
| if (!is_privileged) { |
| return true; |
| } |
| |
| if (client_site) { |
| base::win::ScopedComPtr<IChromeFramePrivileged> service; |
| HRESULT hr = DoQueryService(SID_ChromeFramePrivileged, |
| client_site, |
| service.Receive()); |
| if (SUCCEEDED(hr) && service) { |
| return (S_FALSE != service->ShouldShowVersionMismatchDialog()); |
| } |
| } |
| |
| NOTREACHED(); |
| return true; |
| } |
| |
| void ChromeFrameActivex::OnAutomationServerLaunchFailed( |
| AutomationLaunchResult reason, const std::string& server_version) { |
| Base::OnAutomationServerLaunchFailed(reason, server_version); |
| |
| if (reason == AUTOMATION_VERSION_MISMATCH && |
| ShouldShowVersionMismatchDialog(is_privileged(), m_spClientSite)) { |
| UMA_HISTOGRAM_COUNTS("ChromeFrame.VersionMismatchDisplayed", 1); |
| DisplayVersionMismatchWarning(m_hWnd, server_version); |
| } |
| } |
| |
| void ChromeFrameActivex::OnChannelError() { |
| Fire_onchannelerror(); |
| } |
| |
| HRESULT ChromeFrameActivex::OnDraw(ATL_DRAWINFO& draw_info) { // NOLINT |
| HRESULT hr = S_OK; |
| int dc_type = ::GetObjectType(draw_info.hicTargetDev); |
| if (dc_type == OBJ_ENHMETADC) { |
| RECT print_bounds = {0}; |
| print_bounds.left = draw_info.prcBounds->left; |
| print_bounds.right = draw_info.prcBounds->right; |
| print_bounds.top = draw_info.prcBounds->top; |
| print_bounds.bottom = draw_info.prcBounds->bottom; |
| |
| automation_client_->Print(draw_info.hdcDraw, print_bounds); |
| } else { |
| hr = Base::OnDraw(draw_info); |
| } |
| |
| return hr; |
| } |
| |
| STDMETHODIMP ChromeFrameActivex::Load(IPropertyBag* bag, IErrorLog* error_log) { |
| DCHECK(bag); |
| |
| const wchar_t* event_props[] = { |
| (L"onload"), |
| (L"onloaderror"), |
| (L"onmessage"), |
| (L"onreadystatechanged"), |
| }; |
| |
| base::win::ScopedComPtr<IHTMLObjectElement> obj_element; |
| GetObjectElement(obj_element.Receive()); |
| |
| base::win::ScopedBstr object_id; |
| GetObjectScriptId(obj_element, object_id.Receive()); |
| |
| base::win::ScopedComPtr<IHTMLElement2> element; |
| element.QueryFrom(obj_element); |
| HRESULT hr = S_OK; |
| |
| for (int i = 0; SUCCEEDED(hr) && i < arraysize(event_props); ++i) { |
| base::win::ScopedBstr prop(event_props[i]); |
| base::win::ScopedVariant value; |
| if (SUCCEEDED(bag->Read(prop, value.Receive(), error_log))) { |
| if (value.type() != VT_BSTR || |
| FAILED(hr = CreateScriptBlockForEvent(element, object_id, |
| V_BSTR(&value), prop))) { |
| DLOG(ERROR) << "Failed to create script block for " << prop |
| << base::StringPrintf(L"hr=0x%08X, vt=%i", hr, |
| value.type()); |
| } else { |
| DVLOG(1) << "script block created for event " << prop |
| << base::StringPrintf(" (0x%08X)", hr) << " connections: " << |
| ProxyDIChromeFrameEvents<ChromeFrameActivex>::m_vec.GetSize(); |
| } |
| } else { |
| DVLOG(1) << "event property " << prop << " not in property bag"; |
| } |
| } |
| |
| base::win::ScopedVariant src; |
| if (SUCCEEDED(bag->Read(base::win::ScopedBstr(L"src"), src.Receive(), |
| error_log))) { |
| if (src.type() == VT_BSTR) { |
| hr = put_src(V_BSTR(&src)); |
| DCHECK(hr != E_UNEXPECTED); |
| } |
| } |
| |
| base::win::ScopedVariant use_chrome_network; |
| if (SUCCEEDED(bag->Read(base::win::ScopedBstr(L"useChromeNetwork"), |
| use_chrome_network.Receive(), error_log))) { |
| VariantChangeType(use_chrome_network.AsInput(), |
| use_chrome_network.AsInput(), |
| 0, VT_BOOL); |
| if (use_chrome_network.type() == VT_BOOL) { |
| hr = put_useChromeNetwork(V_BOOL(&use_chrome_network)); |
| DCHECK(hr != E_UNEXPECTED); |
| } |
| } |
| |
| DLOG_IF(ERROR, FAILED(hr)) |
| << base::StringPrintf("Failed to load property bag: 0x%08X", hr); |
| |
| return hr; |
| } |
| |
| const wchar_t g_activex_insecure_content_error[] = { |
| L"data:text/html,<html><body><b>ChromeFrame Security Error<br><br>" |
| L"Cannot navigate to HTTP url when document URL is HTTPS</body></html>"}; |
| |
| STDMETHODIMP ChromeFrameActivex::put_src(BSTR src) { |
| GURL document_url(GetDocumentUrl()); |
| if (document_url.SchemeIsSecure()) { |
| GURL source_url(src); |
| if (!source_url.SchemeIsSecure()) { |
| Base::put_src(base::win::ScopedBstr(g_activex_insecure_content_error)); |
| return E_ACCESSDENIED; |
| } |
| } |
| HRESULT hr = S_OK; |
| // If we are connecting to an existing ExternalTabContainer instance in |
| // Chrome then we should wait for Chrome to initiate the navigation. |
| if (!attaching_to_existing_cf_tab_) { |
| hr = Base::put_src(src); |
| } else { |
| url_.Reset(::SysAllocString(src)); |
| attaching_to_existing_cf_tab_ = false; |
| } |
| return S_OK; |
| } |
| |
| HRESULT ChromeFrameActivex::IOleObject_SetClientSite( |
| IOleClientSite* client_site) { |
| HRESULT hr = Base::IOleObject_SetClientSite(client_site); |
| if (FAILED(hr) || !client_site) { |
| EventHandlers* handlers[] = { |
| &onmessage_, |
| &onloaderror_, |
| &onload_, |
| &onreadystatechanged_, |
| &onextensionready_, |
| }; |
| |
| for (int i = 0; i < arraysize(handlers); ++i) |
| handlers[i]->clear(); |
| |
| // Drop privileged mode on uninitialization. |
| set_is_privileged(false); |
| } else { |
| base::win::ScopedComPtr<IHTMLDocument2> document; |
| GetContainingDocument(document.Receive()); |
| if (document) { |
| base::win::ScopedBstr url; |
| if (SUCCEEDED(document->get_URL(url.Receive()))) |
| WideToUTF8(url, url.Length(), &document_url_); |
| } |
| |
| // Probe to see whether the host implements the privileged service. |
| base::win::ScopedComPtr<IChromeFramePrivileged> service; |
| HRESULT service_hr = DoQueryService(SID_ChromeFramePrivileged, |
| m_spClientSite, |
| service.Receive()); |
| if (SUCCEEDED(service_hr) && service) { |
| // Does the host want privileged mode? |
| boolean wants_privileged = false; |
| service_hr = service->GetWantsPrivileged(&wants_privileged); |
| |
| if (SUCCEEDED(service_hr) && wants_privileged) |
| set_is_privileged(true); |
| |
| url_fetcher_->set_privileged_mode(is_privileged()); |
| } |
| |
| std::wstring profile_name(GetHostProcessName(false)); |
| if (is_privileged()) { |
| base::win::ScopedBstr profile_name_arg; |
| service_hr = service->GetChromeProfileName(profile_name_arg.Receive()); |
| if (S_OK == service_hr && profile_name_arg) |
| profile_name.assign(profile_name_arg, profile_name_arg.Length()); |
| } |
| |
| std::string utf8_url; |
| if (url_.Length()) { |
| WideToUTF8(url_, url_.Length(), &utf8_url); |
| } |
| |
| InitializeAutomationSettings(); |
| |
| if (service) { |
| base::win::ScopedBstr navigation_url; |
| service->GetNavigationUrl(navigation_url.Receive()); |
| if (navigation_url.Length()) { |
| ChromeFrameUrl cf_url; |
| cf_url.Parse(navigation_url.operator BSTR()); |
| if (cf_url.attach_to_external_tab()) { |
| automation_client_->AttachExternalTab(cf_url.cookie()); |
| attaching_to_existing_cf_tab_ = true; |
| } |
| } |
| } |
| url_fetcher_->set_frame_busting(!is_privileged()); |
| automation_client_->SetUrlFetcher(url_fetcher_.get()); |
| if (!InitializeAutomation(profile_name, IsIEInPrivate(), true, |
| GURL(utf8_url), GURL(), false)) { |
| DLOG(ERROR) << "Failed to navigate to url:" << utf8_url; |
| return E_FAIL; |
| } |
| |
| // Log a metric that Chrome Frame is being used in Widget mode |
| UMA_LAUNCH_TYPE_COUNT(RENDERER_TYPE_CHROME_WIDGET); |
| } |
| |
| return hr; |
| } |
| |
| HRESULT ChromeFrameActivex::GetObjectScriptId(IHTMLObjectElement* object_elem, |
| BSTR* id) { |
| DCHECK(object_elem != NULL); |
| DCHECK(id != NULL); |
| |
| HRESULT hr = E_FAIL; |
| if (object_elem) { |
| base::win::ScopedComPtr<IHTMLElement> elem; |
| hr = elem.QueryFrom(object_elem); |
| if (elem) { |
| hr = elem->get_id(id); |
| } |
| } |
| |
| return hr; |
| } |
| |
| HRESULT ChromeFrameActivex::GetObjectElement(IHTMLObjectElement** element) { |
| DCHECK(m_spClientSite); |
| if (!m_spClientSite) |
| return E_UNEXPECTED; |
| |
| base::win::ScopedComPtr<IOleControlSite> site; |
| HRESULT hr = site.QueryFrom(m_spClientSite); |
| if (site) { |
| base::win::ScopedComPtr<IDispatch> disp; |
| hr = site->GetExtendedControl(disp.Receive()); |
| if (disp) { |
| hr = disp.QueryInterface(element); |
| } else { |
| DCHECK(FAILED(hr)); |
| } |
| } |
| |
| return hr; |
| } |
| |
| HRESULT ChromeFrameActivex::CreateScriptBlockForEvent( |
| IHTMLElement2* insert_after, BSTR instance_id, BSTR script, |
| BSTR event_name) { |
| DCHECK(insert_after); |
| DCHECK_GT(::SysStringLen(event_name), 0UL); // should always have this |
| |
| // This might be 0 if not specified in the HTML document. |
| if (!::SysStringLen(instance_id)) { |
| // TODO(tommi): Should we give ourselves an ID if this happens? |
| NOTREACHED() << "Need to handle this"; |
| return E_INVALIDARG; |
| } |
| |
| base::win::ScopedComPtr<IHTMLDocument2> document; |
| HRESULT hr = GetContainingDocument(document.Receive()); |
| if (SUCCEEDED(hr)) { |
| base::win::ScopedComPtr<IHTMLElement> element, new_element; |
| document->createElement(base::win::ScopedBstr(L"script"), |
| element.Receive()); |
| if (element) { |
| base::win::ScopedComPtr<IHTMLScriptElement> script_element; |
| if (SUCCEEDED(hr = script_element.QueryFrom(element))) { |
| script_element->put_htmlFor(instance_id); |
| script_element->put_event(event_name); |
| script_element->put_text(script); |
| |
| hr = insert_after->insertAdjacentElement( |
| base::win::ScopedBstr(L"afterEnd"), |
| element, |
| new_element.Receive()); |
| } |
| } |
| } |
| |
| return hr; |
| } |
| |
| void ChromeFrameActivex::FireEvent(const EventHandlers& handlers, |
| const std::string& arg) { |
| if (handlers.size()) { |
| base::win::ScopedComPtr<IDispatch> event; |
| if (SUCCEEDED(CreateDomEvent("event", arg, "", event.Receive()))) { |
| FireEvent(handlers, event); |
| } |
| } |
| } |
| |
| void ChromeFrameActivex::FireEvent(const EventHandlers& handlers, |
| IDispatch* event) { |
| DCHECK(event != NULL); |
| VARIANT arg = { VT_DISPATCH }; |
| arg.pdispVal = event; |
| DISPPARAMS params = { &arg, NULL, 1, 0 }; |
| for (EventHandlers::const_iterator it = handlers.begin(); |
| it != handlers.end(); |
| ++it) { |
| HRESULT hr = (*it)->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT, |
| DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); |
| // 0x80020101 == SCRIPT_E_REPORTED. |
| // When the script we're invoking has an error, we get this error back. |
| DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101) |
| << base::StringPrintf(L"Failed to invoke script: 0x%08X", hr); |
| } |
| } |
| |
| void ChromeFrameActivex::FireEvent(const EventHandlers& handlers, |
| IDispatch* event, BSTR target) { |
| DCHECK(event != NULL); |
| // Arguments in reverse order to event handler function declaration, |
| // because that's what DISPPARAMS requires. |
| VARIANT args[2] = { { VT_BSTR }, { VT_DISPATCH }, }; |
| args[0].bstrVal = target; |
| args[1].pdispVal = event; |
| DISPPARAMS params = { args, NULL, arraysize(args), 0 }; |
| for (EventHandlers::const_iterator it = handlers.begin(); |
| it != handlers.end(); |
| ++it) { |
| HRESULT hr = (*it)->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT, |
| DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); |
| // 0x80020101 == SCRIPT_E_REPORTED. |
| // When the script we're invoking has an error, we get this error back. |
| DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101) |
| << base::StringPrintf(L"Failed to invoke script: 0x%08X", hr); |
| } |
| } |
| |
| HRESULT ChromeFrameActivex::InstallTopLevelHook(IOleClientSite* client_site) { |
| // Get the parent window of the site, and install our hook on the topmost |
| // window of the parent. |
| base::win::ScopedComPtr<IOleWindow> ole_window; |
| HRESULT hr = ole_window.QueryFrom(client_site); |
| if (FAILED(hr)) |
| return hr; |
| |
| HWND parent_wnd; |
| hr = ole_window->GetWindow(&parent_wnd); |
| if (FAILED(hr)) |
| return hr; |
| |
| HWND top_window = ::GetAncestor(parent_wnd, GA_ROOT); |
| chrome_wndproc_hook_ = InstallLocalWindowHook(top_window); |
| if (chrome_wndproc_hook_) |
| TopLevelWindowMapping::GetInstance()->AddMapping(top_window, m_hWnd); |
| |
| return chrome_wndproc_hook_ ? S_OK : E_FAIL; |
| } |
| |
| HRESULT ChromeFrameActivex::registerBhoIfNeeded() { |
| if (!m_spUnkSite) { |
| NOTREACHED() << "Invalid client site"; |
| return E_FAIL; |
| } |
| |
| if (NavigationManager::GetThreadInstance() != NULL) { |
| DVLOG(1) << "BHO already loaded"; |
| return S_OK; |
| } |
| |
| base::win::ScopedComPtr<IWebBrowser2> web_browser2; |
| HRESULT hr = DoQueryService(SID_SWebBrowserApp, m_spUnkSite, |
| web_browser2.Receive()); |
| if (FAILED(hr) || web_browser2.get() == NULL) { |
| DLOG(WARNING) << "Failed to get IWebBrowser2 from client site. Error:" |
| << base::StringPrintf(" 0x%08X", hr); |
| return hr; |
| } |
| |
| wchar_t bho_class_id_as_string[MAX_PATH] = {0}; |
| StringFromGUID2(CLSID_ChromeFrameBHO, bho_class_id_as_string, |
| arraysize(bho_class_id_as_string)); |
| |
| base::win::ScopedComPtr<IObjectWithSite> bho; |
| hr = bho.CreateInstance(CLSID_ChromeFrameBHO, NULL, CLSCTX_INPROC_SERVER); |
| if (FAILED(hr)) { |
| NOTREACHED() << "Failed to register ChromeFrame BHO. Error:" |
| << base::StringPrintf(" 0x%08X", hr); |
| return hr; |
| } |
| |
| hr = UrlMkSetSessionOption(URLMON_OPTION_USERAGENT_REFRESH, NULL, 0, 0); |
| if (FAILED(hr)) { |
| DLOG(ERROR) << "Failed to refresh user agent string from registry. " |
| << "UrlMkSetSessionOption returned " |
| << base::StringPrintf("0x%08x", hr); |
| return hr; |
| } |
| |
| hr = bho->SetSite(web_browser2); |
| if (FAILED(hr)) { |
| NOTREACHED() << "ChromeFrame BHO SetSite failed. Error:" |
| << base::StringPrintf(" 0x%08X", hr); |
| return hr; |
| } |
| |
| web_browser2->PutProperty(base::win::ScopedBstr(bho_class_id_as_string), |
| base::win::ScopedVariant(bho)); |
| return S_OK; |
| } |