• 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 #ifndef CHROME_FRAME_CHROME_FRAME_ACTIVEX_BASE_H_
6 #define CHROME_FRAME_CHROME_FRAME_ACTIVEX_BASE_H_
7 
8 #include <atlbase.h>
9 #include <atlcom.h>
10 #include <atlctl.h>
11 #include <exdisp.h>
12 #include <wininet.h>
13 #include <shdeprecated.h>  // for IBrowserService2
14 #include <shlguid.h>
15 
16 #include <set>
17 #include <string>
18 #include <vector>
19 
20 #include "base/metrics/histogram.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/win/scoped_bstr.h"
25 #include "base/win/scoped_comptr.h"
26 #include "base/win/scoped_variant.h"
27 #include "chrome/app/chrome_command_ids.h"
28 #include "chrome/common/url_constants.h"
29 #include "chrome_frame/chrome_frame_plugin.h"
30 #include "chrome_frame/chrome_tab.h"
31 #include "chrome_frame/com_message_event.h"
32 #include "chrome_frame/com_type_info_holder.h"
33 #include "chrome_frame/simple_resource_loader.h"
34 #include "chrome_frame/urlmon_url_request.h"
35 #include "chrome_frame/urlmon_url_request_private.h"
36 #include "chrome_frame/utils.h"
37 #include "grit/chrome_frame_resources.h"
38 #include "grit/generated_resources.h"
39 #include "net/cookies/cookie_monster.h"
40 
41 // Connection point class to support firing IChromeFrameEvents (dispinterface).
42 template<class T>
43 class ATL_NO_VTABLE ProxyDIChromeFrameEvents
44     : public IConnectionPointImpl<T, &DIID_DIChromeFrameEvents> {
45  public:
FireMethodWithParams(ChromeFrameEventDispId dispid,const VARIANT * params,size_t num_params)46   void FireMethodWithParams(ChromeFrameEventDispId dispid,
47                             const VARIANT* params, size_t num_params) {
48     T* me = static_cast<T*>(this);
49     // We need to copy the whole vector and AddRef the sinks in case
50     // some would get disconnected as we fire methods. Note that this is not
51     // a threading issue, but a re-entrance issue, because the connection
52     // can be affected by the implementation of the sinks receiving the event.
53     me->Lock();
54     std::vector< base::win::ScopedComPtr<IUnknown> > sink_array(
55         m_vec.GetSize());
56     for (int connection = 0; connection < m_vec.GetSize(); ++connection)
57       sink_array[connection] = m_vec.GetAt(connection);
58     me->Unlock();
59 
60     for (size_t sink = 0; sink < sink_array.size(); ++sink) {
61       DIChromeFrameEvents* events =
62           static_cast<DIChromeFrameEvents*>(sink_array[sink].get());
63       if (events) {
64         DISPPARAMS disp_params = {
65             const_cast<VARIANT*>(params),
66             NULL,
67             num_params,
68             0};
69         HRESULT hr = events->Invoke(static_cast<DISPID>(dispid),
70                                     DIID_DIChromeFrameEvents,
71                                     LOCALE_USER_DEFAULT, DISPATCH_METHOD,
72                                     &disp_params, NULL, NULL, NULL);
73         DLOG_IF(ERROR, FAILED(hr)) << "invoke(" << dispid << ") failed" <<
74             base::StringPrintf("0x%08X", hr);
75       }
76     }
77   }
78 
FireMethodWithParam(ChromeFrameEventDispId dispid,const VARIANT & param)79   void FireMethodWithParam(ChromeFrameEventDispId dispid,
80                            const VARIANT& param) {
81     FireMethodWithParams(dispid, &param, 1);
82   }
83 
Fire_onload(IDispatch * event)84   void Fire_onload(IDispatch* event) {
85     VARIANT var = { VT_DISPATCH };
86     var.pdispVal = event;
87     FireMethodWithParam(CF_EVENT_DISPID_ONLOAD, var);
88   }
89 
Fire_onloaderror(IDispatch * event)90   void Fire_onloaderror(IDispatch* event) {
91     VARIANT var = { VT_DISPATCH };
92     var.pdispVal = event;
93     FireMethodWithParam(CF_EVENT_DISPID_ONLOADERROR, var);
94   }
95 
Fire_onmessage(IDispatch * event)96   void Fire_onmessage(IDispatch* event) {
97     VARIANT var = { VT_DISPATCH };
98     var.pdispVal = event;
99     FireMethodWithParam(CF_EVENT_DISPID_ONMESSAGE, var);
100   }
101 
Fire_onreadystatechanged(long readystate)102   void Fire_onreadystatechanged(long readystate) {  // NOLINT
103     VARIANT var = { VT_I4 };
104     var.lVal = readystate;
105     FireMethodWithParam(CF_EVENT_DISPID_ONREADYSTATECHANGED, var);
106   }
107 
Fire_onprivatemessage(IDispatch * event,BSTR target)108   void Fire_onprivatemessage(IDispatch* event, BSTR target) {
109     // Arguments in reverse order to the function declaration, because
110     // that's what DISPPARAMS requires.
111     VARIANT args[2] = { { VT_BSTR, }, {VT_DISPATCH, } };
112     args[0].bstrVal = target;
113     args[1].pdispVal = event;
114 
115     FireMethodWithParams(CF_EVENT_DISPID_ONPRIVATEMESSAGE,
116                          args,
117                          arraysize(args));
118   }
119 
Fire_onchannelerror()120   void Fire_onchannelerror() {  // NOLINT
121     FireMethodWithParams(CF_EVENT_DISPID_ONCHANNELERROR, NULL, 0);
122   }
123 
Fire_onclose()124   void Fire_onclose() {  // NOLINT
125     FireMethodWithParams(CF_EVENT_DISPID_ONCLOSE, NULL, 0);
126   }
127 };
128 
129 extern bool g_first_launch_by_process_;
130 
131 namespace chrome_frame {
132 // Implemented outside this file so that the header doesn't include
133 // automation_messages.h.
134 std::string ActiveXCreateUrl(const GURL& parsed_url,
135                              const AttachExternalTabParams& params);
136 int GetDisposition(const AttachExternalTabParams& params);
137 void GetMiniContextMenuData(UINT cmd,
138                             const MiniContextMenuParams& params,
139                             GURL* referrer,
140                             GURL* url);
141 }  // namespace chrome_frame
142 
143 // Common implementation for ActiveX and Active Document
144 template <class T, const CLSID& class_id>
145 class ATL_NO_VTABLE ChromeFrameActivexBase :  // NOLINT
146   public CComObjectRootEx<CComMultiThreadModel>,
147   public IOleControlImpl<T>,
148   public IOleObjectImpl<T>,
149   public IOleInPlaceActiveObjectImpl<T>,
150   public IViewObjectExImpl<T>,
151   public IOleInPlaceObjectWindowlessImpl<T>,
152   public ISupportErrorInfo,
153   public IQuickActivateImpl<T>,
154   public com_util::IProvideClassInfo2Impl<class_id,
155                                           DIID_DIChromeFrameEvents>,
156   public com_util::IDispatchImpl<IChromeFrame>,
157   public IConnectionPointContainerImpl<T>,
158   public ProxyDIChromeFrameEvents<T>,
159   public IPropertyNotifySinkCP<T>,
160   public CComCoClass<T, &class_id>,
161   public CComControl<T>,
162   public ChromeFramePlugin<T> {
163  protected:
164   typedef std::set<base::win::ScopedComPtr<IDispatch> > EventHandlers;
165   typedef ChromeFrameActivexBase<T, class_id> BasePlugin;
166 
167  public:
ChromeFrameActivexBase()168   ChromeFrameActivexBase()
169       : ready_state_(READYSTATE_UNINITIALIZED),
170       url_fetcher_(new UrlmonUrlRequestManager()),
171       failed_to_fetch_in_place_frame_(false),
172       draw_sad_tab_(false) {
173     m_bWindowOnly = TRUE;
174     url_fetcher_->set_container(static_cast<IDispatch*>(this));
175   }
176 
~ChromeFrameActivexBase()177   ~ChromeFrameActivexBase() {
178     url_fetcher_->set_container(NULL);
179   }
180 
181 DECLARE_OLEMISC_STATUS(OLEMISC_RECOMPOSEONRESIZE | OLEMISC_CANTLINKINSIDE |
182                        OLEMISC_INSIDEOUT | OLEMISC_ACTIVATEWHENVISIBLE |
183                        OLEMISC_SETCLIENTSITEFIRST)
184 
DECLARE_NOT_AGGREGATABLE(T)185 DECLARE_NOT_AGGREGATABLE(T)
186 
187 BEGIN_COM_MAP(ChromeFrameActivexBase)
188   COM_INTERFACE_ENTRY(IChromeFrame)
189   COM_INTERFACE_ENTRY(IDispatch)
190   COM_INTERFACE_ENTRY(IViewObjectEx)
191   COM_INTERFACE_ENTRY(IViewObject2)
192   COM_INTERFACE_ENTRY(IViewObject)
193   COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
194   COM_INTERFACE_ENTRY(IOleInPlaceObject)
195   COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
196   COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
197   COM_INTERFACE_ENTRY(IOleControl)
198   COM_INTERFACE_ENTRY(IOleObject)
199   COM_INTERFACE_ENTRY(ISupportErrorInfo)
200   COM_INTERFACE_ENTRY(IQuickActivate)
201   COM_INTERFACE_ENTRY(IProvideClassInfo)
202   COM_INTERFACE_ENTRY(IProvideClassInfo2)
203   COM_INTERFACE_ENTRY(IConnectionPointContainer)
204   COM_INTERFACE_ENTRY_FUNC_BLIND(0, InterfaceNotSupported)
205 END_COM_MAP()
206 
207 BEGIN_CONNECTION_POINT_MAP(T)
208   CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink)
209   CONNECTION_POINT_ENTRY(DIID_DIChromeFrameEvents)
210 END_CONNECTION_POINT_MAP()
211 
212 BEGIN_MSG_MAP(ChromeFrameActivexBase)
213   MESSAGE_HANDLER(WM_CREATE, OnCreate)
214   MESSAGE_HANDLER(WM_DOWNLOAD_IN_HOST, OnDownloadRequestInHost)
215   MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
216   CHAIN_MSG_MAP(ChromeFramePlugin<T>)
217   CHAIN_MSG_MAP(CComControl<T>)
218   DEFAULT_REFLECTION_HANDLER()
219 END_MSG_MAP()
220 
221   // IViewObjectEx
222   DECLARE_VIEW_STATUS(VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE)
223 
224   inline HRESULT IViewObject_Draw(DWORD draw_aspect, LONG index,
225       void* aspect_info, DVTARGETDEVICE* ptd, HDC info_dc, HDC dc,
226       LPCRECTL bounds, LPCRECTL win_bounds) {
227     // ATL ASSERTs if dwDrawAspect is DVASPECT_DOCPRINT, so we cheat.
228     DWORD aspect = draw_aspect;
229     if (aspect == DVASPECT_DOCPRINT)
230       aspect = DVASPECT_CONTENT;
231 
232     return CComControl<T>::IViewObject_Draw(aspect, index, aspect_info, ptd,
233         info_dc, dc, bounds, win_bounds);
234   }
235 
DECLARE_PROTECT_FINAL_CONSTRUCT()236   DECLARE_PROTECT_FINAL_CONSTRUCT()
237 
238   void SetResourceModule() {
239     SimpleResourceLoader* loader_instance = SimpleResourceLoader::GetInstance();
240     DCHECK(loader_instance);
241     HMODULE res_dll = loader_instance->GetResourceModuleHandle();
242     _AtlBaseModule.SetResourceInstance(res_dll);
243   }
244 
FinalConstruct()245   HRESULT FinalConstruct() {
246     SetResourceModule();
247 
248     if (!Initialize())
249       return E_OUTOFMEMORY;
250 
251     // Set to true if this is the first launch by this process.
252     // Used to perform one time tasks.
253     if (g_first_launch_by_process_) {
254       g_first_launch_by_process_ = false;
255       UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.IEVersion",
256                                   GetIEVersion(),
257                                   IE_INVALID,
258                                   IE_10,
259                                   IE_10 + 1);
260     }
261 
262     return S_OK;
263   }
264 
FinalRelease()265   void FinalRelease() {
266     Uninitialize();
267   }
268 
ResetUrlRequestManager()269   void ResetUrlRequestManager() {
270     url_fetcher_.reset(new UrlmonUrlRequestManager());
271   }
272 
InterfaceNotSupported(void * pv,REFIID riid,void ** ppv,DWORD dw)273   static HRESULT WINAPI InterfaceNotSupported(void* pv, REFIID riid, void** ppv,
274                                               DWORD dw) {
275 #ifndef NDEBUG
276     wchar_t buffer[64] = {0};
277     ::StringFromGUID2(riid, buffer, arraysize(buffer));
278     DVLOG(1) << "E_NOINTERFACE: " << buffer;
279 #endif
280     return E_NOINTERFACE;
281   }
282 
283   // ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)284   STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid) {
285     static const IID* interfaces[] = {
286       &IID_IChromeFrame,
287       &IID_IDispatch
288     };
289 
290     for (int i = 0; i < arraysize(interfaces); ++i) {
291       if (InlineIsEqualGUID(*interfaces[i], riid))
292         return S_OK;
293     }
294     return S_FALSE;
295   }
296 
297   // Called to draw our control when chrome hasn't been initialized.
OnDraw(ATL_DRAWINFO & draw_info)298   virtual HRESULT OnDraw(ATL_DRAWINFO& draw_info) {  // NOLINT
299     if (NULL == draw_info.prcBounds) {
300       NOTREACHED();
301       return E_FAIL;
302     }
303 
304     if (draw_sad_tab_) {
305       // TODO(tommi): Draw a proper sad tab.
306       RECT rc = {0};
307       if (draw_info.prcBounds) {
308         rc.top = draw_info.prcBounds->top;
309         rc.bottom = draw_info.prcBounds->bottom;
310         rc.left = draw_info.prcBounds->left;
311         rc.right = draw_info.prcBounds->right;
312       } else {
313         GetClientRect(&rc);
314       }
315       ::DrawTextA(draw_info.hdcDraw, ":-(", -1, &rc,
316           DT_CENTER | DT_VCENTER | DT_SINGLELINE);
317     } else {
318       // Don't draw anything.
319     }
320     return S_OK;
321   }
322 
323   // Used to setup the document_url_ member needed for completing navigation.
324   // Create external tab (possibly in incognito mode).
IOleObject_SetClientSite(IOleClientSite * client_site)325   HRESULT IOleObject_SetClientSite(IOleClientSite* client_site) {
326     // If we currently have a document site pointer, release it.
327     doc_site_.Release();
328     if (client_site) {
329       doc_site_.QueryFrom(client_site);
330     }
331 
332     if (client_site == NULL) {
333       in_place_frame_.Release();
334     }
335 
336     return CComControlBase::IOleObject_SetClientSite(client_site);
337   }
338 
HandleContextMenuCommand(UINT cmd,const MiniContextMenuParams & params)339   bool HandleContextMenuCommand(UINT cmd, const MiniContextMenuParams& params) {
340     if (cmd == IDC_ABOUT_CHROME_FRAME) {
341       int tab_handle = automation_client_->tab()->handle();
342       HostNavigate(GURL("about:version"), GURL(), NEW_WINDOW);
343       return true;
344     } else {
345       switch (cmd) {
346         case IDS_CONTENT_CONTEXT_SAVEAUDIOAS:
347         case IDS_CONTENT_CONTEXT_SAVEVIDEOAS:
348         case IDS_CONTENT_CONTEXT_SAVEIMAGEAS:
349         case IDS_CONTENT_CONTEXT_SAVELINKAS: {
350           GURL referrer, url;
351           chrome_frame::GetMiniContextMenuData(cmd, params, &referrer, &url);
352           DoFileDownloadInIE(UTF8ToWide(url.spec()).c_str());
353           return true;
354         }
355 
356         case IDC_PRINT: {
357           automation_client_->PrintTab();
358           return true;
359         }
360       }
361     }
362 
363     return false;
364   }
365 
366   // Should connections initiated by this class try to block
367   // responses served with the X-Frame-Options header?
368   // ActiveX controls genereally will want to do this,
369   // returning true, while true top-level documents
370   // (ActiveDocument servers) will not. Your specialization
371   // of this template should implement this method based on how
372   // it "feels" from a security perspective. If it's hosted in another
373   // scriptable document, return true, else false.
374   //
375   // The base implementation returns true unless we are in privileged
376   // mode, in which case we always trust our container so we return false.
is_frame_busting_enabled()377   bool is_frame_busting_enabled() const {
378     return !is_privileged();
379   }
380 
BringWebBrowserWindowToTop(IWebBrowser2 * web_browser2)381   static void BringWebBrowserWindowToTop(IWebBrowser2* web_browser2) {
382     DCHECK(web_browser2);
383     if (web_browser2) {
384       web_browser2->put_Visible(VARIANT_TRUE);
385       HWND ie_window = NULL;
386       web_browser2->get_HWND(reinterpret_cast<long*>(&ie_window));
387       ::BringWindowToTop(ie_window);
388     }
389   }
390 
391  protected:
GetProfilePath(const std::wstring & profile_name,base::FilePath * profile_path)392   virtual void GetProfilePath(const std::wstring& profile_name,
393                               base::FilePath* profile_path) {
394     bool is_IE = (lstrcmpi(profile_name.c_str(), kIexploreProfileName) == 0) ||
395                  (lstrcmpi(profile_name.c_str(), kRundllProfileName) == 0);
396     // Browsers without IDeleteBrowsingHistory in non-priv mode
397     // have their profiles moved into "Temporary Internet Files".
398     if (is_IE && GetIEVersion() < IE_8) {
399       *profile_path = GetIETemporaryFilesFolder();
400       *profile_path = profile_path->Append(L"Google Chrome Frame");
401     } else {
402       ChromeFramePlugin::GetProfilePath(profile_name, profile_path);
403     }
404     DVLOG(1) << __FUNCTION__ << ": " << profile_path->value();
405   }
406 
OnLoad(const GURL & url)407   void OnLoad(const GURL& url) {
408     if (ready_state_ < READYSTATE_COMPLETE) {
409       ready_state_ = READYSTATE_COMPLETE;
410       FireOnChanged(DISPID_READYSTATE);
411     }
412 
413     HRESULT hr = InvokeScriptFunction(onload_handler_, url.spec());
414   }
415 
OnLoadFailed(int error_code,const std::string & url)416   void OnLoadFailed(int error_code, const std::string& url) {
417     HRESULT hr = InvokeScriptFunction(onerror_handler_, url);
418   }
419 
OnMessageFromChromeFrame(const std::string & message,const std::string & origin,const std::string & target)420   void OnMessageFromChromeFrame(const std::string& message,
421                                 const std::string& origin,
422                                 const std::string& target) {
423     base::win::ScopedComPtr<IDispatch> message_event;
424     if (SUCCEEDED(CreateDomEvent("message", message, origin,
425                                  message_event.Receive()))) {
426       base::win::ScopedVariant event_var;
427       event_var.Set(static_cast<IDispatch*>(message_event));
428       InvokeScriptFunction(onmessage_handler_, event_var.AsInput());
429     }
430   }
431 
OnTabbedOut(bool reverse)432   virtual void OnTabbedOut(bool reverse) {
433     DCHECK(m_bInPlaceActive);
434 
435     HWND parent = ::GetParent(m_hWnd);
436     ::SetFocus(parent);
437     base::win::ScopedComPtr<IOleControlSite> control_site;
438     control_site.QueryFrom(m_spClientSite);
439     if (control_site)
440       control_site->OnFocus(FALSE);
441   }
442 
OnOpenURL(const GURL & url_to_open,const GURL & referrer,int open_disposition)443   virtual void OnOpenURL(const GURL& url_to_open,
444                          const GURL& referrer, int open_disposition) {
445     HostNavigate(url_to_open, referrer, open_disposition);
446   }
447 
448   // Called when Chrome has decided that a request needs to be treated as a
449   // download.  The caller will be the UrlRequest worker thread.
450   // The worker thread will block while we process the request and take
451   // ownership of the request object.
452   // There's room for improvement here and also see todo below.
OnDownloadRequestInHost(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)453   LPARAM OnDownloadRequestInHost(UINT message, WPARAM wparam, LPARAM lparam,
454                                  BOOL& handled) {
455     ChromeFrameUrl cf_url;
456     cf_url.Parse(UTF8ToWide(GetDocumentUrl()));
457 
458     // Always issue the download request in a new window to ensure that the
459     // currently loaded ChromeFrame document does not inadvartently see an
460     // unload request. This runs javascript unload handlers on the page which
461     // renders the page non functional.
462     VARIANT flags = { VT_I4 };
463     V_I4(&flags) = navNoHistory;
464     if (!cf_url.attach_to_external_tab())
465       V_I4(&flags) |= navOpenInNewWindow;
466 
467     DownloadInHostParams* download_params =
468         reinterpret_cast<DownloadInHostParams*>(wparam);
469     DCHECK(download_params);
470     // TODO(tommi): It looks like we might have to switch the request object
471     // into a pass-through request object and serve up any thus far received
472     // content and headers to IE in order to prevent what can currently happen
473     // which is reissuing requests and turning POST into GET.
474     if (download_params->moniker) {
475       NavigateBrowserToMoniker(
476           doc_site_, download_params->moniker,
477           UTF8ToWide(download_params->request_headers).c_str(),
478           download_params->bind_ctx, NULL, download_params->post_data,
479           &flags);
480     }
481     delete download_params;
482     return TRUE;
483   }
484 
OnAttachExternalTab(const AttachExternalTabParams & params)485   virtual void OnAttachExternalTab(const AttachExternalTabParams& params) {
486     GURL current_url(static_cast<BSTR>(url_));
487     std::string url = chrome_frame::ActiveXCreateUrl(current_url, params);
488     // Pass the current document url as the referrer for the new navigation.
489     HostNavigate(GURL(url), current_url, chrome_frame::GetDisposition(params));
490   }
491 
OnHandleContextMenu(const ContextMenuModel & menu_model,int align_flags,const MiniContextMenuParams & params)492   virtual void OnHandleContextMenu(const ContextMenuModel& menu_model,
493                                    int align_flags,
494                                    const MiniContextMenuParams& params) {
495     scoped_refptr<BasePlugin> ref(this);
496     ChromeFramePlugin<T>::OnHandleContextMenu(menu_model, align_flags, params);
497   }
498 
OnCreate(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)499   LRESULT OnCreate(UINT message, WPARAM wparam, LPARAM lparam,
500                    BOOL& handled) {  // NO_LINT
501     ModifyStyle(0, WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0);
502     url_fetcher_->put_notification_window(m_hWnd);
503     if (automation_client_.get()) {
504       automation_client_->SetParentWindow(m_hWnd);
505     } else {
506       NOTREACHED() << "No automation server";
507       return -1;
508     }
509     // Only fire the 'interactive' ready state if we aren't there already.
510     if (ready_state_ < READYSTATE_INTERACTIVE) {
511       ready_state_ = READYSTATE_INTERACTIVE;
512       FireOnChanged(DISPID_READYSTATE);
513     }
514     return 0;
515   }
516 
OnDestroy(UINT message,WPARAM wparam,LPARAM lparam,BOOL & handled)517   LRESULT OnDestroy(UINT message, WPARAM wparam, LPARAM lparam,
518                     BOOL& handled) {  // NO_LINT
519     DVLOG(1) << __FUNCTION__;
520     return 0;
521   }
522 
523   // ChromeFrameDelegate override
OnAutomationServerReady()524   virtual void OnAutomationServerReady() {
525     draw_sad_tab_ = false;
526     ChromeFramePlugin<T>::OnAutomationServerReady();
527 
528     ready_state_ = READYSTATE_COMPLETE;
529     FireOnChanged(DISPID_READYSTATE);
530   }
531 
532   // ChromeFrameDelegate override
OnAutomationServerLaunchFailed(AutomationLaunchResult reason,const std::string & server_version)533   virtual void OnAutomationServerLaunchFailed(
534       AutomationLaunchResult reason, const std::string& server_version) {
535     DVLOG(1) << __FUNCTION__;
536     if (reason == AUTOMATION_SERVER_CRASHED)
537       draw_sad_tab_ = true;
538 
539     ready_state_ = READYSTATE_UNINITIALIZED;
540     FireOnChanged(DISPID_READYSTATE);
541   }
542 
OnCloseTab()543   virtual void OnCloseTab() {
544     Fire_onclose();
545   }
546 
547   // Overridden to take advantage of readystate prop changes and send those
548   // to potential listeners.
FireOnChanged(DISPID dispid)549   HRESULT FireOnChanged(DISPID dispid) {
550     if (dispid == DISPID_READYSTATE) {
551       Fire_onreadystatechanged(ready_state_);
552     }
553     return __super::FireOnChanged(dispid);
554   }
555 
556   // IChromeFrame
557   // Property getter/setters for the src attribute, which contains a URL.
558   // The ChromeFrameActivex control initiates navigation to this URL
559   // when instantiated.
STDMETHOD(get_src)560   STDMETHOD(get_src)(BSTR* src) {
561     if (NULL == src) {
562       return E_POINTER;
563     }
564 
565     *src = SysAllocString(url_);
566     return S_OK;
567   }
568 
STDMETHOD(put_src)569   STDMETHOD(put_src)(BSTR src) {
570     if (src == NULL)
571       return E_INVALIDARG;
572 
573     // Switch the src to UTF8 and try to expand to full URL
574     std::string src_utf8;
575     WideToUTF8(src, SysStringLen(src), &src_utf8);
576     std::string full_url = ResolveURL(GetDocumentUrl(), src_utf8);
577 
578     // We can initiate navigation here even if ready_state is not complete.
579     // We do not have to set proxy, and AutomationClient will take care
580     // of navigation just after CreateExternalTab is done.
581     if (!automation_client_->InitiateNavigation(full_url,
582                                                 GetDocumentUrl(),
583                                                 this)) {
584       // TODO(robertshield): Make InitiateNavigation return more useful
585       // error information.
586       return E_INVALIDARG;
587     }
588 
589     // Save full URL in BSTR member
590     url_.Reset(::SysAllocString(UTF8ToWide(full_url).c_str()));
591 
592     return S_OK;
593   }
594 
STDMETHOD(get_onload)595   STDMETHOD(get_onload)(VARIANT* onload_handler) {
596     if (NULL == onload_handler)
597       return E_INVALIDARG;
598 
599     *onload_handler = onload_handler_.Copy();
600 
601     return S_OK;
602   }
603 
604   // Property setter for the onload attribute, which contains a
605   // javascript function to be invoked on successful navigation.
STDMETHOD(put_onload)606   STDMETHOD(put_onload)(VARIANT onload_handler) {
607     if (V_VT(&onload_handler) != VT_DISPATCH) {
608       DLOG(WARNING) << "Invalid onload handler type: "
609                     << onload_handler.vt
610                     << " specified";
611       return E_INVALIDARG;
612     }
613 
614     onload_handler_ = onload_handler;
615 
616     return S_OK;
617   }
618 
619   // Property getter/setters for the onloaderror attribute, which contains a
620   // javascript function to be invoked on navigation failure.
STDMETHOD(get_onloaderror)621   STDMETHOD(get_onloaderror)(VARIANT* onerror_handler) {
622     if (NULL == onerror_handler)
623       return E_INVALIDARG;
624 
625     *onerror_handler = onerror_handler_.Copy();
626 
627     return S_OK;
628   }
629 
STDMETHOD(put_onloaderror)630   STDMETHOD(put_onloaderror)(VARIANT onerror_handler) {
631     if (V_VT(&onerror_handler) != VT_DISPATCH) {
632       DLOG(WARNING) << "Invalid onloaderror handler type: "
633                     << onerror_handler.vt
634                     << " specified";
635       return E_INVALIDARG;
636     }
637 
638     onerror_handler_ = onerror_handler;
639 
640     return S_OK;
641   }
642 
643   // Property getter/setters for the onmessage attribute, which contains a
644   // javascript function to be invoked when we receive a message from the
645   // chrome frame.
STDMETHOD(put_onmessage)646   STDMETHOD(put_onmessage)(VARIANT onmessage_handler) {
647     if (V_VT(&onmessage_handler) != VT_DISPATCH) {
648       DLOG(WARNING) << "Invalid onmessage handler type: "
649                     << onmessage_handler.vt
650                     << " specified";
651       return E_INVALIDARG;
652     }
653 
654     onmessage_handler_ = onmessage_handler;
655 
656     return S_OK;
657   }
658 
STDMETHOD(get_onmessage)659   STDMETHOD(get_onmessage)(VARIANT* onmessage_handler) {
660     if (NULL == onmessage_handler)
661       return E_INVALIDARG;
662 
663     *onmessage_handler = onmessage_handler_.Copy();
664 
665     return S_OK;
666   }
667 
STDMETHOD(get_readyState)668   STDMETHOD(get_readyState)(long* ready_state) {  // NOLINT
669     DVLOG(1) << __FUNCTION__;
670     DCHECK(ready_state);
671 
672     if (!ready_state)
673       return E_INVALIDARG;
674 
675     *ready_state = ready_state_;
676 
677     return S_OK;
678   }
679 
680   // Property getter/setters for use_chrome_network flag. This flag
681   // indicates if chrome network stack is to be used for fetching
682   // network requests.
STDMETHOD(get_useChromeNetwork)683   STDMETHOD(get_useChromeNetwork)(VARIANT_BOOL* use_chrome_network) {
684     if (!use_chrome_network)
685       return E_INVALIDARG;
686 
687     *use_chrome_network =
688         automation_client_->use_chrome_network() ? VARIANT_TRUE : VARIANT_FALSE;
689     return S_OK;
690   }
691 
STDMETHOD(put_useChromeNetwork)692   STDMETHOD(put_useChromeNetwork)(VARIANT_BOOL use_chrome_network) {
693     if (!is_privileged()) {
694       DLOG(ERROR) << "Attempt to set useChromeNetwork in non-privileged mode";
695       return E_ACCESSDENIED;
696     }
697 
698     automation_client_->set_use_chrome_network(
699         (VARIANT_FALSE != use_chrome_network));
700     return S_OK;
701   }
702 
703   // Posts a message to the chrome frame.
STDMETHOD(postMessage)704   STDMETHOD(postMessage)(BSTR message, VARIANT target) {
705     if (NULL == message) {
706       return E_INVALIDARG;
707     }
708 
709     if (!automation_client_.get())
710       return E_FAIL;
711 
712     std::string utf8_target;
713     if (target.vt == VT_BSTR) {
714       int len = ::SysStringLen(target.bstrVal);
715       if (len == 1 && target.bstrVal[0] == L'*') {
716         utf8_target = "*";
717       } else {
718         GURL resolved(target.bstrVal);
719         if (!resolved.is_valid()) {
720           Error(L"Unable to parse the specified target URL.");
721           return E_INVALIDARG;
722         }
723 
724         utf8_target = resolved.spec();
725       }
726     } else {
727       utf8_target = "*";
728     }
729 
730     std::string utf8_message;
731     WideToUTF8(message, ::SysStringLen(message), &utf8_message);
732 
733     GURL url(GURL(document_url_).GetOrigin());
734     std::string origin(url.is_empty() ? "null" : url.spec());
735     if (!automation_client_->ForwardMessageFromExternalHost(utf8_message,
736                                                             origin,
737                                                             utf8_target)) {
738       Error(L"Failed to post message to chrome frame");
739       return E_FAIL;
740     }
741 
742     return S_OK;
743   }
744 
STDMETHOD(addEventListener)745   STDMETHOD(addEventListener)(BSTR event_type, IDispatch* listener,
746                               VARIANT use_capture) {
747     EventHandlers* handlers = NULL;
748     HRESULT hr = GetHandlersForEvent(event_type, &handlers);
749     if (FAILED(hr))
750       return hr;
751 
752     DCHECK(handlers != NULL);
753 
754     handlers->insert(base::win::ScopedComPtr<IDispatch>(listener));
755 
756     return hr;
757   }
758 
STDMETHOD(removeEventListener)759   STDMETHOD(removeEventListener)(BSTR event_type, IDispatch* listener,
760                                  VARIANT use_capture) {
761     EventHandlers* handlers = NULL;
762     HRESULT hr = GetHandlersForEvent(event_type, &handlers);
763     if (FAILED(hr))
764       return hr;
765 
766     DCHECK(handlers != NULL);
767     handlers->erase(base::win::ScopedComPtr<IDispatch>(listener));
768 
769     return hr;
770   }
771 
STDMETHOD(get_version)772   STDMETHOD(get_version)(BSTR* version) {
773     if (!automation_client_.get()) {
774       NOTREACHED();
775       return E_FAIL;
776     }
777 
778     if (version == NULL) {
779       return E_INVALIDARG;
780     }
781 
782     *version = SysAllocString(automation_client_->GetVersion().c_str());
783     return S_OK;
784   }
785 
STDMETHOD(postPrivateMessage)786   STDMETHOD(postPrivateMessage)(BSTR message, BSTR origin, BSTR target) {
787     if (NULL == message)
788       return E_INVALIDARG;
789 
790     if (!is_privileged()) {
791       DLOG(ERROR) << "Attempt to postPrivateMessage in non-privileged mode";
792       return E_ACCESSDENIED;
793     }
794 
795     DCHECK(automation_client_.get());
796     std::string utf8_message, utf8_origin, utf8_target;
797     WideToUTF8(message, ::SysStringLen(message), &utf8_message);
798     WideToUTF8(origin, ::SysStringLen(origin), &utf8_origin);
799     WideToUTF8(target, ::SysStringLen(target), &utf8_target);
800 
801     if (!automation_client_->ForwardMessageFromExternalHost(utf8_message,
802                                                             utf8_origin,
803                                                             utf8_target)) {
804       Error(L"Failed to post message to chrome frame");
805       return E_FAIL;
806     }
807 
808     return S_OK;
809   }
810 
STDMETHOD(installExtension)811   STDMETHOD(installExtension)(BSTR crx_path) {
812     NOTREACHED();  // Deprecated.
813     return E_NOTIMPL;
814   }
815 
STDMETHOD(loadExtension)816   STDMETHOD(loadExtension)(BSTR path) {
817     NOTREACHED();  // Deprecated.
818     return E_NOTIMPL;
819   }
820 
STDMETHOD(getEnabledExtensions)821   STDMETHOD(getEnabledExtensions)() {
822     NOTREACHED();  // Deprecated.
823     return E_NOTIMPL;
824   }
825 
STDMETHOD(registerBhoIfNeeded)826   STDMETHOD(registerBhoIfNeeded)() {
827     return E_NOTIMPL;
828   }
829 
830   // Returns the vector of event handlers for a given event (e.g. "load").
831   // If the event type isn't recognized, the function fills in a descriptive
832   // error (IErrorInfo) and returns E_INVALIDARG.
GetHandlersForEvent(BSTR event_type,EventHandlers ** handlers)833   HRESULT GetHandlersForEvent(BSTR event_type, EventHandlers** handlers) {
834     DCHECK(handlers != NULL);
835 
836     // TODO(tommi): make these if() statements data-driven.
837     HRESULT hr = S_OK;
838     const wchar_t* event_type_end = event_type + ::SysStringLen(event_type);
839     if (LowerCaseEqualsASCII(event_type, event_type_end, "message")) {
840       *handlers = &onmessage_;
841     } else if (LowerCaseEqualsASCII(event_type, event_type_end, "load")) {
842       *handlers = &onload_;
843     } else if (LowerCaseEqualsASCII(event_type, event_type_end, "loaderror")) {
844       *handlers = &onloaderror_;
845     } else if (LowerCaseEqualsASCII(event_type, event_type_end,
846                                     "readystatechanged")) {
847       *handlers = &onreadystatechanged_;
848     } else if (LowerCaseEqualsASCII(event_type, event_type_end,
849                                     "privatemessage")) {
850       // This event handler is only available in privileged mode.
851       if (is_privileged()) {
852         *handlers = &onprivatemessage_;
853       } else {
854         Error("Event type 'privatemessage' is privileged");
855         hr = E_ACCESSDENIED;
856       }
857     } else if (LowerCaseEqualsASCII(event_type, event_type_end,
858                                     "extensionready")) {
859       // This event handler is only available in privileged mode.
860       if (is_privileged()) {
861         *handlers = &onextensionready_;
862       } else {
863         Error("Event type 'extensionready' is privileged");
864         hr = E_ACCESSDENIED;
865       }
866     } else {
867       Error(base::StringPrintf(
868           "Event type '%ls' not found", event_type).c_str());
869       hr = E_INVALIDARG;
870     }
871 
872     return hr;
873   }
874 
875   // Creates a new event object that supports the |data| property.
876   // Note: you should supply an empty string for |origin| unless you're
877   // creating a "message" event.
CreateDomEvent(const std::string & event_type,const std::string & data,const std::string & origin,IDispatch ** event)878   HRESULT CreateDomEvent(const std::string& event_type, const std::string& data,
879                          const std::string& origin, IDispatch** event) {
880     DCHECK(event_type.length() > 0);  // NOLINT
881     DCHECK(event != NULL);
882 
883     CComObject<ComMessageEvent>* ev = NULL;
884     HRESULT hr = CComObject<ComMessageEvent>::CreateInstance(&ev);
885     if (SUCCEEDED(hr)) {
886       ev->AddRef();
887 
888       base::win::ScopedComPtr<IOleContainer> container;
889       m_spClientSite->GetContainer(container.Receive());
890       if (ev->Initialize(container, data, origin, event_type)) {
891         *event = ev;
892       } else {
893         NOTREACHED() << "event->Initialize";
894         ev->Release();
895         hr = E_UNEXPECTED;
896       }
897     }
898 
899     return hr;
900   }
901 
902   // Helper function to execute a function on a script IDispatch interface.
InvokeScriptFunction(const VARIANT & script_object,const std::string & param)903   HRESULT InvokeScriptFunction(const VARIANT& script_object,
904                                const std::string& param) {
905     base::win::ScopedVariant script_arg(UTF8ToWide(param.c_str()).c_str());
906     return InvokeScriptFunction(script_object, script_arg.AsInput());
907   }
908 
InvokeScriptFunction(const VARIANT & script_object,VARIANT * param)909   HRESULT InvokeScriptFunction(const VARIANT& script_object, VARIANT* param) {
910     return InvokeScriptFunction(script_object, param, 1);
911   }
912 
InvokeScriptFunction(const VARIANT & script_object,VARIANT * params,int param_count)913   HRESULT InvokeScriptFunction(const VARIANT& script_object, VARIANT* params,
914                                int param_count) {
915     DCHECK_GE(param_count, 0);
916     DCHECK(params);
917 
918     if (V_VT(&script_object) != VT_DISPATCH ||
919         script_object.pdispVal == NULL) {
920       return S_FALSE;
921     }
922 
923     CComPtr<IDispatch> script(script_object.pdispVal);
924     HRESULT hr = script.InvokeN(static_cast<DISPID>(DISPID_VALUE),
925                                 params,
926                                 param_count);
927     // 0x80020101 == SCRIPT_E_REPORTED.
928     // When the script we're invoking has an error, we get this error back.
929     DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101) << "Failed to invoke script";
930     return hr;
931   }
932 
933   // Gives the browser a chance to handle an accelerator that was
934   // sent to the out of proc chromium instance.
935   // Returns S_OK iff the accelerator was handled by the browser.
AllowFrameToTranslateAccelerator(const MSG & msg)936   HRESULT AllowFrameToTranslateAccelerator(const MSG& msg) {
937     static const int kMayTranslateAcceleratorOffset = 0x5c;
938     // Although IBrowserService2 is officially deprecated, it's still alive
939     // and well in IE7 and earlier.  We have to use it here to correctly give
940     // the browser a chance to handle keyboard shortcuts.
941     // This happens automatically for activex components that have windows that
942     // belong to the current thread.  In that circumstance IE owns the message
943     // loop and can walk the line of components allowing each participant the
944     // chance to handle the keystroke and eventually falls back to
945     // v_MayTranslateAccelerator.  However in our case, the message loop is
946     // owned by the out-of-proc chromium instance so IE doesn't have a chance to
947     // fall back on its default behavior.  Instead we give IE a chance to
948     // handle the shortcut here.
949     MSG accel_message = msg;
950     accel_message.hwnd = ::GetParent(m_hWnd);
951     HRESULT hr = S_FALSE;
952     base::win::ScopedComPtr<IBrowserService2> bs2;
953 
954     // For non-IE containers, we use the standard IOleInPlaceFrame contract
955     // (which IE does not support). For IE, we try to use IBrowserService2,
956     // but need special handling for IE8 (see below).
957     //
958     // We try to cache an IOleInPlaceFrame for our site.  If we fail, we don't
959     // retry, and we fall back to the IBrowserService2 and PostMessage
960     // approaches below.
961     if (!in_place_frame_ && !failed_to_fetch_in_place_frame_) {
962       base::win::ScopedComPtr<IOleInPlaceUIWindow> dummy_ui_window;
963       RECT dummy_pos_rect = {0};
964       RECT dummy_clip_rect = {0};
965       OLEINPLACEFRAMEINFO dummy_frame_info = {0};
966       if (!m_spInPlaceSite ||
967           FAILED(m_spInPlaceSite->GetWindowContext(in_place_frame_.Receive(),
968                                                    dummy_ui_window.Receive(),
969                                                    &dummy_pos_rect,
970                                                    &dummy_clip_rect,
971                                                    &dummy_frame_info))) {
972         failed_to_fetch_in_place_frame_ = true;
973       }
974     }
975 
976     // The IBrowserService2 code below (second conditional) explicitly checks
977     // for whether the IBrowserService2::v_MayTranslateAccelerator function is
978     // valid. On IE8 there is one vtable ieframe!c_ImpostorBrowserService2Vtbl
979     // where this function entry is NULL which leads to a crash. We don't know
980     // under what circumstances this vtable is actually used though.
981     if (in_place_frame_) {
982       hr = in_place_frame_->TranslateAccelerator(&accel_message, 0);
983     } else if (S_OK == DoQueryService(
984         SID_STopLevelBrowser, m_spInPlaceSite,
985         bs2.Receive()) && bs2.get() &&
986         *(*(reinterpret_cast<void***>(bs2.get())) +
987         kMayTranslateAcceleratorOffset)) {
988       hr = bs2->v_MayTranslateAccelerator(&accel_message);
989     } else {
990       // IE8 doesn't support IBrowserService2 unless you enable a special,
991       // undocumented flag with CoInternetSetFeatureEnabled and even then,
992       // the object you get back implements only a couple of methods of
993       // that interface... all the other entries in the vtable are NULL.
994       // In addition, the class that implements it is called
995       // ImpostorBrowserService2 :)
996       // IE8 does have a new interface though, presumably called
997       // ITabBrowserService or something that can be abbreviated to TBS.
998       // That interface has a method, TranslateAcceleratorTBS that does
999       // call the root MayTranslateAccelerator function, but alas the
1000       // first argument to MayTranslateAccelerator is hard coded to FALSE
1001       // which means that global accelerators are not handled and we're
1002       // out of luck.
1003       // A third thing that's notable with regards to IE8 is that
1004       // none of the *MayTranslate* functions exist in a vtable inside
1005       // ieframe.dll.  I checked this by scanning for the address of
1006       // those functions inside the dll and found none, which means that
1007       // all calls to those functions are relative.
1008       // So, for IE8 in certain cases, and for other containers that may
1009       // support neither IOleInPlaceFrame or IBrowserService2 our approach
1010       // is very simple.  Just post the message to our parent window and IE
1011       // will pick it up if it's an accelerator. We won't know for sure if
1012       // the browser handled the keystroke or not.
1013       ::PostMessage(accel_message.hwnd, accel_message.message,
1014                     accel_message.wParam, accel_message.lParam);
1015     }
1016 
1017     return hr;
1018   }
1019 
OnAcceleratorPressed(const MSG & accel_message)1020   virtual void OnAcceleratorPressed(const MSG& accel_message) {
1021     DCHECK(m_spInPlaceSite != NULL);
1022     // Allow our host a chance to handle the accelerator.
1023     // This catches things like Ctrl+F, Ctrl+O etc, but not browser
1024     // accelerators such as F11, Ctrl+T etc.
1025     // (see AllowFrameToTranslateAccelerator for those).
1026     HRESULT hr = TranslateAccelerator(const_cast<MSG*>(&accel_message));
1027     if (hr != S_OK)
1028       hr = AllowFrameToTranslateAccelerator(accel_message);
1029 
1030     DVLOG(1) << __FUNCTION__ << " browser response: "
1031              << base::StringPrintf("0x%08x", hr);
1032 
1033     if (hr != S_OK) {
1034       // The WM_SYSKEYDOWN/WM_SYSKEYUP messages are not processed by the
1035       // IOleControlSite and IBrowserService2::v_MayTranslateAccelerator
1036       // implementations. We need to understand this better. That is for
1037       // another day. For now we just post these messages back to the parent
1038       // which forwards it off to the frame. This should not cause major
1039       // grief for Chrome as it does not need to handle WM_SYSKEY* messages in
1040       // in ChromeFrame mode.
1041       // TODO(iyengar)
1042       // Understand and fix WM_SYSCHAR handling
1043       // We should probably unify the accelerator handling for the active
1044       // document and the activex.
1045       if (accel_message.message == WM_SYSCHAR ||
1046           accel_message.message == WM_SYSKEYDOWN ||
1047           accel_message.message == WM_SYSKEYUP) {
1048         ::PostMessage(GetParent(), accel_message.message, accel_message.wParam,
1049                       accel_message.lParam);
1050         return;
1051       }
1052     }
1053     // Last chance to handle the keystroke is to pass it to chromium.
1054     // We do this last partially because there's no way for us to tell if
1055     // chromium actually handled the keystroke, but also since the browser
1056     // should have first dibs anyway.
1057     if (hr != S_OK && automation_client_.get()) {
1058       TabProxy* tab = automation_client_->tab();
1059       if (tab) {
1060         tab->ProcessUnhandledAccelerator(accel_message);
1061       }
1062     }
1063   }
1064 
1065  protected:
HostNavigate(const GURL & url_to_open,const GURL & referrer,int open_disposition)1066   void HostNavigate(const GURL& url_to_open,
1067                     const GURL& referrer, int open_disposition) {
1068     base::win::ScopedComPtr<IWebBrowser2> web_browser2;
1069     DoQueryService(SID_SWebBrowserApp, m_spClientSite, web_browser2.Receive());
1070     if (!web_browser2) {
1071       NOTREACHED() << "Failed to retrieve IWebBrowser2 interface";
1072       return;
1073     }
1074     base::win::ScopedVariant url;
1075     // Check to see if the URL uses a "view-source:" prefix, if so, open it
1076     // using chrome frame full tab mode by using 'cf:' protocol handler.
1077     // Also change the disposition to NEW_WINDOW since IE6 doesn't have tabs.
1078     if (url_to_open.has_scheme() &&
1079         (url_to_open.SchemeIs(content::kViewSourceScheme) ||
1080         url_to_open.SchemeIs(chrome::kAboutScheme))) {
1081       std::wstring chrome_url;
1082       chrome_url.append(kChromeProtocolPrefix);
1083       chrome_url.append(UTF8ToWide(url_to_open.spec()));
1084       url.Set(chrome_url.c_str());
1085       open_disposition = NEW_WINDOW;
1086     } else {
1087       url.Set(UTF8ToWide(url_to_open.spec()).c_str());
1088     }
1089 
1090     VARIANT flags = { VT_I4 };
1091     V_I4(&flags) = 0;
1092 
1093     IEVersion ie_version = GetIEVersion();
1094     DCHECK(ie_version != NON_IE && ie_version != IE_UNSUPPORTED);
1095     // Since IE6 doesn't support tabs, so we just use window instead.
1096     if (ie_version == IE_6) {
1097       if (open_disposition == NEW_FOREGROUND_TAB ||
1098           open_disposition == NEW_BACKGROUND_TAB ||
1099           open_disposition == NEW_WINDOW ||
1100           open_disposition == NEW_POPUP) {
1101         V_I4(&flags) = navOpenInNewWindow;
1102       } else if (open_disposition != CURRENT_TAB) {
1103         NOTREACHED() << "Unsupported open disposition in IE6";
1104       }
1105     } else {
1106       switch (open_disposition) {
1107         case NEW_FOREGROUND_TAB:
1108           V_I4(&flags) = navOpenInNewTab;
1109           break;
1110         case NEW_BACKGROUND_TAB:
1111           V_I4(&flags) = navOpenInBackgroundTab;
1112           break;
1113         case NEW_WINDOW:
1114         case NEW_POPUP:
1115           V_I4(&flags) = navOpenInNewWindow;
1116           break;
1117         default:
1118           break;
1119       }
1120     }
1121 
1122     // TODO(sanjeevr): The navOpenInNewWindow flag causes IE to open this
1123     // in a new window ONLY if the user has specified
1124     // "Always open popups in a new window". Otherwise it opens in a new tab.
1125     // We need to investigate more and see if we can force IE to display the
1126     // link in a new window. MSHTML uses the below code to force an open in a
1127     // new window. But this logic also fails for us. Perhaps this flag is not
1128     // honoured if the ActiveDoc is not MSHTML.
1129     // Even the HLNF_DISABLEWINDOWRESTRICTIONS flag did not work.
1130     // Start of MSHTML-like logic.
1131     // CComQIPtr<ITargetFramePriv2> target_frame = web_browser2;
1132     // if (target_frame) {
1133     //   CComPtr<IUri> uri;
1134     //   CreateUri(UTF8ToWide(open_url_command->url_.spec()).c_str(),
1135     //             Uri_CREATE_IE_SETTINGS, 0, &uri);
1136     //   CComPtr<IBindCtx> bind_ctx;
1137     //   CreateBindCtx(0, &bind_ctx);
1138     //   target_frame->AggregatedNavigation2(
1139     //       HLNF_TRUSTFIRSTDOWNLOAD|HLNF_OPENINNEWWINDOW, bind_ctx, NULL,
1140     //       L"No_Name", uri, L"");
1141     // }
1142     // End of MSHTML-like logic
1143     VARIANT empty = base::win::ScopedVariant::kEmptyVariant;
1144     base::win::ScopedVariant http_headers;
1145 
1146     if (referrer.is_valid()) {
1147       std::wstring referrer_header = L"Referer: ";
1148       referrer_header += UTF8ToWide(referrer.spec());
1149       referrer_header += L"\r\n\r\n";
1150       http_headers.Set(referrer_header.c_str());
1151     }
1152 
1153     // IE6 does not support tabs. If Chrome sent us a window open request
1154     // indicating that the navigation needs to occur in a foreground tab or
1155     // a popup window, then we need to ensure that the new window in IE6 is
1156     // brought to the foreground.
1157     if (ie_version == IE_6) {
1158       ChromeFrameUrl cf_url;
1159       cf_url.Parse(static_cast<BSTR>(url_));
1160 
1161       if (cf_url.attach_to_external_tab() &&
1162           (cf_url.disposition() == NEW_FOREGROUND_TAB ||
1163            cf_url.disposition() == NEW_POPUP)) {
1164         BringWebBrowserWindowToTop(web_browser2);
1165       }
1166     }
1167 
1168     HRESULT hr = web_browser2->Navigate2(url.AsInput(), &flags, &empty, &empty,
1169                                          http_headers.AsInput());
1170     // If the current window is a popup window then attempting to open a new
1171     // tab for the navigation will fail. We attempt to issue the navigation in
1172     // a new window in this case.
1173     // http://msdn.microsoft.com/en-us/library/aa752133(v=vs.85).aspx
1174     if (FAILED(hr) && V_I4(&flags) != navOpenInNewWindow) {
1175       V_I4(&flags) = navOpenInNewWindow;
1176       hr = web_browser2->Navigate2(url.AsInput(), &flags, &empty, &empty,
1177                                    http_headers.AsInput());
1178       DLOG_IF(ERROR, FAILED(hr))
1179           << "Navigate2 failed with error: "
1180           << base::StringPrintf("0x%08X", hr);
1181     }
1182   }
1183 
InitializeAutomationSettings()1184   void InitializeAutomationSettings() {
1185     static const wchar_t kHandleTopLevelRequests[] = L"HandleTopLevelRequests";
1186     static const wchar_t kUseChromeNetworking[] = L"UseChromeNetworking";
1187 
1188     // Query and assign the top-level-request routing, and host networking
1189     // settings from the registry.
1190     bool top_level_requests = GetConfigBool(true, kHandleTopLevelRequests);
1191     bool chrome_network = GetConfigBool(false, kUseChromeNetworking);
1192     automation_client_->set_handle_top_level_requests(top_level_requests);
1193     automation_client_->set_use_chrome_network(chrome_network);
1194   }
1195 
1196   base::win::ScopedBstr url_;
1197   base::win::ScopedComPtr<IOleDocumentSite> doc_site_;
1198 
1199   // If false, we tried but failed to fetch an IOleInPlaceFrame from our host.
1200   // Cached here so we don't try to fetch it every time if we keep failing.
1201   bool failed_to_fetch_in_place_frame_;
1202   bool draw_sad_tab_;
1203 
1204   base::win::ScopedComPtr<IOleInPlaceFrame> in_place_frame_;
1205 
1206   // For more information on the ready_state_ property see:
1207   // http://msdn.microsoft.com/en-us/library/aa768179(VS.85).aspx#
1208   READYSTATE ready_state_;
1209 
1210   // The following members contain IDispatch interfaces representing the
1211   // onload/onerror/onmessage handlers on the page.
1212   base::win::ScopedVariant onload_handler_;
1213   base::win::ScopedVariant onerror_handler_;
1214   base::win::ScopedVariant onmessage_handler_;
1215 
1216   EventHandlers onmessage_;
1217   EventHandlers onloaderror_;
1218   EventHandlers onload_;
1219   EventHandlers onreadystatechanged_;
1220   EventHandlers onprivatemessage_;
1221   EventHandlers onextensionready_;
1222 
1223   // Handle network requests when host network stack is used. Passed to the
1224   // automation client on initialization.
1225   scoped_ptr<UrlmonUrlRequestManager> url_fetcher_;
1226 };
1227 
1228 #endif  // CHROME_FRAME_CHROME_FRAME_ACTIVEX_BASE_H_
1229