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
31namespace {
32
33// Class used to maintain a mapping from top-level windows to ChromeFrameActivex
34// instances.
35class 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.
76LRESULT 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
100HHOOK 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
117namespace chrome_frame {
118std::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
132int GetDisposition(const AttachExternalTabParams& params) {
133  return params.disposition;
134}
135
136void 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
147ChromeFrameActivex::ChromeFrameActivex()
148    : chrome_wndproc_hook_(NULL),
149      attaching_to_existing_cf_tab_(false) {
150  TRACE_EVENT_BEGIN_ETW("chromeframe.createactivex", this, "");
151}
152
153HRESULT 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
163ChromeFrameActivex::~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
182LRESULT 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
193LRESULT ChromeFrameActivex::OnHostMoved(UINT message, WPARAM wparam,
194                                        LPARAM lparam, BOOL& handled) {
195  Base::OnHostMoved();
196  return 0;
197}
198
199HRESULT 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
207HRESULT 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
215void 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
225void 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
234void 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
278bool 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
299void 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
310void ChromeFrameActivex::OnChannelError() {
311  Fire_onchannelerror();
312}
313
314HRESULT 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
332STDMETHODIMP 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
399const 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
403STDMETHODIMP 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
424HRESULT 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
508HRESULT 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
525HRESULT 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
545HRESULT 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
582void 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
592void 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, &params, 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
610void 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, &params, 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
631HRESULT 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
652HRESULT 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