Home | History | Annotate | Download | only in chrome_frame
      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, &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 
    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, &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 
    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