OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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 |
| 12 // Copied min/max defs from windows headers to appease atlimage.h. |
| 13 // TODO(slightlyoff): Figure out of more recent platform SDK's (> 6.1) |
| 14 // undo the janky "#define NOMINMAX" train wreck. See: |
| 15 // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?Feedback
ID=100703 |
| 16 #ifndef max |
| 17 #define max(a,b) (((a) > (b)) ? (a) : (b)) |
| 18 #endif |
| 19 #ifndef min |
| 20 #define min(a,b) (((a) < (b)) ? (a) : (b)) |
| 21 #endif |
| 22 #include <atlimage.h> |
| 23 #undef max |
| 24 #undef min |
| 25 |
| 26 #include <shdeprecated.h> // for IBrowserService2 |
| 27 #include <shlguid.h> |
| 28 |
| 29 #include <set> |
| 30 #include <string> |
| 31 |
| 32 #include "base/histogram.h" |
| 33 #include "base/scoped_bstr_win.h" |
| 34 #include "base/scoped_comptr_win.h" |
| 35 #include "base/scoped_variant_win.h" |
| 36 #include "base/string_util.h" |
| 37 #include "grit/chrome_frame_resources.h" |
| 38 #include "grit/chrome_frame_strings.h" |
| 39 #include "chrome_frame/chrome_frame_plugin.h" |
| 40 #include "chrome_frame/com_type_info_holder.h" |
| 41 #include "chrome_frame/urlmon_url_request.h" |
| 42 |
| 43 // Include without path to make GYP build see it. |
| 44 #include "chrome_tab.h" // NOLINT |
| 45 |
| 46 // Connection point class to support firing IChromeFrameEvents (dispinterface). |
| 47 template<class T> |
| 48 class ATL_NO_VTABLE ProxyDIChromeFrameEvents |
| 49 : public IConnectionPointImpl<T, &DIID_DIChromeFrameEvents> { |
| 50 public: |
| 51 void FireMethodWithParams(ChromeFrameEventDispId dispid, |
| 52 const VARIANT* params, size_t num_params) { |
| 53 T* me = static_cast<T*>(this); |
| 54 int connections = m_vec.GetSize(); |
| 55 |
| 56 for (int connection = 0; connection < connections; ++connection) { |
| 57 me->Lock(); |
| 58 CComPtr<IUnknown> sink(m_vec.GetAt(connection)); |
| 59 me->Unlock(); |
| 60 |
| 61 DIChromeFrameEvents* events = static_cast<DIChromeFrameEvents*>(sink.p); |
| 62 if (events) { |
| 63 DISPPARAMS disp_params = { |
| 64 const_cast<VARIANT*>(params), |
| 65 NULL, |
| 66 num_params, |
| 67 0}; |
| 68 HRESULT hr = events->Invoke(static_cast<DISPID>(dispid), |
| 69 DIID_DIChromeFrameEvents, |
| 70 LOCALE_USER_DEFAULT, DISPATCH_METHOD, |
| 71 &disp_params, NULL, NULL, NULL); |
| 72 DLOG_IF(ERROR, FAILED(hr)) << "invoke(" << dispid << ") failed" << |
| 73 StringPrintf("0x%08X", hr); |
| 74 } |
| 75 } |
| 76 } |
| 77 |
| 78 void FireMethodWithParam(ChromeFrameEventDispId dispid, |
| 79 const VARIANT& param) { |
| 80 FireMethodWithParams(dispid, ¶m, 1); |
| 81 } |
| 82 |
| 83 void Fire_onload(IDispatch* event) { |
| 84 VARIANT var = { VT_DISPATCH }; |
| 85 var.pdispVal = event; |
| 86 FireMethodWithParam(CF_EVENT_DISPID_ONLOAD, var); |
| 87 } |
| 88 |
| 89 void Fire_onloaderror(IDispatch* event) { |
| 90 VARIANT var = { VT_DISPATCH }; |
| 91 var.pdispVal = event; |
| 92 FireMethodWithParam(CF_EVENT_DISPID_ONLOADERROR, var); |
| 93 } |
| 94 |
| 95 void Fire_onmessage(IDispatch* event) { |
| 96 VARIANT var = { VT_DISPATCH }; |
| 97 var.pdispVal = event; |
| 98 FireMethodWithParam(CF_EVENT_DISPID_ONMESSAGE, var); |
| 99 } |
| 100 |
| 101 void Fire_onreadystatechanged(long readystate) { |
| 102 VARIANT var = { VT_I4 }; |
| 103 var.lVal = readystate; |
| 104 FireMethodWithParam(CF_EVENT_DISPID_ONREADYSTATECHANGED, var); |
| 105 } |
| 106 |
| 107 void Fire_onprivatemessage(IDispatch* event, BSTR target) { |
| 108 // Arguments in reverse order to the function declaration, because |
| 109 // that's what DISPPARAMS requires. |
| 110 VARIANT args[2] = { { VT_BSTR, }, {VT_DISPATCH, } }; |
| 111 args[0].bstrVal = target; |
| 112 args[1].pdispVal = event; |
| 113 |
| 114 FireMethodWithParams(CF_EVENT_DISPID_ONPRIVATEMESSAGE, |
| 115 args, |
| 116 arraysize(args)); |
| 117 } |
| 118 }; |
| 119 |
| 120 extern bool g_first_launch_by_process_; |
| 121 |
| 122 // Common implementation for ActiveX and Active Document |
| 123 template <class T, const CLSID& class_id> |
| 124 class ATL_NO_VTABLE ChromeFrameActivexBase : |
| 125 public CComObjectRootEx<CComSingleThreadModel>, |
| 126 public IOleControlImpl<T>, |
| 127 public IOleObjectImpl<T>, |
| 128 public IOleInPlaceActiveObjectImpl<T>, |
| 129 public IViewObjectExImpl<T>, |
| 130 public IOleInPlaceObjectWindowlessImpl<T>, |
| 131 public ISupportErrorInfo, |
| 132 public IQuickActivateImpl<T>, |
| 133 public com_util::IProvideClassInfo2Impl<class_id, |
| 134 DIID_DIChromeFrameEvents>, |
| 135 public com_util::IDispatchImpl<IChromeFrame>, |
| 136 public IConnectionPointContainerImpl<T>, |
| 137 public ProxyDIChromeFrameEvents<T>, |
| 138 public IPropertyNotifySinkCP<T>, |
| 139 public CComCoClass<T, &class_id>, |
| 140 public CComControl<T>, |
| 141 public ChromeFramePlugin<T> { |
| 142 protected: |
| 143 typedef std::set<ScopedComPtr<IDispatch> > EventHandlers; |
| 144 |
| 145 public: |
| 146 ChromeFrameActivexBase() |
| 147 : ready_state_(READYSTATE_UNINITIALIZED) { |
| 148 m_bWindowOnly = TRUE; |
| 149 } |
| 150 |
| 151 ~ChromeFrameActivexBase() { |
| 152 } |
| 153 |
| 154 DECLARE_OLEMISC_STATUS(OLEMISC_RECOMPOSEONRESIZE | OLEMISC_CANTLINKINSIDE | |
| 155 OLEMISC_INSIDEOUT | OLEMISC_ACTIVATEWHENVISIBLE | |
| 156 OLEMISC_SETCLIENTSITEFIRST) |
| 157 |
| 158 DECLARE_NOT_AGGREGATABLE(T) |
| 159 |
| 160 BEGIN_COM_MAP(ChromeFrameActivexBase) |
| 161 COM_INTERFACE_ENTRY(IChromeFrame) |
| 162 COM_INTERFACE_ENTRY(IDispatch) |
| 163 COM_INTERFACE_ENTRY(IViewObjectEx) |
| 164 COM_INTERFACE_ENTRY(IViewObject2) |
| 165 COM_INTERFACE_ENTRY(IViewObject) |
| 166 COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless) |
| 167 COM_INTERFACE_ENTRY(IOleInPlaceObject) |
| 168 COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless) |
| 169 COM_INTERFACE_ENTRY(IOleInPlaceActiveObject) |
| 170 COM_INTERFACE_ENTRY(IOleControl) |
| 171 COM_INTERFACE_ENTRY(IOleObject) |
| 172 COM_INTERFACE_ENTRY(ISupportErrorInfo) |
| 173 COM_INTERFACE_ENTRY(IQuickActivate) |
| 174 COM_INTERFACE_ENTRY(IProvideClassInfo) |
| 175 COM_INTERFACE_ENTRY(IProvideClassInfo2) |
| 176 COM_INTERFACE_ENTRY_FUNC_BLIND(0, InterfaceNotSupported) |
| 177 END_COM_MAP() |
| 178 |
| 179 BEGIN_CONNECTION_POINT_MAP(T) |
| 180 CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink) |
| 181 CONNECTION_POINT_ENTRY(DIID_DIChromeFrameEvents) |
| 182 END_CONNECTION_POINT_MAP() |
| 183 |
| 184 BEGIN_MSG_MAP(ChromeFrameActivexBase) |
| 185 MESSAGE_HANDLER(WM_CREATE, OnCreate) |
| 186 CHAIN_MSG_MAP(ChromeFramePlugin<T>) |
| 187 CHAIN_MSG_MAP(CComControl<T>) |
| 188 DEFAULT_REFLECTION_HANDLER() |
| 189 END_MSG_MAP() |
| 190 |
| 191 // IViewObjectEx |
| 192 DECLARE_VIEW_STATUS(VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE) |
| 193 |
| 194 inline HRESULT IViewObject_Draw(DWORD draw_aspect, LONG index, |
| 195 void* aspect_info, DVTARGETDEVICE* ptd, HDC info_dc, HDC dc, |
| 196 LPCRECTL bounds, LPCRECTL win_bounds) { |
| 197 // ATL ASSERTs if dwDrawAspect is DVASPECT_DOCPRINT, so we cheat. |
| 198 DWORD aspect = draw_aspect; |
| 199 if (aspect == DVASPECT_DOCPRINT) |
| 200 aspect = DVASPECT_CONTENT; |
| 201 |
| 202 return CComControl<T>::IViewObject_Draw(aspect, index, aspect_info, ptd, |
| 203 info_dc, dc, bounds, win_bounds); |
| 204 } |
| 205 |
| 206 DECLARE_PROTECT_FINAL_CONSTRUCT() |
| 207 |
| 208 HRESULT FinalConstruct() { |
| 209 if (!Initialize()) |
| 210 return E_OUTOFMEMORY; |
| 211 |
| 212 // Set to true if this is the first launch by this process. |
| 213 // Used to perform one time tasks. |
| 214 if (g_first_launch_by_process_) { |
| 215 g_first_launch_by_process_ = false; |
| 216 UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.IEVersion", |
| 217 GetIEVersion(), |
| 218 IE_INVALID, |
| 219 IE_8, |
| 220 IE_8 + 1); |
| 221 } |
| 222 return S_OK; |
| 223 } |
| 224 |
| 225 void FinalRelease() { |
| 226 } |
| 227 |
| 228 static HRESULT WINAPI InterfaceNotSupported(void* pv, REFIID riid, void** ppv, |
| 229 DWORD dw) { |
| 230 #ifndef NDEBUG |
| 231 wchar_t buffer[64] = {0}; |
| 232 ::StringFromGUID2(riid, buffer, arraysize(buffer)); |
| 233 DLOG(INFO) << "E_NOINTERFACE: " << buffer; |
| 234 #endif |
| 235 return E_NOINTERFACE; |
| 236 } |
| 237 |
| 238 // ISupportsErrorInfo |
| 239 STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid) { |
| 240 static const IID* interfaces[] = { |
| 241 &IID_IChromeFrame, |
| 242 &IID_IDispatch |
| 243 }; |
| 244 |
| 245 for (int i = 0; i < arraysize(interfaces); ++i) { |
| 246 if (InlineIsEqualGUID(*interfaces[i], riid)) |
| 247 return S_OK; |
| 248 } |
| 249 return S_FALSE; |
| 250 } |
| 251 |
| 252 // Called to draw our control when chrome hasn't been initialized. |
| 253 virtual HRESULT OnDraw(ATL_DRAWINFO& draw_info) { // NO_LINT |
| 254 if (NULL == draw_info.prcBounds) { |
| 255 NOTREACHED(); |
| 256 return E_FAIL; |
| 257 } |
| 258 // Don't draw anything. |
| 259 return S_OK; |
| 260 } |
| 261 |
| 262 |
| 263 // Used to setup the document_url_ member needed for completing navigation. |
| 264 // Create external tab (possibly in incognito mode). |
| 265 HRESULT IOleObject_SetClientSite(IOleClientSite* client_site) { |
| 266 // If we currently have a document site pointer, release it. |
| 267 doc_site_.Release(); |
| 268 if (client_site) { |
| 269 doc_site_.QueryFrom(client_site); |
| 270 } |
| 271 |
| 272 return CComControlBase::IOleObject_SetClientSite(client_site); |
| 273 } |
| 274 |
| 275 bool HandleContextMenuCommand(UINT cmd) { |
| 276 if (cmd == IDC_ABOUT_CHROME_FRAME) { |
| 277 int tab_handle = automation_client_->tab()->handle(); |
| 278 OnOpenURL(tab_handle, GURL("about:version"), NEW_WINDOW); |
| 279 return true; |
| 280 } |
| 281 |
| 282 return false; |
| 283 } |
| 284 |
| 285 // Should connections initiated by this class try to block |
| 286 // responses served with the X-Frame-Options header? |
| 287 // ActiveX controls genereally will want to do this, |
| 288 // returning true, while true top-level documents |
| 289 // (ActiveDocument servers) will not. Your specialization |
| 290 // of this template should implement this method based on how |
| 291 // it "feels" from a security perspective. If it's hosted in another |
| 292 // scriptable document, return true, else false. |
| 293 virtual bool is_frame_busting_enabled() const { |
| 294 return true; |
| 295 } |
| 296 |
| 297 protected: |
| 298 virtual void OnTabbedOut(int tab_handle, bool reverse) { |
| 299 DCHECK(m_bInPlaceActive); |
| 300 |
| 301 HWND parent = ::GetParent(m_hWnd); |
| 302 ::SetFocus(parent); |
| 303 ScopedComPtr<IOleControlSite> control_site; |
| 304 control_site.QueryFrom(m_spClientSite); |
| 305 if (control_site) |
| 306 control_site->OnFocus(FALSE); |
| 307 } |
| 308 |
| 309 virtual void OnOpenURL(int tab_handle, const GURL& url_to_open, |
| 310 int open_disposition) { |
| 311 ScopedComPtr<IWebBrowser2> web_browser2; |
| 312 DoQueryService(SID_SWebBrowserApp, m_spClientSite, web_browser2.Receive()); |
| 313 DCHECK(web_browser2); |
| 314 |
| 315 ScopedVariant url; |
| 316 // Check to see if the URL uses a "view-source:" prefix, if so, open it |
| 317 // using chrome frame full tab mode by using 'cf:' protocol handler. |
| 318 // Also change the disposition to NEW_WINDOW since IE6 doesn't have tabs. |
| 319 if (url_to_open.has_scheme() && (url_to_open.SchemeIs("view-source") || |
| 320 url_to_open.SchemeIs("about"))) { |
| 321 std::string chrome_url; |
| 322 chrome_url.append("cf:"); |
| 323 chrome_url.append(url_to_open.spec()); |
| 324 url.Set(UTF8ToWide(chrome_url).c_str()); |
| 325 open_disposition = NEW_WINDOW; |
| 326 } else { |
| 327 url.Set(UTF8ToWide(url_to_open.spec()).c_str()); |
| 328 } |
| 329 |
| 330 VARIANT flags = { VT_I4 }; |
| 331 V_I4(&flags) = 0; |
| 332 |
| 333 IEVersion ie_version = GetIEVersion(); |
| 334 DCHECK(ie_version != NON_IE && ie_version != IE_UNSUPPORTED); |
| 335 // Since IE6 doesn't support tabs, so we just use window instead. |
| 336 if (ie_version == IE_6) { |
| 337 if (open_disposition == NEW_FOREGROUND_TAB || |
| 338 open_disposition == NEW_BACKGROUND_TAB || |
| 339 open_disposition == NEW_WINDOW) { |
| 340 V_I4(&flags) = navOpenInNewWindow; |
| 341 } else if (open_disposition != CURRENT_TAB) { |
| 342 NOTREACHED() << "Unsupported open disposition in IE6"; |
| 343 } |
| 344 } else { |
| 345 switch (open_disposition) { |
| 346 case NEW_FOREGROUND_TAB: |
| 347 V_I4(&flags) = navOpenInNewTab; |
| 348 break; |
| 349 case NEW_BACKGROUND_TAB: |
| 350 V_I4(&flags) = navOpenInBackgroundTab; |
| 351 break; |
| 352 case NEW_WINDOW: |
| 353 V_I4(&flags) = navOpenInNewWindow; |
| 354 break; |
| 355 default: |
| 356 break; |
| 357 } |
| 358 } |
| 359 |
| 360 // TODO(sanjeevr): The navOpenInNewWindow flag causes IE to open this |
| 361 // in a new window ONLY if the user has specified |
| 362 // "Always open popups in a new window". Otherwise it opens in a new tab. |
| 363 // We need to investigate more and see if we can force IE to display the |
| 364 // link in a new window. MSHTML uses the below code to force an open in a |
| 365 // new window. But this logic also fails for us. Perhaps this flag is not |
| 366 // honoured if the ActiveDoc is not MSHTML. |
| 367 // Even the HLNF_DISABLEWINDOWRESTRICTIONS flag did not work. |
| 368 // Start of MSHTML-like logic. |
| 369 // CComQIPtr<ITargetFramePriv2> target_frame = web_browser2; |
| 370 // if (target_frame) { |
| 371 // CComPtr<IUri> uri; |
| 372 // CreateUri(UTF8ToWide(open_url_command->url_.spec()).c_str(), |
| 373 // Uri_CREATE_IE_SETTINGS, 0, &uri); |
| 374 // CComPtr<IBindCtx> bind_ctx; |
| 375 // CreateBindCtx(0, &bind_ctx); |
| 376 // target_frame->AggregatedNavigation2( |
| 377 // HLNF_TRUSTFIRSTDOWNLOAD|HLNF_OPENINNEWWINDOW, bind_ctx, NULL, |
| 378 // L"No_Name", uri, L""); |
| 379 // } |
| 380 // End of MSHTML-like logic |
| 381 VARIANT empty = ScopedVariant::kEmptyVariant; |
| 382 web_browser2->Navigate2(url.AsInput(), &flags, &empty, &empty, &empty); |
| 383 web_browser2->put_Visible(VARIANT_TRUE); |
| 384 } |
| 385 |
| 386 virtual void OnRequestStart(int tab_handle, int request_id, |
| 387 const IPC::AutomationURLRequest& request_info) { |
| 388 scoped_refptr<CComObject<UrlmonUrlRequest> > request; |
| 389 if (base_url_request_.get() && |
| 390 GURL(base_url_request_->url()) == GURL(request_info.url)) { |
| 391 request.swap(base_url_request_); |
| 392 } else { |
| 393 CComObject<UrlmonUrlRequest>* new_request = NULL; |
| 394 CComObject<UrlmonUrlRequest>::CreateInstance(&new_request); |
| 395 request = new_request; |
| 396 } |
| 397 |
| 398 DCHECK(request.get() != NULL); |
| 399 |
| 400 if (request->Initialize(automation_client_.get(), tab_handle, request_id, |
| 401 request_info.url, request_info.method, |
| 402 request_info.referrer, |
| 403 request_info.extra_request_headers, |
| 404 request_info.upload_data.get(), |
| 405 static_cast<T*>(this)->is_frame_busting_enabled()))
{ |
| 406 // If Start is successful, it will add a self reference. |
| 407 request->Start(); |
| 408 request->set_parent_window(m_hWnd); |
| 409 } |
| 410 } |
| 411 |
| 412 virtual void OnRequestRead(int tab_handle, int request_id, |
| 413 int bytes_to_read) { |
| 414 automation_client_->ReadRequest(request_id, bytes_to_read); |
| 415 } |
| 416 |
| 417 virtual void OnRequestEnd(int tab_handle, int request_id, |
| 418 const URLRequestStatus& status) { |
| 419 automation_client_->RemoveRequest(request_id, status.status(), true); |
| 420 } |
| 421 |
| 422 virtual void OnSetCookieAsync(int tab_handle, const GURL& url, |
| 423 const std::string& cookie) { |
| 424 std::string name; |
| 425 std::string data; |
| 426 size_t name_end = cookie.find('='); |
| 427 if (std::string::npos != name_end) { |
| 428 name = cookie.substr(0, name_end); |
| 429 data = cookie.substr(name_end + 1); |
| 430 } else { |
| 431 data = cookie; |
| 432 } |
| 433 |
| 434 BOOL ret = InternetSetCookieA(url.spec().c_str(), name.c_str(), |
| 435 data.c_str()); |
| 436 DCHECK(ret) << "InternetSetcookie failed. Error: " << GetLastError(); |
| 437 } |
| 438 |
| 439 virtual void OnAttachExternalTab(int tab_handle, |
| 440 intptr_t cookie, |
| 441 int disposition) { |
| 442 std::string url; |
| 443 url = StringPrintf("cf:attach_external_tab&%d&%d", |
| 444 cookie, disposition); |
| 445 OnOpenURL(tab_handle, GURL(url), disposition); |
| 446 } |
| 447 |
| 448 LRESULT OnCreate(UINT message, WPARAM wparam, LPARAM lparam, |
| 449 BOOL& handled) { // NO_LINT |
| 450 ModifyStyle(0, WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0); |
| 451 automation_client_->SetParentWindow(m_hWnd); |
| 452 // Only fire the 'interactive' ready state if we aren't there already. |
| 453 if (ready_state_ < READYSTATE_INTERACTIVE) { |
| 454 ready_state_ = READYSTATE_INTERACTIVE; |
| 455 FireOnChanged(DISPID_READYSTATE); |
| 456 } |
| 457 return 0; |
| 458 } |
| 459 |
| 460 // ChromeFrameDelegate override |
| 461 virtual void OnAutomationServerReady() { |
| 462 ChromeFramePlugin<T>::OnAutomationServerReady(); |
| 463 |
| 464 ready_state_ = READYSTATE_COMPLETE; |
| 465 FireOnChanged(DISPID_READYSTATE); |
| 466 } |
| 467 |
| 468 // ChromeFrameDelegate override |
| 469 virtual void OnAutomationServerLaunchFailed( |
| 470 AutomationLaunchResult reason, const std::string& server_version) { |
| 471 ready_state_ = READYSTATE_UNINITIALIZED; |
| 472 FireOnChanged(DISPID_READYSTATE); |
| 473 } |
| 474 |
| 475 // Overridden to take advantage of readystate prop changes and send those |
| 476 // to potential listeners. |
| 477 HRESULT FireOnChanged(DISPID dispid) { |
| 478 if (dispid == DISPID_READYSTATE) { |
| 479 Fire_onreadystatechanged(ready_state_); |
| 480 } |
| 481 return __super::FireOnChanged(dispid); |
| 482 } |
| 483 |
| 484 // IChromeFrame |
| 485 // Property getter/setters for the src attribute, which contains a URL. |
| 486 // The ChromeFrameActivex control initiates navigation to this URL |
| 487 // when instantiated. |
| 488 STDMETHOD(get_src)(BSTR* src) { |
| 489 if (NULL == src) { |
| 490 return E_POINTER; |
| 491 } |
| 492 |
| 493 *src = SysAllocString(url_); |
| 494 return S_OK; |
| 495 } |
| 496 |
| 497 STDMETHOD(put_src)(BSTR src) { |
| 498 if (src == NULL) |
| 499 return E_INVALIDARG; |
| 500 |
| 501 // Switch the src to UTF8 and try to expand to full URL |
| 502 std::string src_utf8; |
| 503 WideToUTF8(src, SysStringLen(src), &src_utf8); |
| 504 std::string full_url = ResolveURL(GetDocumentUrl(), src_utf8); |
| 505 if (full_url.empty()) { |
| 506 return E_INVALIDARG; |
| 507 } |
| 508 |
| 509 // We can initiate navigation here even if ready_state is not complete. |
| 510 // We do not have to set proxy, and AutomationClient will take care |
| 511 // of navigation just after CreateExternalTab is done. |
| 512 if (!automation_client_->InitiateNavigation(full_url)) { |
| 513 // TODO(robertshield): Make InitiateNavigation return more useful |
| 514 // error information. |
| 515 return E_INVALIDARG; |
| 516 } |
| 517 |
| 518 // Save full URL in BSTR member |
| 519 url_.Reset(::SysAllocString(UTF8ToWide(full_url).c_str())); |
| 520 |
| 521 return S_OK; |
| 522 } |
| 523 |
| 524 STDMETHOD(get_onload)(VARIANT* onload_handler) { |
| 525 if (NULL == onload_handler) |
| 526 return E_INVALIDARG; |
| 527 |
| 528 *onload_handler = onload_handler_.Copy(); |
| 529 |
| 530 return S_OK; |
| 531 } |
| 532 |
| 533 // Property setter for the onload attribute, which contains a |
| 534 // javascript function to be invoked on successful navigation. |
| 535 STDMETHOD(put_onload)(VARIANT onload_handler) { |
| 536 if (V_VT(&onload_handler) != VT_DISPATCH) { |
| 537 DLOG(WARNING) << "Invalid onload handler type: " |
| 538 << onload_handler.vt |
| 539 << " specified"; |
| 540 return E_INVALIDARG; |
| 541 } |
| 542 |
| 543 onload_handler_ = onload_handler; |
| 544 |
| 545 return S_OK; |
| 546 } |
| 547 |
| 548 // Property getter/setters for the onloaderror attribute, which contains a |
| 549 // javascript function to be invoked on navigation failure. |
| 550 STDMETHOD(get_onloaderror)(VARIANT* onerror_handler) { |
| 551 if (NULL == onerror_handler) |
| 552 return E_INVALIDARG; |
| 553 |
| 554 *onerror_handler = onerror_handler_.Copy(); |
| 555 |
| 556 return S_OK; |
| 557 } |
| 558 |
| 559 STDMETHOD(put_onloaderror)(VARIANT onerror_handler) { |
| 560 if (V_VT(&onerror_handler) != VT_DISPATCH) { |
| 561 DLOG(WARNING) << "Invalid onloaderror handler type: " |
| 562 << onerror_handler.vt |
| 563 << " specified"; |
| 564 return E_INVALIDARG; |
| 565 } |
| 566 |
| 567 onerror_handler_ = onerror_handler; |
| 568 |
| 569 return S_OK; |
| 570 } |
| 571 |
| 572 // Property getter/setters for the onmessage attribute, which contains a |
| 573 // javascript function to be invoked when we receive a message from the |
| 574 // chrome frame. |
| 575 STDMETHOD(put_onmessage)(VARIANT onmessage_handler) { |
| 576 if (V_VT(&onmessage_handler) != VT_DISPATCH) { |
| 577 DLOG(WARNING) << "Invalid onmessage handler type: " |
| 578 << onmessage_handler.vt |
| 579 << " specified"; |
| 580 return E_INVALIDARG; |
| 581 } |
| 582 |
| 583 onmessage_handler_ = onmessage_handler; |
| 584 |
| 585 return S_OK; |
| 586 } |
| 587 |
| 588 STDMETHOD(get_onmessage)(VARIANT* onmessage_handler) { |
| 589 if (NULL == onmessage_handler) |
| 590 return E_INVALIDARG; |
| 591 |
| 592 *onmessage_handler = onmessage_handler_.Copy(); |
| 593 |
| 594 return S_OK; |
| 595 } |
| 596 |
| 597 STDMETHOD(get_readyState)(long* ready_state) { // NOLINT |
| 598 DLOG(INFO) << __FUNCTION__; |
| 599 DCHECK(ready_state); |
| 600 |
| 601 if (!ready_state) |
| 602 return E_INVALIDARG; |
| 603 |
| 604 *ready_state = ready_state_; |
| 605 |
| 606 return S_OK; |
| 607 } |
| 608 |
| 609 // Property getter/setters for use_chrome_network flag. This flag |
| 610 // indicates if chrome network stack is to be used for fetching |
| 611 // network requests. |
| 612 STDMETHOD(get_useChromeNetwork)(VARIANT_BOOL* use_chrome_network) { |
| 613 if (!use_chrome_network) |
| 614 return E_INVALIDARG; |
| 615 |
| 616 *use_chrome_network = |
| 617 automation_client_->use_chrome_network() ? VARIANT_TRUE : VARIANT_FALSE; |
| 618 return S_OK; |
| 619 } |
| 620 |
| 621 STDMETHOD(put_useChromeNetwork)(VARIANT_BOOL use_chrome_network) { |
| 622 if (!is_privileged_) { |
| 623 DLOG(ERROR) << "Attempt to set useChromeNetwork in non-privileged mode"; |
| 624 return E_ACCESSDENIED; |
| 625 } |
| 626 |
| 627 automation_client_->set_use_chrome_network( |
| 628 (VARIANT_FALSE != use_chrome_network)); |
| 629 return S_OK; |
| 630 } |
| 631 |
| 632 // Posts a message to the chrome frame. |
| 633 STDMETHOD(postMessage)(BSTR message, VARIANT target) { |
| 634 if (NULL == message) { |
| 635 return E_INVALIDARG; |
| 636 } |
| 637 |
| 638 if (!automation_client_.get()) |
| 639 return E_FAIL; |
| 640 |
| 641 std::string utf8_target; |
| 642 if (target.vt == VT_BSTR) { |
| 643 int len = ::SysStringLen(target.bstrVal); |
| 644 if (len == 1 && target.bstrVal[0] == L'*') { |
| 645 utf8_target = "*"; |
| 646 } else { |
| 647 GURL resolved(target.bstrVal); |
| 648 if (!resolved.is_valid()) { |
| 649 Error(L"Unable to parse the specified target URL."); |
| 650 return E_INVALIDARG; |
| 651 } |
| 652 |
| 653 utf8_target = resolved.spec(); |
| 654 } |
| 655 } else { |
| 656 utf8_target = "*"; |
| 657 } |
| 658 |
| 659 std::string utf8_message; |
| 660 WideToUTF8(message, ::SysStringLen(message), &utf8_message); |
| 661 |
| 662 GURL url(GURL(document_url_).GetOrigin()); |
| 663 std::string origin(url.is_empty() ? "null" : url.spec()); |
| 664 if (!automation_client_->ForwardMessageFromExternalHost(utf8_message, |
| 665 origin, |
| 666 utf8_target)) { |
| 667 Error(L"Failed to post message to chrome frame"); |
| 668 return E_FAIL; |
| 669 } |
| 670 |
| 671 return S_OK; |
| 672 } |
| 673 |
| 674 STDMETHOD(addEventListener)(BSTR event_type, IDispatch* listener, |
| 675 VARIANT use_capture) { |
| 676 EventHandlers* handlers = NULL; |
| 677 HRESULT hr = GetHandlersForEvent(event_type, &handlers); |
| 678 if (FAILED(hr)) |
| 679 return hr; |
| 680 |
| 681 DCHECK(handlers != NULL); |
| 682 |
| 683 handlers->insert(ScopedComPtr<IDispatch>(listener)); |
| 684 |
| 685 return hr; |
| 686 } |
| 687 |
| 688 STDMETHOD(removeEventListener)(BSTR event_type, IDispatch* listener, |
| 689 VARIANT use_capture) { |
| 690 EventHandlers* handlers = NULL; |
| 691 HRESULT hr = GetHandlersForEvent(event_type, &handlers); |
| 692 if (FAILED(hr)) |
| 693 return hr; |
| 694 |
| 695 DCHECK(handlers != NULL); |
| 696 std::remove(handlers->begin(), handlers->end(), listener); |
| 697 |
| 698 return hr; |
| 699 } |
| 700 |
| 701 STDMETHOD(get_version)(BSTR* version) { |
| 702 if (!automation_client_.get()) { |
| 703 NOTREACHED(); |
| 704 return E_FAIL; |
| 705 } |
| 706 |
| 707 if (version == NULL) { |
| 708 return E_INVALIDARG; |
| 709 } |
| 710 |
| 711 *version = SysAllocString(automation_client_->GetVersion().c_str()); |
| 712 return S_OK; |
| 713 } |
| 714 |
| 715 STDMETHOD(postPrivateMessage)(BSTR message, BSTR origin, BSTR target) { |
| 716 if (NULL == message) |
| 717 return E_INVALIDARG; |
| 718 |
| 719 if (!is_privileged_) { |
| 720 DLOG(ERROR) << "Attempt to postPrivateMessage in non-privileged mode"; |
| 721 return E_ACCESSDENIED; |
| 722 } |
| 723 |
| 724 DCHECK(automation_client_.get()); |
| 725 std::string utf8_message, utf8_origin, utf8_target; |
| 726 WideToUTF8(message, ::SysStringLen(message), &utf8_message); |
| 727 WideToUTF8(origin, ::SysStringLen(origin), &utf8_origin); |
| 728 WideToUTF8(target, ::SysStringLen(target), &utf8_target); |
| 729 |
| 730 if (!automation_client_->ForwardMessageFromExternalHost(utf8_message, |
| 731 utf8_origin, |
| 732 utf8_target)) { |
| 733 Error(L"Failed to post message to chrome frame"); |
| 734 return E_FAIL; |
| 735 } |
| 736 |
| 737 return S_OK; |
| 738 } |
| 739 |
| 740 // Returns the vector of event handlers for a given event (e.g. "load"). |
| 741 // If the event type isn't recognized, the function fills in a descriptive |
| 742 // error (IErrorInfo) and returns E_INVALIDARG. |
| 743 HRESULT GetHandlersForEvent(BSTR event_type, EventHandlers** handlers) { |
| 744 DCHECK(handlers != NULL); |
| 745 |
| 746 HRESULT hr = S_OK; |
| 747 const wchar_t* event_type_end = event_type + ::SysStringLen(event_type); |
| 748 if (LowerCaseEqualsASCII(event_type, event_type_end, "message")) { |
| 749 *handlers = &onmessage_; |
| 750 } else if (LowerCaseEqualsASCII(event_type, event_type_end, "load")) { |
| 751 *handlers = &onload_; |
| 752 } else if (LowerCaseEqualsASCII(event_type, event_type_end, "loaderror")) { |
| 753 *handlers = &onloaderror_; |
| 754 } else if (LowerCaseEqualsASCII(event_type, event_type_end, |
| 755 "readystatechanged")) { |
| 756 *handlers = &onreadystatechanged_; |
| 757 } else if (LowerCaseEqualsASCII(event_type, event_type_end, |
| 758 "privatemessage")) { |
| 759 // This event handler is only available in privileged mode. |
| 760 if (!is_privileged_) { |
| 761 Error("Event type 'privatemessage' is privileged"); |
| 762 return E_ACCESSDENIED; |
| 763 } |
| 764 *handlers = &onprivatemessage_; |
| 765 } else { |
| 766 Error(StringPrintf("Event type '%ls' not found", event_type).c_str()); |
| 767 hr = E_INVALIDARG; |
| 768 } |
| 769 |
| 770 return hr; |
| 771 } |
| 772 |
| 773 // Gives the browser a chance to handle an accelerator that was |
| 774 // sent to the out of proc chromium instance. |
| 775 // Returns S_OK iff the accelerator was handled by the browser. |
| 776 HRESULT AllowFrameToTranslateAccelerator(const MSG& msg) { |
| 777 // Although IBrowserService2 is officially deprecated, it's still alive |
| 778 // and well in IE7 and earlier. We have to use it here to correctly give |
| 779 // the browser a chance to handle keyboard shortcuts. |
| 780 // This happens automatically for activex components that have windows that |
| 781 // belong to the current thread. In that circumstance IE owns the message |
| 782 // loop and can walk the line of components allowing each participant the |
| 783 // chance to handle the keystroke and eventually falls back to |
| 784 // v_MayTranslateAccelerator. However in our case, the message loop is |
| 785 // owned by the out-of-proc chromium instance so IE doesn't have a chance to |
| 786 // fall back on its default behavior. Instead we give IE a chance to |
| 787 // handle the shortcut here. |
| 788 |
| 789 HRESULT hr = S_FALSE; |
| 790 ScopedComPtr<IBrowserService2> bs2; |
| 791 if (S_OK == DoQueryService(SID_STopLevelBrowser, m_spInPlaceSite, |
| 792 bs2.Receive())) { |
| 793 hr = bs2->v_MayTranslateAccelerator(const_cast<MSG*>(&msg)); |
| 794 } else { |
| 795 // IE8 doesn't support IBrowserService2 unless you enable a special, |
| 796 // undocumented flag with CoInternetSetFeatureEnabled and even then, |
| 797 // the object you get back implements only a couple of methods of |
| 798 // that interface... all the other entries in the vtable are NULL. |
| 799 // In addition, the class that implements it is called |
| 800 // ImpostorBrowserService2 :) |
| 801 // IE8 does have a new interface though, presumably called |
| 802 // ITabBrowserService or something that can be abbreviated to TBS. |
| 803 // That interface has a method, TranslateAcceleratorTBS that does |
| 804 // call the root MayTranslateAccelerator function, but alas the |
| 805 // first argument to MayTranslateAccelerator is hard coded to FALSE |
| 806 // which means that global accelerators are not handled and we're |
| 807 // out of luck. |
| 808 // A third thing that's notable with regards to IE8 is that |
| 809 // none of the *MayTranslate* functions exist in a vtable inside |
| 810 // ieframe.dll. I checked this by scanning for the address of |
| 811 // those functions inside the dll and found none, which means that |
| 812 // all calls to those functions are relative. |
| 813 // So, for IE8, our approach is very simple. Just post the message |
| 814 // to our parent window and IE will pick it up if it's an |
| 815 // accelerator. We won't know for sure if the browser handled the |
| 816 // keystroke or not. |
| 817 ::PostMessage(::GetParent(m_hWnd), msg.message, msg.wParam, |
| 818 msg.lParam); |
| 819 } |
| 820 |
| 821 return hr; |
| 822 } |
| 823 |
| 824 protected: |
| 825 ScopedBstr url_; |
| 826 ScopedComPtr<IOleDocumentSite> doc_site_; |
| 827 |
| 828 // For more information on the ready_state_ property see: |
| 829 // http://msdn.microsoft.com/en-us/library/aa768179(VS.85).aspx# |
| 830 READYSTATE ready_state_; |
| 831 |
| 832 // The following members contain IDispatch interfaces representing the |
| 833 // onload/onerror/onmessage handlers on the page. |
| 834 ScopedVariant onload_handler_; |
| 835 ScopedVariant onerror_handler_; |
| 836 ScopedVariant onmessage_handler_; |
| 837 |
| 838 EventHandlers onmessage_; |
| 839 EventHandlers onloaderror_; |
| 840 EventHandlers onload_; |
| 841 EventHandlers onreadystatechanged_; |
| 842 EventHandlers onprivatemessage_; |
| 843 |
| 844 // The UrlmonUrlRequest instance instantiated for downloading the base URL. |
| 845 scoped_refptr<CComObject<UrlmonUrlRequest> > base_url_request_; |
| 846 }; |
| 847 |
| 848 #endif // CHROME_FRAME_CHROME_FRAME_ACTIVEX_BASE_H_ |
OLD | NEW |