1 // Copyright (c) 2012 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/chrome_frame_activex.h" 6 7 #include <wininet.h> 8 9 #include <algorithm> 10 #include <map> 11 12 #include "base/basictypes.h" 13 #include "base/command_line.h" 14 #include "base/debug/trace_event.h" 15 #include "base/logging.h" 16 #include "base/memory/singleton.h" 17 #include "base/path_service.h" 18 #include "base/strings/string_split.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "base/win/scoped_bstr.h" 23 #include "base/win/scoped_variant.h" 24 #include "chrome/common/automation_messages.h" 25 #include "chrome/common/chrome_constants.h" 26 #include "chrome/common/chrome_switches.h" 27 #include "chrome/test/automation/tab_proxy.h" 28 #include "chrome_frame/utils.h" 29 #include "url/gurl.h" 30 31 namespace { 32 33 // Class used to maintain a mapping from top-level windows to ChromeFrameActivex 34 // instances. 35 class TopLevelWindowMapping { 36 public: 37 typedef std::vector<HWND> WindowList; 38 39 static TopLevelWindowMapping* GetInstance() { 40 return Singleton<TopLevelWindowMapping>::get(); 41 } 42 43 // Add |cf_window| to the set of windows registered under |top_window|. 44 void AddMapping(HWND top_window, HWND cf_window) { 45 top_window_map_lock_.Lock(); 46 top_window_map_[top_window].push_back(cf_window); 47 top_window_map_lock_.Unlock(); 48 } 49 50 // Return the set of Chrome-Frame instances under |window|. 51 WindowList GetInstances(HWND window) { 52 top_window_map_lock_.Lock(); 53 WindowList list = top_window_map_[window]; 54 top_window_map_lock_.Unlock(); 55 return list; 56 } 57 58 private: 59 // Constructor is private as this class it to be used as a singleton. 60 // See static method instance(). 61 TopLevelWindowMapping() {} 62 63 friend struct DefaultSingletonTraits<TopLevelWindowMapping>; 64 65 typedef std::map<HWND, WindowList> TopWindowMap; 66 TopWindowMap top_window_map_; 67 68 CComAutoCriticalSection top_window_map_lock_; 69 70 DISALLOW_COPY_AND_ASSIGN(TopLevelWindowMapping); 71 }; 72 73 // Message pump hook function that monitors for WM_MOVE and WM_MOVING 74 // messages on a top-level window, and passes notification to the appropriate 75 // Chrome-Frame instances. 76 LRESULT CALLBACK TopWindowProc(int code, WPARAM wparam, LPARAM lparam) { 77 CWPSTRUCT* info = reinterpret_cast<CWPSTRUCT*>(lparam); 78 const UINT &message = info->message; 79 const HWND &message_hwnd = info->hwnd; 80 81 switch (message) { 82 case WM_MOVE: 83 case WM_MOVING: { 84 TopLevelWindowMapping::WindowList cf_instances = 85 TopLevelWindowMapping::GetInstance()->GetInstances(message_hwnd); 86 TopLevelWindowMapping::WindowList::iterator 87 iter(cf_instances.begin()), end(cf_instances.end()); 88 for (; iter != end; ++iter) { 89 PostMessage(*iter, WM_HOST_MOVED_NOTIFICATION, NULL, NULL); 90 } 91 break; 92 } 93 default: 94 break; 95 } 96 97 return CallNextHookEx(0, code, wparam, lparam); 98 } 99 100 HHOOK InstallLocalWindowHook(HWND window) { 101 if (!window) 102 return NULL; 103 104 DWORD proc_thread = ::GetWindowThreadProcessId(window, NULL); 105 if (!proc_thread) 106 return NULL; 107 108 // Note that this hook is installed as a LOCAL hook. 109 return ::SetWindowsHookEx(WH_CALLWNDPROC, 110 TopWindowProc, 111 NULL, 112 proc_thread); 113 } 114 115 } // unnamed namespace 116 117 namespace chrome_frame { 118 std::string ActiveXCreateUrl(const GURL& parsed_url, 119 const AttachExternalTabParams& params) { 120 return base::StringPrintf( 121 "%hs?attach_external_tab&%I64u&%d&%d&%d&%d&%d&%hs", 122 parsed_url.GetOrigin().spec().c_str(), 123 params.cookie, 124 params.disposition, 125 params.dimensions.x(), 126 params.dimensions.y(), 127 params.dimensions.width(), 128 params.dimensions.height(), 129 params.profile_name.c_str()); 130 } 131 132 int GetDisposition(const AttachExternalTabParams& params) { 133 return params.disposition; 134 } 135 136 void GetMiniContextMenuData(UINT cmd, 137 const MiniContextMenuParams& params, 138 GURL* referrer, 139 GURL* url) { 140 *referrer = params.frame_url.is_empty() ? params.page_url : params.frame_url; 141 *url = (cmd == IDS_CONTENT_CONTEXT_SAVELINKAS ? 142 params.link_url : params.src_url); 143 } 144 145 } // namespace chrome_frame 146 147 ChromeFrameActivex::ChromeFrameActivex() 148 : chrome_wndproc_hook_(NULL), 149 attaching_to_existing_cf_tab_(false) { 150 TRACE_EVENT_BEGIN_ETW("chromeframe.createactivex", this, ""); 151 } 152 153 HRESULT ChromeFrameActivex::FinalConstruct() { 154 HRESULT hr = Base::FinalConstruct(); 155 if (FAILED(hr)) 156 return hr; 157 158 // No need to call FireOnChanged at this point since nobody will be listening. 159 ready_state_ = READYSTATE_LOADING; 160 return S_OK; 161 } 162 163 ChromeFrameActivex::~ChromeFrameActivex() { 164 // We expect these to be released during a call to SetClientSite(NULL). 165 DCHECK_EQ(0u, onmessage_.size()); 166 DCHECK_EQ(0u, onloaderror_.size()); 167 DCHECK_EQ(0u, onload_.size()); 168 DCHECK_EQ(0u, onreadystatechanged_.size()); 169 DCHECK_EQ(0u, onextensionready_.size()); 170 171 if (chrome_wndproc_hook_) { 172 BOOL unhook_success = ::UnhookWindowsHookEx(chrome_wndproc_hook_); 173 DCHECK(unhook_success); 174 } 175 176 // ChromeFramePlugin::Uninitialize() 177 Base::Uninitialize(); 178 179 TRACE_EVENT_END_ETW("chromeframe.createactivex", this, ""); 180 } 181 182 LRESULT ChromeFrameActivex::OnCreate(UINT message, WPARAM wparam, LPARAM lparam, 183 BOOL& handled) { 184 Base::OnCreate(message, wparam, lparam, handled); 185 // Install the notification hook on the top-level window, so that we can 186 // be notified on move events. Note that the return value is not checked. 187 // This hook is installed here, as opposed to during IOleObject_SetClientSite 188 // because m_hWnd has not yet been assigned during the SetSite call. 189 InstallTopLevelHook(m_spClientSite); 190 return 0; 191 } 192 193 LRESULT ChromeFrameActivex::OnHostMoved(UINT message, WPARAM wparam, 194 LPARAM lparam, BOOL& handled) { 195 Base::OnHostMoved(); 196 return 0; 197 } 198 199 HRESULT ChromeFrameActivex::GetContainingDocument(IHTMLDocument2** doc) { 200 base::win::ScopedComPtr<IOleContainer> container; 201 HRESULT hr = m_spClientSite->GetContainer(container.Receive()); 202 if (container) 203 hr = container.QueryInterface(doc); 204 return hr; 205 } 206 207 HRESULT ChromeFrameActivex::GetDocumentWindow(IHTMLWindow2** window) { 208 base::win::ScopedComPtr<IHTMLDocument2> document; 209 HRESULT hr = GetContainingDocument(document.Receive()); 210 if (document) 211 hr = document->get_parentWindow(window); 212 return hr; 213 } 214 215 void ChromeFrameActivex::OnLoad(const GURL& gurl) { 216 base::win::ScopedComPtr<IDispatch> event; 217 std::string url = gurl.spec(); 218 if (SUCCEEDED(CreateDomEvent("event", url, "", event.Receive()))) 219 Fire_onload(event); 220 221 FireEvent(onload_, url); 222 Base::OnLoad(gurl); 223 } 224 225 void ChromeFrameActivex::OnLoadFailed(int error_code, const std::string& url) { 226 base::win::ScopedComPtr<IDispatch> event; 227 if (SUCCEEDED(CreateDomEvent("event", url, "", event.Receive()))) 228 Fire_onloaderror(event); 229 230 FireEvent(onloaderror_, url); 231 Base::OnLoadFailed(error_code, url); 232 } 233 234 void ChromeFrameActivex::OnMessageFromChromeFrame(const std::string& message, 235 const std::string& origin, 236 const std::string& target) { 237 DVLOG(1) << __FUNCTION__; 238 239 if (target.compare("*") != 0) { 240 bool drop = true; 241 242 if (is_privileged()) { 243 // Forward messages if the control is in privileged mode. 244 base::win::ScopedComPtr<IDispatch> message_event; 245 if (SUCCEEDED(CreateDomEvent("message", message, origin, 246 message_event.Receive()))) { 247 base::win::ScopedBstr target_bstr(UTF8ToWide(target).c_str()); 248 Fire_onprivatemessage(message_event, target_bstr); 249 250 FireEvent(onprivatemessage_, message_event, target_bstr); 251 } 252 } else { 253 if (HaveSameOrigin(target, document_url_)) { 254 drop = false; 255 } else { 256 DLOG(WARNING) << "Dropping posted message since target doesn't match " 257 "the current document's origin. target=" << target; 258 } 259 } 260 261 if (drop) 262 return; 263 } 264 265 base::win::ScopedComPtr<IDispatch> message_event; 266 if (SUCCEEDED(CreateDomEvent("message", message, origin, 267 message_event.Receive()))) { 268 Fire_onmessage(message_event); 269 270 FireEvent(onmessage_, message_event); 271 272 base::win::ScopedVariant event_var; 273 event_var.Set(static_cast<IDispatch*>(message_event)); 274 InvokeScriptFunction(onmessage_handler_, event_var.AsInput()); 275 } 276 } 277 278 bool ChromeFrameActivex::ShouldShowVersionMismatchDialog( 279 bool is_privileged, 280 IOleClientSite* client_site) { 281 if (!is_privileged) { 282 return true; 283 } 284 285 if (client_site) { 286 base::win::ScopedComPtr<IChromeFramePrivileged> service; 287 HRESULT hr = DoQueryService(SID_ChromeFramePrivileged, 288 client_site, 289 service.Receive()); 290 if (SUCCEEDED(hr) && service) { 291 return (S_FALSE != service->ShouldShowVersionMismatchDialog()); 292 } 293 } 294 295 NOTREACHED(); 296 return true; 297 } 298 299 void ChromeFrameActivex::OnAutomationServerLaunchFailed( 300 AutomationLaunchResult reason, const std::string& server_version) { 301 Base::OnAutomationServerLaunchFailed(reason, server_version); 302 303 if (reason == AUTOMATION_VERSION_MISMATCH && 304 ShouldShowVersionMismatchDialog(is_privileged(), m_spClientSite)) { 305 UMA_HISTOGRAM_COUNTS("ChromeFrame.VersionMismatchDisplayed", 1); 306 DisplayVersionMismatchWarning(m_hWnd, server_version); 307 } 308 } 309 310 void ChromeFrameActivex::OnChannelError() { 311 Fire_onchannelerror(); 312 } 313 314 HRESULT ChromeFrameActivex::OnDraw(ATL_DRAWINFO& draw_info) { // NOLINT 315 HRESULT hr = S_OK; 316 int dc_type = ::GetObjectType(draw_info.hicTargetDev); 317 if (dc_type == OBJ_ENHMETADC) { 318 RECT print_bounds = {0}; 319 print_bounds.left = draw_info.prcBounds->left; 320 print_bounds.right = draw_info.prcBounds->right; 321 print_bounds.top = draw_info.prcBounds->top; 322 print_bounds.bottom = draw_info.prcBounds->bottom; 323 324 automation_client_->Print(draw_info.hdcDraw, print_bounds); 325 } else { 326 hr = Base::OnDraw(draw_info); 327 } 328 329 return hr; 330 } 331 332 STDMETHODIMP ChromeFrameActivex::Load(IPropertyBag* bag, IErrorLog* error_log) { 333 DCHECK(bag); 334 335 const wchar_t* event_props[] = { 336 (L"onload"), 337 (L"onloaderror"), 338 (L"onmessage"), 339 (L"onreadystatechanged"), 340 }; 341 342 base::win::ScopedComPtr<IHTMLObjectElement> obj_element; 343 GetObjectElement(obj_element.Receive()); 344 345 base::win::ScopedBstr object_id; 346 GetObjectScriptId(obj_element, object_id.Receive()); 347 348 base::win::ScopedComPtr<IHTMLElement2> element; 349 element.QueryFrom(obj_element); 350 HRESULT hr = S_OK; 351 352 for (int i = 0; SUCCEEDED(hr) && i < arraysize(event_props); ++i) { 353 base::win::ScopedBstr prop(event_props[i]); 354 base::win::ScopedVariant value; 355 if (SUCCEEDED(bag->Read(prop, value.Receive(), error_log))) { 356 if (value.type() != VT_BSTR || 357 FAILED(hr = CreateScriptBlockForEvent(element, object_id, 358 V_BSTR(&value), prop))) { 359 DLOG(ERROR) << "Failed to create script block for " << prop 360 << base::StringPrintf(L"hr=0x%08X, vt=%i", hr, 361 value.type()); 362 } else { 363 DVLOG(1) << "script block created for event " << prop 364 << base::StringPrintf(" (0x%08X)", hr) << " connections: " << 365 ProxyDIChromeFrameEvents<ChromeFrameActivex>::m_vec.GetSize(); 366 } 367 } else { 368 DVLOG(1) << "event property " << prop << " not in property bag"; 369 } 370 } 371 372 base::win::ScopedVariant src; 373 if (SUCCEEDED(bag->Read(base::win::ScopedBstr(L"src"), src.Receive(), 374 error_log))) { 375 if (src.type() == VT_BSTR) { 376 hr = put_src(V_BSTR(&src)); 377 DCHECK(hr != E_UNEXPECTED); 378 } 379 } 380 381 base::win::ScopedVariant use_chrome_network; 382 if (SUCCEEDED(bag->Read(base::win::ScopedBstr(L"useChromeNetwork"), 383 use_chrome_network.Receive(), error_log))) { 384 VariantChangeType(use_chrome_network.AsInput(), 385 use_chrome_network.AsInput(), 386 0, VT_BOOL); 387 if (use_chrome_network.type() == VT_BOOL) { 388 hr = put_useChromeNetwork(V_BOOL(&use_chrome_network)); 389 DCHECK(hr != E_UNEXPECTED); 390 } 391 } 392 393 DLOG_IF(ERROR, FAILED(hr)) 394 << base::StringPrintf("Failed to load property bag: 0x%08X", hr); 395 396 return hr; 397 } 398 399 const wchar_t g_activex_insecure_content_error[] = { 400 L"data:text/html,<html><body><b>ChromeFrame Security Error<br><br>" 401 L"Cannot navigate to HTTP url when document URL is HTTPS</body></html>"}; 402 403 STDMETHODIMP ChromeFrameActivex::put_src(BSTR src) { 404 GURL document_url(GetDocumentUrl()); 405 if (document_url.SchemeIsSecure()) { 406 GURL source_url(src); 407 if (!source_url.SchemeIsSecure()) { 408 Base::put_src(base::win::ScopedBstr(g_activex_insecure_content_error)); 409 return E_ACCESSDENIED; 410 } 411 } 412 HRESULT hr = S_OK; 413 // If we are connecting to an existing ExternalTabContainer instance in 414 // Chrome then we should wait for Chrome to initiate the navigation. 415 if (!attaching_to_existing_cf_tab_) { 416 hr = Base::put_src(src); 417 } else { 418 url_.Reset(::SysAllocString(src)); 419 attaching_to_existing_cf_tab_ = false; 420 } 421 return S_OK; 422 } 423 424 HRESULT ChromeFrameActivex::IOleObject_SetClientSite( 425 IOleClientSite* client_site) { 426 HRESULT hr = Base::IOleObject_SetClientSite(client_site); 427 if (FAILED(hr) || !client_site) { 428 EventHandlers* handlers[] = { 429 &onmessage_, 430 &onloaderror_, 431 &onload_, 432 &onreadystatechanged_, 433 &onextensionready_, 434 }; 435 436 for (int i = 0; i < arraysize(handlers); ++i) 437 handlers[i]->clear(); 438 439 // Drop privileged mode on uninitialization. 440 set_is_privileged(false); 441 } else { 442 base::win::ScopedComPtr<IHTMLDocument2> document; 443 GetContainingDocument(document.Receive()); 444 if (document) { 445 base::win::ScopedBstr url; 446 if (SUCCEEDED(document->get_URL(url.Receive()))) 447 WideToUTF8(url, url.Length(), &document_url_); 448 } 449 450 // Probe to see whether the host implements the privileged service. 451 base::win::ScopedComPtr<IChromeFramePrivileged> service; 452 HRESULT service_hr = DoQueryService(SID_ChromeFramePrivileged, 453 m_spClientSite, 454 service.Receive()); 455 if (SUCCEEDED(service_hr) && service) { 456 // Does the host want privileged mode? 457 boolean wants_privileged = false; 458 service_hr = service->GetWantsPrivileged(&wants_privileged); 459 460 if (SUCCEEDED(service_hr) && wants_privileged) 461 set_is_privileged(true); 462 463 url_fetcher_->set_privileged_mode(is_privileged()); 464 } 465 466 std::wstring profile_name(GetHostProcessName(false)); 467 if (is_privileged()) { 468 base::win::ScopedBstr profile_name_arg; 469 service_hr = service->GetChromeProfileName(profile_name_arg.Receive()); 470 if (S_OK == service_hr && profile_name_arg) 471 profile_name.assign(profile_name_arg, profile_name_arg.Length()); 472 } 473 474 std::string utf8_url; 475 if (url_.Length()) { 476 WideToUTF8(url_, url_.Length(), &utf8_url); 477 } 478 479 InitializeAutomationSettings(); 480 481 if (service) { 482 base::win::ScopedBstr navigation_url; 483 service->GetNavigationUrl(navigation_url.Receive()); 484 if (navigation_url.Length()) { 485 ChromeFrameUrl cf_url; 486 cf_url.Parse(navigation_url.operator BSTR()); 487 if (cf_url.attach_to_external_tab()) { 488 automation_client_->AttachExternalTab(cf_url.cookie()); 489 attaching_to_existing_cf_tab_ = true; 490 } 491 } 492 } 493 url_fetcher_->set_frame_busting(!is_privileged()); 494 automation_client_->SetUrlFetcher(url_fetcher_.get()); 495 if (!InitializeAutomation(profile_name, IsIEInPrivate(), true, 496 GURL(utf8_url), GURL(), false)) { 497 DLOG(ERROR) << "Failed to navigate to url:" << utf8_url; 498 return E_FAIL; 499 } 500 501 // Log a metric that Chrome Frame is being used in Widget mode 502 UMA_LAUNCH_TYPE_COUNT(RENDERER_TYPE_CHROME_WIDGET); 503 } 504 505 return hr; 506 } 507 508 HRESULT ChromeFrameActivex::GetObjectScriptId(IHTMLObjectElement* object_elem, 509 BSTR* id) { 510 DCHECK(object_elem != NULL); 511 DCHECK(id != NULL); 512 513 HRESULT hr = E_FAIL; 514 if (object_elem) { 515 base::win::ScopedComPtr<IHTMLElement> elem; 516 hr = elem.QueryFrom(object_elem); 517 if (elem) { 518 hr = elem->get_id(id); 519 } 520 } 521 522 return hr; 523 } 524 525 HRESULT ChromeFrameActivex::GetObjectElement(IHTMLObjectElement** element) { 526 DCHECK(m_spClientSite); 527 if (!m_spClientSite) 528 return E_UNEXPECTED; 529 530 base::win::ScopedComPtr<IOleControlSite> site; 531 HRESULT hr = site.QueryFrom(m_spClientSite); 532 if (site) { 533 base::win::ScopedComPtr<IDispatch> disp; 534 hr = site->GetExtendedControl(disp.Receive()); 535 if (disp) { 536 hr = disp.QueryInterface(element); 537 } else { 538 DCHECK(FAILED(hr)); 539 } 540 } 541 542 return hr; 543 } 544 545 HRESULT ChromeFrameActivex::CreateScriptBlockForEvent( 546 IHTMLElement2* insert_after, BSTR instance_id, BSTR script, 547 BSTR event_name) { 548 DCHECK(insert_after); 549 DCHECK_GT(::SysStringLen(event_name), 0UL); // should always have this 550 551 // This might be 0 if not specified in the HTML document. 552 if (!::SysStringLen(instance_id)) { 553 // TODO(tommi): Should we give ourselves an ID if this happens? 554 NOTREACHED() << "Need to handle this"; 555 return E_INVALIDARG; 556 } 557 558 base::win::ScopedComPtr<IHTMLDocument2> document; 559 HRESULT hr = GetContainingDocument(document.Receive()); 560 if (SUCCEEDED(hr)) { 561 base::win::ScopedComPtr<IHTMLElement> element, new_element; 562 document->createElement(base::win::ScopedBstr(L"script"), 563 element.Receive()); 564 if (element) { 565 base::win::ScopedComPtr<IHTMLScriptElement> script_element; 566 if (SUCCEEDED(hr = script_element.QueryFrom(element))) { 567 script_element->put_htmlFor(instance_id); 568 script_element->put_event(event_name); 569 script_element->put_text(script); 570 571 hr = insert_after->insertAdjacentElement( 572 base::win::ScopedBstr(L"afterEnd"), 573 element, 574 new_element.Receive()); 575 } 576 } 577 } 578 579 return hr; 580 } 581 582 void ChromeFrameActivex::FireEvent(const EventHandlers& handlers, 583 const std::string& arg) { 584 if (handlers.size()) { 585 base::win::ScopedComPtr<IDispatch> event; 586 if (SUCCEEDED(CreateDomEvent("event", arg, "", event.Receive()))) { 587 FireEvent(handlers, event); 588 } 589 } 590 } 591 592 void ChromeFrameActivex::FireEvent(const EventHandlers& handlers, 593 IDispatch* event) { 594 DCHECK(event != NULL); 595 VARIANT arg = { VT_DISPATCH }; 596 arg.pdispVal = event; 597 DISPPARAMS params = { &arg, NULL, 1, 0 }; 598 for (EventHandlers::const_iterator it = handlers.begin(); 599 it != handlers.end(); 600 ++it) { 601 HRESULT hr = (*it)->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT, 602 DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); 603 // 0x80020101 == SCRIPT_E_REPORTED. 604 // When the script we're invoking has an error, we get this error back. 605 DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101) 606 << base::StringPrintf(L"Failed to invoke script: 0x%08X", hr); 607 } 608 } 609 610 void ChromeFrameActivex::FireEvent(const EventHandlers& handlers, 611 IDispatch* event, BSTR target) { 612 DCHECK(event != NULL); 613 // Arguments in reverse order to event handler function declaration, 614 // because that's what DISPPARAMS requires. 615 VARIANT args[2] = { { VT_BSTR }, { VT_DISPATCH }, }; 616 args[0].bstrVal = target; 617 args[1].pdispVal = event; 618 DISPPARAMS params = { args, NULL, arraysize(args), 0 }; 619 for (EventHandlers::const_iterator it = handlers.begin(); 620 it != handlers.end(); 621 ++it) { 622 HRESULT hr = (*it)->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT, 623 DISPATCH_METHOD, ¶ms, NULL, NULL, NULL); 624 // 0x80020101 == SCRIPT_E_REPORTED. 625 // When the script we're invoking has an error, we get this error back. 626 DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101) 627 << base::StringPrintf(L"Failed to invoke script: 0x%08X", hr); 628 } 629 } 630 631 HRESULT ChromeFrameActivex::InstallTopLevelHook(IOleClientSite* client_site) { 632 // Get the parent window of the site, and install our hook on the topmost 633 // window of the parent. 634 base::win::ScopedComPtr<IOleWindow> ole_window; 635 HRESULT hr = ole_window.QueryFrom(client_site); 636 if (FAILED(hr)) 637 return hr; 638 639 HWND parent_wnd; 640 hr = ole_window->GetWindow(&parent_wnd); 641 if (FAILED(hr)) 642 return hr; 643 644 HWND top_window = ::GetAncestor(parent_wnd, GA_ROOT); 645 chrome_wndproc_hook_ = InstallLocalWindowHook(top_window); 646 if (chrome_wndproc_hook_) 647 TopLevelWindowMapping::GetInstance()->AddMapping(top_window, m_hWnd); 648 649 return chrome_wndproc_hook_ ? S_OK : E_FAIL; 650 } 651 652 HRESULT ChromeFrameActivex::registerBhoIfNeeded() { 653 if (!m_spUnkSite) { 654 NOTREACHED() << "Invalid client site"; 655 return E_FAIL; 656 } 657 658 if (NavigationManager::GetThreadInstance() != NULL) { 659 DVLOG(1) << "BHO already loaded"; 660 return S_OK; 661 } 662 663 base::win::ScopedComPtr<IWebBrowser2> web_browser2; 664 HRESULT hr = DoQueryService(SID_SWebBrowserApp, m_spUnkSite, 665 web_browser2.Receive()); 666 if (FAILED(hr) || web_browser2.get() == NULL) { 667 DLOG(WARNING) << "Failed to get IWebBrowser2 from client site. Error:" 668 << base::StringPrintf(" 0x%08X", hr); 669 return hr; 670 } 671 672 wchar_t bho_class_id_as_string[MAX_PATH] = {0}; 673 StringFromGUID2(CLSID_ChromeFrameBHO, bho_class_id_as_string, 674 arraysize(bho_class_id_as_string)); 675 676 base::win::ScopedComPtr<IObjectWithSite> bho; 677 hr = bho.CreateInstance(CLSID_ChromeFrameBHO, NULL, CLSCTX_INPROC_SERVER); 678 if (FAILED(hr)) { 679 NOTREACHED() << "Failed to register ChromeFrame BHO. Error:" 680 << base::StringPrintf(" 0x%08X", hr); 681 return hr; 682 } 683 684 hr = UrlMkSetSessionOption(URLMON_OPTION_USERAGENT_REFRESH, NULL, 0, 0); 685 if (FAILED(hr)) { 686 DLOG(ERROR) << "Failed to refresh user agent string from registry. " 687 << "UrlMkSetSessionOption returned " 688 << base::StringPrintf("0x%08x", hr); 689 return hr; 690 } 691 692 hr = bho->SetSite(web_browser2); 693 if (FAILED(hr)) { 694 NOTREACHED() << "ChromeFrame BHO SetSite failed. Error:" 695 << base::StringPrintf(" 0x%08X", hr); 696 return hr; 697 } 698 699 web_browser2->PutProperty(base::win::ScopedBstr(bho_class_id_as_string), 700 base::win::ScopedVariant(bho)); 701 return S_OK; 702 } 703