• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetInstance()39   static TopLevelWindowMapping* GetInstance() {
40     return Singleton<TopLevelWindowMapping>::get();
41   }
42 
43   // Add |cf_window| to the set of windows registered under |top_window|.
AddMapping(HWND top_window,HWND cf_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|.
GetInstances(HWND 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().
TopLevelWindowMapping()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.
TopWindowProc(int code,WPARAM wparam,LPARAM lparam)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 
InstallLocalWindowHook(HWND window)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 {
ActiveXCreateUrl(const GURL & parsed_url,const AttachExternalTabParams & params)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 
GetDisposition(const AttachExternalTabParams & params)132 int GetDisposition(const AttachExternalTabParams& params) {
133   return params.disposition;
134 }
135 
GetMiniContextMenuData(UINT cmd,const MiniContextMenuParams & params,GURL * referrer,GURL * url)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 
ChromeFrameActivex()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 
FinalConstruct()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 
~ChromeFrameActivex()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 
OnCreate(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)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 
OnHostMoved(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)193 LRESULT ChromeFrameActivex::OnHostMoved(UINT message, WPARAM wparam,
194                                         LPARAM lparam, BOOL& handled) {
195   Base::OnHostMoved();
196   return 0;
197 }
198 
GetContainingDocument(IHTMLDocument2 ** doc)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 
GetDocumentWindow(IHTMLWindow2 ** window)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 
OnLoad(const GURL & gurl)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 
OnLoadFailed(int error_code,const std::string & url)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 
OnMessageFromChromeFrame(const std::string & message,const std::string & origin,const std::string & target)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 
ShouldShowVersionMismatchDialog(bool is_privileged,IOleClientSite * client_site)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 
OnAutomationServerLaunchFailed(AutomationLaunchResult reason,const std::string & server_version)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 
OnChannelError()310 void ChromeFrameActivex::OnChannelError() {
311   Fire_onchannelerror();
312 }
313 
OnDraw(ATL_DRAWINFO & draw_info)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 
Load(IPropertyBag * bag,IErrorLog * error_log)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 
put_src(BSTR src)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 
IOleObject_SetClientSite(IOleClientSite * client_site)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 
GetObjectScriptId(IHTMLObjectElement * object_elem,BSTR * id)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 
GetObjectElement(IHTMLObjectElement ** element)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 
CreateScriptBlockForEvent(IHTMLElement2 * insert_after,BSTR instance_id,BSTR script,BSTR event_name)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 
FireEvent(const EventHandlers & handlers,const std::string & arg)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 
FireEvent(const EventHandlers & handlers,IDispatch * event)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 
FireEvent(const EventHandlers & handlers,IDispatch * event,BSTR target)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 
InstallTopLevelHook(IOleClientSite * client_site)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 
registerBhoIfNeeded()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