// Copyright (c)1997-1999 Microsoft Corporation, All Rights Reserved // Notes on m_bfModeSwitched and m_bfReloadAttempted. // IE5 bug 52818 was punted; pages containing IFrames don't refresh when changing // browse/edit modes, because the stream is seen as dirty (because the IFrame // considers itself dirty.) In response, we set m_bfModeSwitched when changing mode, // m_bfReloadAttempted when and ATTEMPT is made to reload the page, and check for BOTH // in OnReadyStateChanged. If the mode was changed but the page wasn't reloaded, // we have to reload it manually. #include "stdafx.h" #include "DHTMLEd.h" #include "DHTMLEdit.h" #include "site.h" #include "proxyframe.h" #include #include #include "dispexa.h" #include #include // HTML to initialize Trident with if the host didn't supply any // The

 

works around a nasty Trident bug. // Change: now there is one with paragraphs, one with DIVs. // static WCHAR* g_initialHTMLwithP = \ L"\r\n\ \r\n\ \r\n\ \r\n\ \r\n\ \r\n\

 

\r\n\ \r\n\ \r\n"; static WCHAR* g_initialHTMLwithDIV = \ L"\r\n\ \r\n\ \r\n\ \r\n\ \r\n\ \r\n\
 
\r\n\ \r\n\ \r\n"; // Text, numbers and constants used to construct a unique-per-process protocol ID // static WCHAR* g_wszProtocolPrefix = L"DHTMLEd"; static int s_iProtocolSuffix = 0; #define MAX_PROTOCOL_SUFFIX 999999 // Name of the Title property which we get from the IHtmlDocument2 interface. static WCHAR* g_wszHTMLTitlePropName = L"title"; // Maps private DHTMLEdit command IDs to Triedit command IDs. // The third field is true if the command includes an out parameter. // static CommandMap cmdMap[] = { {DECMD_BOLD, IDM_TRIED_BOLD, FALSE}, {DECMD_COPY, IDM_TRIED_COPY, FALSE}, {DECMD_CUT, IDM_TRIED_CUT, FALSE}, {DECMD_DELETE, IDM_TRIED_DELETE, FALSE}, {DECMD_DELETECELLS, IDM_TRIED_DELETECELLS, FALSE}, {DECMD_DELETECOLS, IDM_TRIED_DELETECOLS, FALSE}, {DECMD_DELETEROWS, IDM_TRIED_DELETEROWS, FALSE}, {DECMD_FINDTEXT, IDM_TRIED_FIND, FALSE}, {DECMD_FONT, IDM_TRIED_FONT, FALSE}, {DECMD_GETBACKCOLOR, IDM_TRIED_BACKCOLOR, TRUE}, {DECMD_GETBLOCKFMT, IDM_TRIED_BLOCKFMT, TRUE}, {DECMD_GETBLOCKFMTNAMES, IDM_TRIED_GETBLOCKFMTS, TRUE}, {DECMD_GETFONTNAME, IDM_TRIED_FONTNAME, TRUE}, {DECMD_GETFONTSIZE, IDM_TRIED_FONTSIZE, TRUE}, {DECMD_GETFORECOLOR, IDM_TRIED_FORECOLOR, TRUE}, {DECMD_HYPERLINK, IDM_TRIED_HYPERLINK, FALSE}, {DECMD_IMAGE, IDM_TRIED_IMAGE, FALSE}, {DECMD_INDENT, IDM_TRIED_INDENT, FALSE}, {DECMD_INSERTCELL, IDM_TRIED_INSERTCELL, FALSE}, {DECMD_INSERTCOL, IDM_TRIED_INSERTCOL, FALSE}, {DECMD_INSERTROW, IDM_TRIED_INSERTROW, FALSE}, {DECMD_INSERTTABLE, IDM_TRIED_INSERTTABLE, FALSE}, {DECMD_ITALIC, IDM_TRIED_ITALIC, FALSE}, {DECMD_JUSTIFYLEFT, IDM_TRIED_JUSTIFYLEFT, FALSE}, {DECMD_JUSTIFYRIGHT, IDM_TRIED_JUSTIFYRIGHT, FALSE}, {DECMD_JUSTIFYCENTER, IDM_TRIED_JUSTIFYCENTER, FALSE}, {DECMD_LOCK_ELEMENT, IDM_TRIED_LOCK_ELEMENT, FALSE}, {DECMD_MAKE_ABSOLUTE, IDM_TRIED_MAKE_ABSOLUTE, FALSE}, {DECMD_MERGECELLS, IDM_TRIED_MERGECELLS, FALSE}, {DECMD_ORDERLIST, IDM_TRIED_ORDERLIST, FALSE}, {DECMD_OUTDENT, IDM_TRIED_OUTDENT, FALSE}, {DECMD_PASTE, IDM_TRIED_PASTE, FALSE}, {DECMD_REDO, IDM_TRIED_REDO, FALSE}, {DECMD_REMOVEFORMAT, IDM_TRIED_REMOVEFORMAT, FALSE}, {DECMD_SELECTALL, IDM_TRIED_SELECTALL, FALSE}, {DECMD_SEND_BACKWARD, IDM_TRIED_SEND_BACKWARD, FALSE}, {DECMD_BRING_FORWARD, IDM_TRIED_SEND_FORWARD, FALSE}, {DECMD_SEND_BELOW_TEXT, IDM_TRIED_SEND_BEHIND_1D, FALSE}, {DECMD_BRING_ABOVE_TEXT, IDM_TRIED_SEND_FRONT_1D, FALSE}, {DECMD_SEND_TO_BACK, IDM_TRIED_SEND_TO_BACK, FALSE}, {DECMD_BRING_TO_FRONT, IDM_TRIED_SEND_TO_FRONT, FALSE}, {DECMD_SETBACKCOLOR, IDM_TRIED_BACKCOLOR, FALSE}, {DECMD_SETBLOCKFMT, IDM_TRIED_BLOCKFMT, FALSE}, {DECMD_SETFONTNAME, IDM_TRIED_FONTNAME, FALSE}, {DECMD_SETFONTSIZE, IDM_TRIED_FONTSIZE, FALSE}, {DECMD_SETFORECOLOR, IDM_TRIED_FORECOLOR, FALSE}, {DECMD_SPLITCELL, IDM_TRIED_SPLITCELL, FALSE}, {DECMD_UNDERLINE, IDM_TRIED_UNDERLINE, FALSE}, {DECMD_UNDO, IDM_TRIED_UNDO, FALSE}, {DECMD_UNLINK, IDM_TRIED_UNLINK, FALSE}, {DECMD_UNORDERLIST, IDM_TRIED_UNORDERLIST, FALSE}, {DECMD_PROPERTIES, IDM_TRIED_DOVERB, FALSE} }; CProxyFrame::CProxyFrame(CDHTMLSafe* pCtl) { SAFEARRAYBOUND rgsabound[1] = {0}; _ASSERTE(pCtl); m_cRef = 1; m_pUnkTriEdit = NULL; m_hWndObj = NULL; m_pIOleIPActiveObject = NULL; m_pSite = NULL; m_pCtl = pCtl; m_fCreated = FALSE; m_fActivated = FALSE; m_state = ESTATE_NOTCREATED; m_readyState = READYSTATE_UNINITIALIZED; m_dwFilterFlags = m_dwFilterOutFlags = filterAll; m_fActivateApplets = FALSE; m_fActivateControls = FALSE; m_fActivateDTCs = TRUE; m_fShowAllTags = FALSE; m_fShowBorders = FALSE; m_fDialogEditing = TRUE; m_fDisplay3D = TRUE; m_fScrollbars = TRUE; m_fDisplayFlatScrollbars = FALSE; m_fContextMenu = TRUE; m_fPreserveSource = TRUE; m_fAbsoluteDropMode = FALSE; m_fSnapToGrid = FALSE; m_ulSnapToGridX = 50; m_ulSnapToGridY = 50; rgsabound[0].lLbound = 0; rgsabound[0].cElements = 0; m_pMenuStrings = NULL; m_pMenuStates = NULL; m_vbBrowseMode = VARIANT_FALSE; m_vbUseDivOnCr = VARIANT_FALSE; m_bfIsLoading = FALSE; m_bfBaseURLFromBASETag = FALSE; m_bfPreserveDirtyFlagAcrossBrowseMode = FALSE; m_bstrInitialDoc.Empty (); m_bstrCurDocPath.Empty (); wcscpy ( m_wszProtocol, g_wszProtocolPrefix ); WCHAR wszSuffix[8]; _itow ( s_iProtocolSuffix++, wszSuffix, 10 ); if ( MAX_PROTOCOL_SUFFIX <= s_iProtocolSuffix ) { s_iProtocolSuffix = 0; // Roll over. } wcscat ( m_wszProtocol, wszSuffix ); wcscpy ( m_wszProtocolPrefix, m_wszProtocol ); wcscat ( m_wszProtocolPrefix, L":" ); m_pProtInfo = NULL; m_bfIsURL = FALSE; m_bstrBaseURL = L""; m_hwndRestoreFocus = NULL; #ifdef LATE_BIND_URLMON_WININET m_hUlrMon = NULL; m_hWinINet = NULL; m_pfnCoInternetCombineUrl = NULL; m_pfnCoInternetParseUrl = NULL; m_pfnCreateURLMoniker = NULL; m_pfnCoInternetGetSession = NULL; m_pfnURLOpenBlockingStream = NULL; m_pfnDeleteUrlCacheEntry = NULL; m_pfnInternetCreateUrl = NULL; m_pfnInternetCrackUrl = NULL; #endif // LATE_BIND_URLMON_WININET m_bfModeSwitched = FALSE; m_bfReloadAttempted = FALSE; m_bfSFSRedirect = FALSE; } CProxyFrame::~CProxyFrame() { _ASSERTE(FALSE == m_fCreated); _ASSERTE(FALSE == m_fActivated); _ASSERTE( m_cRef == 0 ); if (m_pMenuStrings) { SafeArrayDestroy(m_pMenuStrings); m_pMenuStrings = NULL; } if (m_pMenuStates) { SafeArrayDestroy(m_pMenuStates); m_pMenuStates = NULL; } // This should never happen: SetActiveObject should take care of it. _ASSERTE ( NULL == m_pIOleIPActiveObject ); if (m_pIOleIPActiveObject) { m_pIOleIPActiveObject->Release(); m_pIOleIPActiveObject = NULL; } UnRegisterPluggableProtocol (); #ifdef LATE_BIND_URLMON_WININET DynUnloadLibraries (); #endif // LATE_BIND_URLMON_WININET } // Create the TriEdit object and host it. // Clean up and return an error if there was any problem. // HRESULT CProxyFrame::Init(IUnknown* pUnk, IUnknown** ppUnkTriEdit) { HRESULT hr = S_OK; #ifdef LATE_BIND_URLMON_WININET if ( ! DynLoadLibraries () ) { return E_FAIL; } #endif // LATE_BIND_URLMON_WININET hr = RegisterPluggableProtocol (); if ( FAILED ( hr ) ) { return hr; } _ASSERTE(NULL == m_pSite); _ASSERTE(GetState() == ESTATE_NOTCREATED); InitializeDocString (); if (m_pSite) return E_UNEXPECTED; if (GetState() != ESTATE_NOTCREATED) return E_UNEXPECTED; // Create and initialize the site for TriEdit m_pSite = new CSite(this); if (NULL == m_pSite) { return E_OUTOFMEMORY; } m_pSite->AddRef(); // So we can free with Release // Ask the site to create TriEdit if (SUCCEEDED(hr = m_pSite->HrCreate(pUnk, &m_pUnkTriEdit))) { ChangeState(ESTATE_CREATED); m_fCreated = TRUE; if (ppUnkTriEdit) { m_pUnkTriEdit->AddRef(); *ppUnkTriEdit = m_pUnkTriEdit; } } else { m_pSite->Release(); m_pSite = NULL; } return hr; } // Destroy the site and the TriEdit object. // HRESULT CProxyFrame::Close() { HRESULT hr = S_OK; _ASSERTE(m_pUnkTriEdit); _ASSERTE(m_pSite); _ASSERTE(GetState() != ESTATE_NOTCREATED); m_bstrCurDocPath.Empty (); // triedit must be created // any state from created to activated is ok if (GetState() == ESTATE_NOTCREATED) return E_UNEXPECTED; if (m_fActivated) { hr = HrExecCommand(&CGID_MSHTML, IDM_STOP, MSOCMDEXECOPT_DONTPROMPTUSER, NULL, NULL); _ASSERTE(SUCCEEDED(hr)); } ChangeState(ESTATE_NOTCREATED); m_fCreated = FALSE; m_fActivated = FALSE; if (m_pSite != NULL) { CSite* pSite = m_pSite; // prevents reentry; m_pSite = NULL; pSite->Close(FALSE); ReleaseInterface(pSite) pSite = NULL; } if (m_pUnkTriEdit != NULL) { LPUNKNOWN pUnkTriEdit = m_pUnkTriEdit; m_pUnkTriEdit = NULL; ReleaseInterface(pUnkTriEdit); pUnkTriEdit = NULL; } m_hwndRestoreFocus = NULL; return S_OK; } // Determine which string constant to use and return a pointer to it. // WCHAR* CProxyFrame::GetInitialHTML () { if ( m_vbUseDivOnCr ) { return g_initialHTMLwithDIV; } else { return g_initialHTMLwithP; } } // Perform these steps before loading TriEdit's contents // HRESULT CProxyFrame::PreActivate() { HRESULT hr = S_OK; _ASSERTE(m_pSite); _ASSERTE(m_pCtl); _ASSERTE(ESTATE_CREATED == GetState()); if (GetState() != ESTATE_CREATED) return E_UNEXPECTED; if (FAILED(hr = m_pSite->HrObjectInitialize())) { _ASSERTE(SUCCEEDED(hr)); goto error; } m_fActivated = TRUE; ChangeState(ESTATE_PREACTIVATING); if (FAILED(hr = HrSetRuntimeProperties())) { _ASSERTE(SUCCEEDED(hr)); goto error; } ChangeState(ESTATE_ACTIVATING); error: return hr; } void CProxyFrame::UIDeactivate() { // This was m_pSite->GetIPObject()->UIDeactivate(), // but the QA teams VB app using this version of the control crashed // with a NULL pointer dereference. if ( NULL != m_pSite ) { LPOLEINPLACEOBJECT pipObj = m_pSite->GetIPObject(); if ( NULL != pipObj ) { pipObj->UIDeactivate(); } } } // Perform these steps after loading TriEdits contents to go UI active. // HRESULT CProxyFrame::Activate() { HRESULT hr = S_OK; _ASSERTE(m_pSite); _ASSERTE(m_pCtl); _ASSERTE(m_pCtl->m_hWndCD); _ASSERTE(GetState() == ESTATE_ACTIVATING); if (GetState() != ESTATE_ACTIVATING) return E_UNEXPECTED; // UI-activate the control if ( ! m_pCtl->m_bUIActive ) { // Used to be UIActivate, until MohanB fixed OnSetFocus and activation/deactivation linkage. m_pCtl->DoVerbInPlaceActivate ( NULL, NULL ); } // activate Trident with "Show" m_pSite->InitialActivate(OLEIVERB_SHOW, m_pCtl->m_hWndCD); ChangeState(ESTATE_ACTIVATED); // This may have been deferred, because the site's command target did not yet exist... SetBrowseMode ( m_vbBrowseMode ); return hr; } // Load and activate the control with a minimal, empty page. // HRESULT CProxyFrame::LoadInitialDoc() { HRESULT hr = S_OK; _ASSERTE(GetState() == ESTATE_CREATED); if (GetState() != ESTATE_CREATED) return E_UNEXPECTED; if (FAILED(hr = PreActivate())) goto error; if (FAILED(hr = LoadBSTRDeferred ( m_bstrInitialDoc ))) { _ASSERTE(SUCCEEDED(hr)); goto error; } if (FAILED(hr = Activate())) goto error; error: return hr; } // Before getting or setting a property or calling a method on the docobject, // assure that it's been properly activated. // void CProxyFrame::AssureActivated () { if ( ! m_fActivated ) { if ( m_pCtl->IsUserMode() ) { if ( !m_pCtl->m_bInPlaceActive ) { m_pCtl->DoVerbInPlaceActivate ( NULL, NULL ); } LoadInitialDoc (); } } } // Loading MSHTML shifts the focus to its document window. // This is not desirable in a control. Experimentation has demonstrated // that the focus shifts between various QIs from MSHTML (probably in response // to posted messages.) There is no routine in DHTMLEdit which enters with the // focus outside the control and exits with the focus within the control. // Therefore, a member variable is used to preserve the appropriate focus // across calls to OnReadyStateChanged, which are called in response to events // fired by the control. m_hwndRestoreFocus is used to preserve the appropriate // window to receive the focus. Note that NULL may be appropriate, but is not honored. // If no window had focus, the document will gain focus. // void CProxyFrame::OnReadyStateChanged(READYSTATE readyState) { _ASSERTE(m_pCtl); m_readyState = readyState; switch (m_readyState) { case READYSTATE_UNINITIALIZED: { m_hwndRestoreFocus = NULL; } break; case READYSTATE_LOADING: { m_hwndRestoreFocus = ::GetFocus (); } break; case READYSTATE_LOADED: { } break; case READYSTATE_INTERACTIVE: { if ( NULL != m_hwndRestoreFocus ) { _ASSERTE ( ::IsWindow ( m_hwndRestoreFocus ) ); if ( ::IsWindow ( m_hwndRestoreFocus ) ) { ::SetFocus ( m_hwndRestoreFocus ); } } // See if we failed to get a refresh on a mode change. This happens if // there are IFrames on the page, perhaps in other cases as well. if ( m_bfModeSwitched && !m_bfReloadAttempted ) { HRESULT hr = S_OK; CComPtr srpMoniker; CComPtr srpBindCtx; CComQIPtr srpPM (m_pUnkTriEdit); _ASSERTE ( srpPM ); if ( srpPM ) { CComBSTR bstrProtocol = m_wszProtocolPrefix; #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnCreateURLMoniker ); hr = (*m_pfnCreateURLMoniker)( NULL, bstrProtocol, &srpMoniker ); #else hr = CreateURLMoniker ( NULL, bstrProtocol, &srpMoniker ); #endif // LATE_BIND_URLMON_WININET _ASSERTE ( SUCCEEDED( hr ) ); if ( SUCCEEDED ( hr ) ) { hr = ::CreateBindCtx(NULL, &srpBindCtx); _ASSERTE ( SUCCEEDED( hr ) ); if ( SUCCEEDED ( hr ) ) { hr = srpPM->Load(FALSE, srpMoniker, srpBindCtx, STGM_READ); } } } } m_bfModeSwitched = FALSE; m_bfReloadAttempted = FALSE; } break; case READYSTATE_COMPLETE: { HRESULT hr = S_OK; m_hwndRestoreFocus = NULL; if ( ! m_vbBrowseMode ) { hr = HrSetDocLoadedProperties(); _ASSERTE(SUCCEEDED(hr)); } _ASSERTE ( m_pCtl->m_hWnd ); _ASSERTE ( ::IsWindow ( m_pCtl->m_hWnd ) ); if ( m_bfPreserveDirtyFlagAcrossBrowseMode && !m_vbBrowseMode ) { m_bfPreserveDirtyFlagAcrossBrowseMode = FALSE; SetDirtyFlag ( TRUE ); } // Post a user message to fire the DocumentComplete event. // Otherwise, calling things like LoadURL from DocumentComplete behaves strangely. ::PostMessage ( m_pCtl->m_hWnd, DOCUMENT_COMPETE_MESSAGE, DOCUMENT_COMPETE_SIGNATURE, 0L ); HrSetRuntimeProperties (); //m_bfIsLoading = FALSE; // This has been moved the the DOCUMENT_COMPETE_MESSAGE handler. SetBaseURLFromBaseHref (); // Must be called after clearing m_bfIsLoading } break; } } /* * IUnknown implementation */ /* * CProxyFrame::QueryInterface * CProxyFrame::AddRef * CProxyFrame::Release */ STDMETHODIMP CProxyFrame::QueryInterface( REFIID riid, void **ppv ) { /* * We provide IOleInPlaceFrame and IOleCommandTarget * interfaces here for the ActiveX Document hosting */ *ppv = NULL; if ( IID_IUnknown == riid || IID_IOleInPlaceUIWindow == riid || IID_IOleWindow == riid || IID_IOleInPlaceFrame == riid ) { *ppv = static_cast(this); } else if ( IID_IOleCommandTarget == riid ) { *ppv = static_cast(this); } else if ( IID_IBindStatusCallback == riid ) { *ppv = static_cast(this); } else if ( IID_IAuthenticate == riid ) { *ppv = static_cast(this); } else if ( IID_IServiceProvider == riid ) { // Ask the control for a security manager IF in edit mode: if ( ! m_vbBrowseMode ) { return m_pCtl->GetUnknown()->QueryInterface ( riid, ppv ); } } if ( NULL != *ppv ) { ((LPUNKNOWN)*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CProxyFrame::AddRef( void ) { return ++m_cRef; } STDMETHODIMP_(ULONG) CProxyFrame::Release( void ) { //Nothing special happening here-- life if user-controlled. // Debug check to see we don't fall below 0 _ASSERTE( m_cRef != 0 ); ULONG ulRefCount = --m_cRef; if ( 0 == ulRefCount ) { delete this; // Do not refer to any member variables after this. } return ulRefCount; } /* * IOleInPlaceFrame implementation */ /* * CProxyFrame::GetWindow * * Purpose: * Retrieves the handle of the window associated with the object * on which this interface is implemented. * * Parameters: * phWnd HWND * in which to store the window handle. * * Return Value: * HRESULT S_OK if successful, E_FAIL if there is no * window. */ STDMETHODIMP CProxyFrame::GetWindow( HWND* phWnd ) { if ( m_pCtl != NULL ) { *phWnd = m_pCtl->m_hWnd; } return S_OK; } /* * CProxyFrame::ContextSensitiveHelp * * Purpose: * Instructs the object on which this interface is implemented to * enter or leave a context-sensitive help mode. * * Parameters: * fEnterMode BOOL TRUE to enter the mode, FALSE otherwise. * * Return Value: * HRESULT S_OK */ STDMETHODIMP CProxyFrame::ContextSensitiveHelp( BOOL /*fEnterMode*/ ) { return S_OK; } /* * CProxyFrame::GetBorder * * Purpose: * Returns the rectangle in which the container is willing to * negotiate about an object's adornments. * * Parameters: * prcBorder LPRECT in which to store the rectangle. * * Return Value: * HRESULT S_OK if all is well, INPLACE_E_NOTOOLSPACE * if there is no negotiable space. */ STDMETHODIMP CProxyFrame::GetBorder( LPRECT prcBorder ) { if ( NULL == prcBorder ) { return E_INVALIDARG; } //We return all the client area space m_pCtl->GetClientRect( prcBorder ); return S_OK; } /* * CProxyFrame::RequestBorderSpace * * Purpose: * Asks the container if it can surrender the amount of space * in pBW that the object would like for it's adornments. The * container does nothing but validate the spaces on this call. * * Parameters: * pBW LPCBORDERWIDTHS containing the requested space. * The values are the amount of space requested * from each side of the relevant window. * * Return Value: * HRESULT S_OK if we can give up space, * INPLACE_E_NOTOOLSPACE otherwise. */ STDMETHODIMP CProxyFrame::RequestBorderSpace( LPCBORDERWIDTHS /*pBW*/ ) { // We have no border space restrictions return S_OK; } /* * CProxyFrame::SetBorderSpace * * Purpose: * Called when the object now officially requests that the * container surrender border space it previously allowed * in RequestBorderSpace. The container should resize windows * appropriately to surrender this space. * * Parameters: * pBW LPCBORDERWIDTHS containing the amount of space * from each side of the relevant window that the * object is now reserving. * * Return Value: * HRESULT S_OK */ STDMETHODIMP CProxyFrame::SetBorderSpace( LPCBORDERWIDTHS /*pBW*/ ) { // We turn off the MSHTML.DLL UI so we ignore all of this. return S_OK; } /* * CProxyFrame::SetActiveObject * * Purpose: * Provides the container with the object's IOleInPlaceActiveObject * pointer * * Parameters: * pIIPActiveObj LPOLEINPLACEACTIVEOBJECT of interest. * pszObj LPCOLESTR naming the object. Not used. * * Return Value: * HRESULT S_OK */ STDMETHODIMP CProxyFrame::SetActiveObject( LPOLEINPLACEACTIVEOBJECT pIIPActiveObj, LPCOLESTR /*pszObj*/) { // If we already have an active Object then release it. if ( NULL != m_pIOleIPActiveObject ) { m_pIOleIPActiveObject->Release(); } //NULLs m_pIOleIPActiveObject if pIIPActiveObj is NULL m_pIOleIPActiveObject = pIIPActiveObj; if ( NULL != m_pIOleIPActiveObject ) { m_pIOleIPActiveObject->AddRef(); m_pIOleIPActiveObject->GetWindow( &m_hWndObj ); } return S_OK; } /* * CProxyFrame::InsertMenus * * Purpose: * Instructs the container to place its in-place menu items where * necessary in the given menu and to fill in elements 0, 2, and 4 * of the OLEMENUGROUPWIDTHS array to indicate how many top-level * items are in each group. * * Parameters: * hMenu HMENU in which to add popups. * pMGW LPOLEMENUGROUPWIDTHS in which to store the * width of each container menu group. * * Return Value: * HRESULT E_NOTIMPL */ STDMETHODIMP CProxyFrame::InsertMenus( HMENU /*hMenu*/, LPOLEMENUGROUPWIDTHS /*pMGW*/ ) { // We've turned off the MSHTML.DLL Menus so we don't expect any merging to go on! return E_NOTIMPL; } /* * CProxyFrame::SetMenu * * Purpose: * Instructs the container to replace whatever menu it's currently * using with the given menu and to call OleSetMenuDescritor so OLE * knows to whom to dispatch messages. * * Parameters: * hMenu HMENU to show. * hOLEMenu HOLEMENU to the menu descriptor. * hWndObj HWND of the active object to which messages are * dispatched. * * Return Value: * HRESULT NOERROR */ STDMETHODIMP CProxyFrame::SetMenu( HMENU /*hMenu*/, HOLEMENU /*hOLEMenu*/, HWND /*hWndObj*/ ) { // We've turned off the MSHTML.DLL Menus so we don't expect any merging to go on! return E_NOTIMPL; } /* * CProxyFrame::RemoveMenus * * Purpose: * Asks the container to remove any menus it put into hMenu in * InsertMenus. * * Parameters: * hMenu HMENU from which to remove the container's * items. * * Return Value: * HRESULT NOERROR */ STDMETHODIMP CProxyFrame::RemoveMenus( HMENU /*hMenu*/ ) { // We've turned off the MSHTML.DLL Menus so we don't expect any merging to go on! return E_NOTIMPL; } /* * CProxyFrame::SetStatusText * * Purpose: * Asks the container to place some text in a status line, if one * exists. If the container does not have a status line it * should return E_FAIL here in which case the object could * display its own. * * Parameters: * pszText LPCOLESTR to display. * * Return Value: * HRESULT S_OK if successful, S_TRUNCATED if not all * of the text could be displayed, or E_FAIL if * the container has no status line. */ STDMETHODIMP CProxyFrame::SetStatusText( LPCOLESTR /*pszText*/ ) { return S_OK; } /* * CProxyFrame::EnableModeless * * Purpose: * Instructs the container to show or hide any modeless popup * windows that it may be using. * * Parameters: * fEnable BOOL indicating to enable/show the windows * (TRUE) or to hide them (FALSE). * * Return Value: * HRESULT S_OK */ STDMETHODIMP CProxyFrame::EnableModeless( BOOL /*fEnable*/ ) { return S_OK; } /* * CProxyFrame::TranslateAccelerator * * Purpose: * When dealing with an in-place object from an EXE server, this * is called to give the container a chance to process accelerators * after the server has looked at the message. * * Parameters: * pMSG LPMSG for the container to examine. * wID WORD the identifier in the container's * accelerator table (from IOleInPlaceSite * ::GetWindowContext) for this message (OLE does * some translation before calling). * * Return Value: * HRESULT NOERROR if the keystroke was used, * S_FALSE otherwise. */ STDMETHODIMP CProxyFrame::TranslateAccelerator( LPMSG /*pMSG*/, WORD /*wID*/ ) { return S_FALSE; } /* * IOleCommandTarget::QueryStatus */ STDMETHODIMP CProxyFrame::QueryStatus( const GUID* pguidCmdGroup, ULONG cCmds, OLECMD* prgCmds, OLECMDTEXT* pCmdText ) { if ( pguidCmdGroup != NULL ) { // It's a nonstandard group!! return OLECMDERR_E_UNKNOWNGROUP; } MSOCMD* pCmd; INT c; HRESULT hr = S_OK; // By default command text is NOT SUPPORTED. if ( pCmdText && ( pCmdText->cmdtextf != OLECMDTEXTF_NONE ) ) { pCmdText->cwActual = 0; } // Loop through each command in the array, setting the status of each. for ( pCmd = prgCmds, c = cCmds; --c >= 0; pCmd++ ) { // By default command status is NOT SUPPORTED. pCmd->cmdf = 0; switch ( pCmd->cmdID ) { case OLECMDID_UPDATECOMMANDS: pCmd->cmdf = OLECMDF_SUPPORTED; break; case OLECMDID_NEW: case OLECMDID_OPEN: case OLECMDID_SAVE: pCmd->cmdf = (MSOCMDF_SUPPORTED | MSOCMDF_ENABLED); break; } } return (hr); } /* * IOleCommandTarget::Exec */ STDMETHODIMP CProxyFrame::Exec( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD /*nCmdexecopt*/, VARIANTARG* /*pvaIn*/, VARIANTARG* /*pvaOut*/ ) { HRESULT hr = S_OK; if ( pguidCmdGroup == NULL ) { switch (nCmdID) { case OLECMDID_UPDATECOMMANDS: { // Fires event to container. m_pCtl->Fire_DisplayChanged(); hr = S_OK; } break; default: hr = OLECMDERR_E_NOTSUPPORTED; break; } } else { hr = OLECMDERR_E_UNKNOWNGROUP; } return (hr); } // Connector from control to site. // void CProxyFrame::UpdateObjectRects() { _ASSERTE ( m_pSite ); if ( NULL != m_pSite ) { m_pSite->UpdateObjectRects(); } } // Called from the control's TranslateAccelerator. // Try our own (VID-like) acclerators first, and if not handled pass them along to TriEdit. // HRESULT CProxyFrame::HrTranslateAccelerator(LPMSG lpmsg) { HRESULT hr = S_OK; if (NULL != m_pIOleIPActiveObject) { _ASSERTE(lpmsg); hr = HrHandleAccelerator(lpmsg); if (hr != S_OK) { _ASSERTE(SUCCEEDED(hr)); hr = m_pIOleIPActiveObject->TranslateAccelerator(lpmsg); } } return hr; } // A lot of time was lost here in scenarios like clicking on/tabbing to a control // embedded in a VB OCX, tabbing to a control on a page, etc. // Exercise great caution and perform a lot of testing if this is changed. // LRESULT CProxyFrame::OnSetFocus(UINT /*nMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if ( ! m_pCtl->m_bUIActive ) { m_pCtl->DoVerbUIActivate ( NULL, NULL ); } // Give the focus to the ActiveX Document window if ( m_hWndObj != NULL ) { ::SetFocus( m_hWndObj ); bHandled = TRUE; } // activate Trident with "Show" m_pSite->Activate(OLEIVERB_SHOW); return 0; } // Sets the Trident window's parent correctly when created and destroyed. // void CProxyFrame::SetParent ( HWND hwndParent ) { // This may be called before the control has been drawn. if ( NULL != m_hWndObj ) { if( hwndParent ) { HWND hwndOldParent = ::SetParent ( m_hWndObj, hwndParent ); if ( NULL == hwndOldParent ) { DWORD dwErr = 0; dwErr = GetLastError (); } _ASSERTE ( m_pSite ); m_pSite->SetWindow ( hwndParent ); } } } // Handles WM_SHOWWINDOW messages directed to the control. // void CProxyFrame::Show ( WPARAM nCmdShow ) { // This may be called before the control has been drawn. // Hide or show the hosted Trident if ( NULL != m_hWndObj ) { ::ShowWindow ( m_hWndObj, (int)nCmdShow ); } } /////////////////////////////////////////////////////////////////////////////////////////// // // ExecCommand mechanism // /////////////////////////////////////////////////////////////////////////////////////////// // Convert a command ID into a TriEdit command ID. // Some commands used to represent other command groups as well, thus the ppguidCmdGroup parameter. // While this does little now, it may be useful again in the future. // HRESULT CProxyFrame::HrMapCommand(DHTMLEDITCMDID typeLibCmdID, ULONG* cmdID, const GUID** ppguidCmdGroup, BOOL* pbOutParam) { _ASSERTE(cmdID); _ASSERTE(ppguidCmdGroup); _ASSERTE(pbOutParam); *cmdID = 0; *ppguidCmdGroup = NULL; *pbOutParam = FALSE; for (UINT i=0; i < sizeof(cmdMap)/sizeof(CommandMap); ++i) { if (typeLibCmdID == cmdMap[i].typeLibCmdID) { *cmdID = cmdMap[i].cmdID; *ppguidCmdGroup = &GUID_TriEditCommandGroup; *pbOutParam = cmdMap[i].bOutParam; return S_OK; } } return OLECMDERR_E_NOTSUPPORTED ; } // Helper routine for calling Exec. // HRESULT CProxyFrame::HrExecCommand(const GUID* pguidCmdGroup, ULONG ucmdID, OLECMDEXECOPT cmdexecopt, VARIANT* pVarIn, VARIANT* pVarOut) { HRESULT hr = E_FAIL; LPOLECOMMANDTARGET pCommandTarget = NULL; // note that it is valid for pguidCmdGroup to be NULL _ASSERTE(m_pSite); if (NULL == m_pSite) return E_UNEXPECTED; pCommandTarget = m_pSite->GetCommandTarget(); _ASSERTE(pCommandTarget); if (pCommandTarget != NULL) { hr = pCommandTarget->Exec(pguidCmdGroup, ucmdID, cmdexecopt, pVarIn, pVarOut); } return hr; } // Main command dispatcher; called from the control's ExecCommand method. // Handle our unique commands here, pass the rest onto HrExecGenericCommands. // HRESULT CProxyFrame::HrMapExecCommand(DHTMLEDITCMDID deCommand, OLECMDEXECOPT cmdexecopt, VARIANT* pVarInput, VARIANT* pVarOutput) { HRESULT hr = S_OK; LPOLECOMMANDTARGET pCmdTgt = NULL; ULONG ulMappedCommand = 0; const GUID* pguidCmdGroup = NULL; BOOL bOutParam = FALSE; if (FALSE == m_fActivated) return E_UNEXPECTED; _ASSERTE(m_pSite); if (NULL == m_pSite) return E_UNEXPECTED; pCmdTgt = m_pSite->GetCommandTarget(); _ASSERTE(pCmdTgt); if (NULL == pCmdTgt) return E_UNEXPECTED; // Its valid for pVarInput to be NULL if (FAILED(hr = HrMapCommand(deCommand, &ulMappedCommand, &pguidCmdGroup, &bOutParam))) return hr; AssureActivated(); switch ( deCommand ) { case DECMD_GETBLOCKFMTNAMES: hr = HrExecGetBlockFmtNames(pVarInput); break; case DECMD_INSERTTABLE: hr = HrExecInsertTable(pVarInput); break; case DECMD_GETFORECOLOR: case DECMD_GETBACKCOLOR: hr = HrExecGetColor(deCommand, ulMappedCommand, pVarOutput); break; case DECMD_SETFONTSIZE: hr = HrExecSetFontSize(pVarInput); break; case DECMD_GETBLOCKFMT: // Trident inconsistancy: GetBlockFmt fails if outparam isn't a BSTR. GetFontName is OK with VT_EMPTY VariantChangeType ( pVarOutput, pVarOutput, 0, VT_BSTR ); // Fall through; do not break! case DECMD_GETFONTNAME: case DECMD_GETFONTSIZE: hr = HrExecGenericCommands(pguidCmdGroup, ulMappedCommand, cmdexecopt, pVarOutput, TRUE ); break; // Because our QueryStatus on DECMD_PROPERTIES returns TRUE for anything with IOleObject, executing the properties // verb can return an unexpected error. Therefore, we ALWAYS return S_OK from this command to avoid causing VB and // script to terminate. case DECMD_PROPERTIES: { CComVariant varParam; varParam.vt = VT_I4; varParam.lVal = OLEIVERB_PROPERTIES; hr = HrExecGenericCommands(pguidCmdGroup, ulMappedCommand, cmdexecopt, &varParam, FALSE ); hr = S_OK; } break; default: hr = HrExecGenericCommands(pguidCmdGroup, ulMappedCommand, cmdexecopt, pVarInput, bOutParam); break; } if (FAILED(hr)) { if (DISP_E_BADVARTYPE == hr || DISP_E_MEMBERNOTFOUND == hr) { // Map these Trident errors to something more general. // These errors can occur if Trident expected the element // it was trying to operate on to support certain interfaces. // The caller was trying to perform an operation not valid // for the current selection. Probably didn't call QueryStatus // first. hr = OLECMDERR_E_NOTSUPPORTED; } } return hr; } /////////////////////////////////////////////////////////////////////////////////////////// // // ExecCommand handler implementations // /////////////////////////////////////////////////////////////////////////////////////////// // Helper routine for calling Exec and dealing with results. // HRESULT CProxyFrame::HrExecGenericCommands(const GUID* pguidCmdGroup, ULONG cmdID, OLECMDEXECOPT cmdexecopt, LPVARIANT pVarInput, BOOL bOutParam) { HRESULT hr = S_OK; LPOLECOMMANDTARGET pCmdTgt = NULL; LPVARIANT _pVar = NULL; VARIANT varCopy; pCmdTgt = m_pSite->GetCommandTarget(); if (pVarInput && V_VT(pVarInput) & VT_BYREF) { // convert VARIANTARGs to Variant for use by Trident // this occurs in VB if the user specified a basic type // as an arg, i.e., String or Long, instead of Variant VariantInit(&varCopy); if (FAILED(hr = VariantCopyInd(&varCopy, pVarInput))) { _ASSERTE(SUCCEEDED(hr)); return hr; } _pVar = &varCopy; } else if (pVarInput) _pVar = pVarInput; if (bOutParam) { hr = pCmdTgt->Exec(pguidCmdGroup, cmdID, cmdexecopt, NULL, _pVar); } else { hr = pCmdTgt->Exec(pguidCmdGroup, cmdID, cmdexecopt, _pVar, NULL); } if (FAILED(hr)) goto cleanup; // if a VARIANTARG was passed in for a command with output then // fill it in with the result from the Exec if (bOutParam && pVarInput && (V_VT(pVarInput) & VT_BYREF)) { _ASSERTE(_pVar); // _pVar should always be non NULL here // if there was an input arg that was byref, // then it should have been mapped to _pVar if (NULL == _pVar) return E_UNEXPECTED; // the catch all error return for "we are in a weird state" // if the type of return is different that the type the caller // passed in then do nothing and return if (V_VT(_pVar) != (V_VT(pVarInput) ^ VT_BYREF)) return hr; switch(V_VT(_pVar)) { case VT_BSTR: _ASSERTE(V_VT(pVarInput) == (VT_BSTR|VT_BYREF)); if (V_BSTRREF(pVarInput)) hr = SysReAllocString(V_BSTRREF(pVarInput), V_BSTR(_pVar)); break; case VT_BOOL: _ASSERTE(V_VT(pVarInput) == (VT_BOOL|VT_BYREF)); if (V_BOOLREF(pVarInput)) *(V_BOOLREF(pVarInput)) = V_BOOL(_pVar); break; case VT_I4: _ASSERTE(V_VT(pVarInput) == (VT_I4|VT_BYREF)); if (V_I4REF(pVarInput)) *(V_I4REF(pVarInput)) = V_I4(_pVar); break; default: _ASSERTE(0); break; } } cleanup: // Our documentation replaces E_FAIL with DE_E_UNEXPECTED: different values. if ( E_FAIL == hr ) { hr = DE_E_UNEXPECTED; } return hr; } // Handler for command DECMD_GETBLOCKFMTNAMES. // There are plenty of possible types of arrays to be handled. // HRESULT CProxyFrame::HrExecGetBlockFmtNames(LPVARIANT pVarInput) { HRESULT hr = S_OK; LPOLECOMMANDTARGET pCmdTgt = NULL; VARIANT varArray; LPUNKNOWN pUnk = NULL; CComPtr piNamesParam; pCmdTgt = m_pSite->GetCommandTarget(); if (NULL == pVarInput) return E_INVALIDARG; if (V_VT(pVarInput) == (VT_BYREF|VT_DISPATCH)) { if (V_DISPATCHREF(pVarInput)) pUnk = *(V_DISPATCHREF(pVarInput)); else return E_INVALIDARG; } else if (V_VT(pVarInput) == VT_DISPATCH) { if (V_DISPATCH(pVarInput)) pUnk = V_DISPATCH(pVarInput); else return E_INVALIDARG; } else if (V_VT(pVarInput) == (VT_BYREF|VT_UNKNOWN)) { if (V_UNKNOWNREF(pVarInput)) pUnk = *(V_UNKNOWNREF(pVarInput)); else return E_INVALIDARG; } else if (V_VT(pVarInput) == VT_UNKNOWN) { if (V_UNKNOWN(pVarInput)) pUnk = V_UNKNOWN(pVarInput); else return E_INVALIDARG; } else return E_INVALIDARG; // This can happen in VB if an object that has not // been set with CreateObject has been passed in if (NULL == pUnk) return E_INVALIDARG; // Try to get the names object before // performing the command if (FAILED(hr = pUnk->QueryInterface(IID_IDEGetBlockFmtNamesParam, (LPVOID*) &piNamesParam))) return E_INVALIDARG; _ASSERTE((!piNamesParam) == FALSE); // Trident wants the vt to be specifically VT_ARRAY with // no type qualifer -- if you give one it fails even though // an array of BSTRs is returned VariantInit(&varArray); V_VT(&varArray) = VT_ARRAY; hr = pCmdTgt->Exec(&GUID_TriEditCommandGroup, IDM_TRIED_GETBLOCKFMTS, MSOCMDEXECOPT_DONTPROMPTUSER, NULL, &varArray); if (FAILED(hr)) goto cleanup; piNamesParam->put_Names(&varArray); cleanup: return hr; } // Handler for command DECMD_INSERTTABLE. // HRESULT CProxyFrame::HrExecInsertTable(LPVARIANT pVarInput) { HRESULT hr = S_OK; LPOLECOMMANDTARGET pCmdTgt = NULL; VARIANT varTableArray; LPUNKNOWN pUnk = NULL; CComPtr piTableParam; pCmdTgt = m_pSite->GetCommandTarget(); VariantInit(&varTableArray); if (NULL == pVarInput) return E_INVALIDARG; if (V_VT(pVarInput) == (VT_BYREF|VT_DISPATCH)) { if (V_DISPATCHREF(pVarInput)) pUnk = *(V_DISPATCHREF(pVarInput)); else return E_INVALIDARG; } else if (V_VT(pVarInput) == VT_DISPATCH) { if (V_DISPATCH(pVarInput)) pUnk = V_DISPATCH(pVarInput); else return E_INVALIDARG; } else if (V_VT(pVarInput) == (VT_BYREF|VT_UNKNOWN)) { if (V_UNKNOWNREF(pVarInput)) pUnk = *(V_UNKNOWNREF(pVarInput)); else return E_INVALIDARG; } else if (V_VT(pVarInput) == VT_UNKNOWN) { if (V_UNKNOWN(pVarInput)) pUnk = V_UNKNOWN(pVarInput); else return E_INVALIDARG; } else return E_INVALIDARG; // This can happen in VB if an object that has not // been set with CreateObject has been passed in if (NULL == pUnk) return E_INVALIDARG; if (FAILED(hr = pUnk->QueryInterface(IID_IDEInsertTableParam, (LPVOID*) &piTableParam))) return E_INVALIDARG; _ASSERTE((!piTableParam) == FALSE); if (FAILED(hr = HrGetTableSafeArray(piTableParam, &varTableArray))) { _ASSERTE(SUCCEEDED(hr)); return hr; } hr = pCmdTgt->Exec(&GUID_TriEditCommandGroup, IDM_TRIED_INSERTTABLE, MSOCMDEXECOPT_DONTPROMPTUSER, &varTableArray, NULL); return hr; } // Hanlder for commands DECMD_GETFORECOLOR and DECMD_GETBACKCOLOR. // Reply with a string in the format #RRGGBB or an empty string. // HRESULT CProxyFrame::HrExecGetColor(DHTMLEDITCMDID deCommand, ULONG ulMappedCommand, LPVARIANT pVarOutput) { USES_CONVERSION; HRESULT hr = S_OK; LPOLECOMMANDTARGET pCmdTgt = NULL; VARIANT varColorOut; TCHAR buf[32]; WCHAR* oleStr = NULL; pCmdTgt = m_pSite->GetCommandTarget(); if (NULL == pVarOutput) return E_INVALIDARG; // validate the command if (DECMD_GETFORECOLOR != deCommand && DECMD_GETBACKCOLOR != deCommand) return E_INVALIDARG; // validate the args if (V_VT(pVarOutput) == (VT_BYREF|VT_BSTR)) { if (NULL == V_BSTRREF(pVarOutput)) return E_INVALIDARG; } else if (V_VT(pVarOutput) == VT_BSTR) { if (NULL == V_BSTR(pVarOutput)) return E_INVALIDARG; } else if (V_VT(pVarOutput) != (VT_EMPTY) && V_VT(pVarOutput) != (VT_NULL)) return E_INVALIDARG; VariantInit(&varColorOut); V_VT(&varColorOut) = VT_I4; hr = pCmdTgt->Exec(&GUID_TriEditCommandGroup, ulMappedCommand, MSOCMDEXECOPT_DONTPROMPTUSER, NULL, &varColorOut); // Trident will return VT_NULL if color selection // was mixed or no text is selected, we return empty // string ("") in that case. buf[0] = 0; if (VT_I4 == V_VT(&varColorOut)) { ULONG ulColor = 0; ULONG r=0; ULONG g=0; ULONG b=0; ulColor = V_I4(&varColorOut); r = 0x000000ff & ulColor; g = (0x0000ff00 & ulColor) >> 8; b = (0x00ff0000 & ulColor) >> 16; wsprintf(buf, TEXT("#%02X%02X%02X"), r, g, b); } oleStr = T2OLE(buf); if (V_VT(pVarOutput) == (VT_BSTR|VT_BYREF)) hr = SysReAllocString(V_BSTRREF(pVarOutput), oleStr); else if (V_VT(pVarOutput) == (VT_BSTR)) hr = SysReAllocString(&(V_BSTR(pVarOutput)), oleStr); else if (V_VT(pVarOutput) == (VT_EMPTY) || V_VT(pVarOutput) == (VT_NULL)) { V_VT(pVarOutput) = VT_BSTR; V_BSTR(pVarOutput) = SysAllocString(oleStr); } return hr; } // Handler for command DECMD_SETFONTSIZE. // HRESULT CProxyFrame::HrExecSetFontSize(LPVARIANT pVarInput) { HRESULT hr = S_OK; LPOLECOMMANDTARGET pCmdTgt = NULL; VARIANT varSizeIn; pCmdTgt = m_pSite->GetCommandTarget(); if (NULL == pVarInput) return E_INVALIDARG; VariantInit(&varSizeIn); if (FAILED(hr = VariantChangeType(&varSizeIn, pVarInput, 0, VT_I4))) return E_INVALIDARG; if (varSizeIn.lVal < 0 || varSizeIn.lVal > 7) return E_INVALIDARG; if (0 == varSizeIn.lVal) varSizeIn.lVal = varSizeIn.lVal + 1; hr = pCmdTgt->Exec(&GUID_TriEditCommandGroup, IDM_TRIED_FONTSIZE, MSOCMDEXECOPT_DONTPROMPTUSER, &varSizeIn, NULL); return hr; } /////////////////////////////////////////////////////////////////////////////////////////// // // QueryStatus mechanism // /////////////////////////////////////////////////////////////////////////////////////////// // Map the control specific command ID to a TriEdit command ID and call QueryStatus. // HRESULT CProxyFrame::HrMapQueryStatus( DHTMLEDITCMDID ucmdID, DHTMLEDITCMDF* cmdf) { LPOLECOMMANDTARGET pCommandTarget = NULL; _ASSERTE(cmdf); HRESULT hr = E_FAIL; if (FALSE == m_fActivated) return E_UNEXPECTED; if (NULL == cmdf) return E_INVALIDARG; *cmdf = (DHTMLEDITCMDF) 0; _ASSERTE(m_pSite); if (NULL == m_pSite) return E_UNEXPECTED; pCommandTarget = m_pSite->GetCommandTarget(); _ASSERTE(pCommandTarget); if ( pCommandTarget != NULL ) { AssureActivated (); ULONG cmdID = 0; const GUID* pguidCmdGroup = NULL; BOOL bOutParam = FALSE; if (SUCCEEDED(hr = HrMapCommand(ucmdID, &cmdID, &pguidCmdGroup, &bOutParam))) { MSOCMD msocmd; msocmd.cmdID = cmdID; msocmd.cmdf = 0; hr = pCommandTarget->QueryStatus(pguidCmdGroup, 1, &msocmd, NULL); *cmdf = (DHTMLEDITCMDF) msocmd.cmdf; } } return hr; } // General routine for determining the status of a command. // Should resolve to not supported, disabled, enabled, latched or ninched. // HRESULT CProxyFrame::HrQueryStatus(const GUID* pguidCmdGroup, ULONG ucmdID, OLECMDF* cmdf) { HRESULT hr = E_FAIL; _ASSERTE(cmdf); // Note that it is valid for pguidCmdGroup to be NULL if (NULL == cmdf) return E_INVALIDARG; *cmdf = (OLECMDF) 0; _ASSERTE(m_pSite); if ( m_pSite != NULL ) // m_pSite should always be set { LPOLECOMMANDTARGET pCommandTarget = m_pSite->GetCommandTarget(); if ( pCommandTarget != NULL ) { MSOCMD msocmd; msocmd.cmdID = ucmdID; msocmd.cmdf = 0; hr = pCommandTarget->QueryStatus(pguidCmdGroup, 1, &msocmd, NULL); *cmdf = (OLECMDF) msocmd.cmdf; } } return hr; } // A tragic FAT16 compatibility problem: file names in the specific form: // [a-zA-z]\:[^\\].+ cause various, severe problems. NTFS "forgives". // We must detect these, both in file names and file:// URL and return an error. // BOOL CProxyFrame::IsMissingBackSlash ( BSTR path, BOOL bfIsURL ) { BOOL bfMissing = FALSE; if ( bfIsURL ) { WCHAR wszFileProtocol[] = L"file://"; int cchProtocol = wcslen ( wszFileProtocol ); if ( 0 == _wcsnicmp ( path, wszFileProtocol, cchProtocol ) ) { if ( OLECHAR(':') == path[cchProtocol+1] ) { if ( OLECHAR('\\') != path[cchProtocol+2] ) { bfMissing = TRUE; } } } } else { // Path name. chec for drive letter, colon, non-backslash. if ( OLECHAR(':') == path[1] ) { if ( OLECHAR('\\') != path[2] ) { bfMissing = TRUE; } } } return bfMissing; } /////////////////////////////////////////////////////////////////////////////////////////// // // Control methods and properties // /////////////////////////////////////////////////////////////////////////////////////////// // Handles NewDocument, LoadURL and LoadDocument control methods. // The document is loaded indirectly via the pluggable protocol handler. // If "path" is NULL, do NewDocument. TestbfURL to see if it's a URL or UNC path. // HRESULT CProxyFrame::LoadDocument(BSTR path, BOOL bfIsURL ) { USES_CONVERSION; HRESULT hr = S_OK; UINT pathLen = 0; AssureActivated (); // This can set m_bstrLoadText as a side effect in unactivated controls! Be careful! if (FALSE == m_fActivated) return E_UNEXPECTED; m_bstrLoadText.Empty (); // Clear the text to be added directly, or it will be used instead! m_bstrCurDocPath = L""; m_bstrBaseURL = L""; if (path) pathLen = ::SysStringLen(path); else pathLen = 0; // We've resetting the contents of the control. Go back to default save mechanism. // If we load Unicode it will be reset. m_pSite->SetSaveAsUnicode ( FALSE ); if (path && pathLen) { _ASSERTE(path); _ASSERTE(pathLen > 0); // First, look out for a wicked error: X:FileName with no '\' is BAD on FAT16. if ( IsMissingBackSlash ( path, bfIsURL ) ) { hr = DE_E_PATH_NOT_FOUND; LoadBSTRDeferred ( m_bstrInitialDoc ); goto error; } // Try to open the file -- stop the sequence // if its bogus or we don't have access if ( !bfIsURL ) { if (FAILED(hr = m_pSite->HrTestFileOpen(path))) { LoadBSTRDeferred ( m_bstrInitialDoc ); goto error; } } m_bfIsURL = bfIsURL; m_bstrCurDocPath = path; // This needs to be set before loading, because base url is needed durring load. SetBaseURLFromCurDocPath ( bfIsURL ); m_bfPreserveDirtyFlagAcrossBrowseMode = FALSE; CComPtr srpMoniker; CComPtr srpBindCtx; CComQIPtr srpPM (m_pUnkTriEdit); _ASSERTE ( srpPM ); if ( srpPM ) { CComBSTR bstrProtocol = m_wszProtocolPrefix; bstrProtocol += L"("; bstrProtocol += path; bstrProtocol += L")"; #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnCreateURLMoniker ); hr = (*m_pfnCreateURLMoniker)( NULL, bstrProtocol, &srpMoniker ); #else hr = CreateURLMoniker ( NULL, bstrProtocol, &srpMoniker ); #endif // LATE_BIND_URLMON_WININET _ASSERTE ( SUCCEEDED( hr ) ); if ( SUCCEEDED ( hr ) ) { hr = ::CreateBindCtx(NULL, &srpBindCtx); _ASSERTE ( SUCCEEDED( hr ) ); if ( SUCCEEDED ( hr ) ) { // Delete the cache entry before downloading. // This assures that loading, posting, and reloading works. // Bug 18544. // NOTE: Inexact match fails! http://www.microsoft.com fails, // because this actually loads/caches a specific default page. if ( bfIsURL ) { LPTSTR szURL = OLE2T ( m_bstrCurDocPath ); #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnDeleteUrlCacheEntry ); (*m_pfnDeleteUrlCacheEntry)( szURL ); #else DeleteUrlCacheEntry ( szURL ); #endif // LATE_BIND_URLMON_WININET } m_bfIsLoading = TRUE; m_hrDeferredLoadError = S_OK; // URLs: don't let Trident get the error! hr = srpPM->Load(FALSE, srpMoniker, srpBindCtx, STGM_READ); if ( SUCCEEDED ( hr ) && FAILED ( m_hrDeferredLoadError ) ) { hr = m_hrDeferredLoadError; // In case we stashed a result } if ( FAILED ( hr ) ) { m_bfIsLoading = FALSE; } } } } } else { if (FAILED(hr = LoadBSTRDeferred ( m_bstrInitialDoc ))) { _ASSERTE(SUCCEEDED(hr)); goto error; } } error: return hr; } // Implements FilterSourceCode control method // Used to restore filtered content extracted directly from DOM. // HRESULT CProxyFrame::FilterSourceCode ( BSTR bsSourceIn, BSTR* pbsSourceOut ) { HRESULT hr; CComPtr spStreamIn; IStream* piStreamOut; hr = m_pSite->HrBstrToStream(bsSourceIn, &spStreamIn); if ( SUCCEEDED ( hr ) ) { if ( m_vbBrowseMode ) { spStreamIn.p->AddRef (); piStreamOut = spStreamIn; } else { hr = m_pSite->HrFilter ( FALSE, spStreamIn, &piStreamOut, m_dwFilterOutFlags | dwFilterSourceCode); } if ( SUCCEEDED ( hr ) ) { hr = m_pSite->HrStreamToBstr ( piStreamOut, pbsSourceOut ); piStreamOut->Release (); } } return hr; } // Implements the control's Print method // HRESULT CProxyFrame::Print ( BOOL bfWithUI ) { AssureActivated (); if (FALSE == m_fActivated) return E_UNEXPECTED; return HrExecCommand ( &GUID_TriEditCommandGroup, IDM_TRIED_PRINT, bfWithUI ? MSOCMDEXECOPT_PROMPTUSER : MSOCMDEXECOPT_DONTPROMPTUSER, NULL, NULL ); } // Implements the control's Refresh method // HRESULT CProxyFrame::RefreshDoc () { if ( NULL != m_hWndObj ) { if ( ::IsWindow ( m_hWndObj ) ) { ::InvalidateRect ( m_hWndObj, NULL, TRUE ); return S_OK; } } return S_FALSE; } // Implements the control's SaveDocument method // HRESULT CProxyFrame::SaveDocument(BSTR path) { HRESULT hr = S_OK; ULONG pathLen = 0; if (FALSE == m_fActivated) return E_UNEXPECTED; _ASSERTE(GetState() == ESTATE_ACTIVATED); AssureActivated (); if (GetState() != ESTATE_ACTIVATED) return E_UNEXPECTED; _ASSERTE(path); if (path) pathLen = ::SysStringLen(path); else pathLen = 0; if (0 == pathLen) return E_INVALIDARG; _ASSERTE(pathLen); // First, look out for a wicked error: X:FileName with no '\' is BAD on FAT16. if ( IsMissingBackSlash ( path, FALSE ) ) { return DE_E_PATH_NOT_FOUND; } hr = m_pSite->HrSaveToFile(path, m_dwFilterOutFlags); if ( SUCCEEDED ( hr ) ) { m_bstrCurDocPath = path; } return hr; } // Implements the control's SetContextMenu method // One routine handles javascript arrays, the other simple arrays. // HRESULT CProxyFrame::SetContextMenu(LPVARIANT pVarMenuStrings, LPVARIANT pVarMenuStates) { if (V_VT(pVarMenuStrings) == VT_DISPATCH || V_VT(pVarMenuStates) == VT_DISPATCH) return SetContextMenuDispEx(pVarMenuStrings, pVarMenuStates); else return SetContextMenuSA(pVarMenuStrings, pVarMenuStates); } // Get menu strings from SafeArray // HRESULT CProxyFrame::SetContextMenuSA(LPVARIANT pVarMenuStrings, LPVARIANT pVarMenuStates) { HRESULT hr = S_OK; SAFEARRAY* psaStrings = NULL; SAFEARRAY* psaStates = NULL; LONG lLBound, lUBound, lLBoundState, lUBoundState; if (NULL == pVarMenuStrings || NULL == pVarMenuStates) return E_INVALIDARG; if ((VT_ARRAY|VT_BSTR) != V_VT(pVarMenuStrings) && ((VT_ARRAY|VT_BSTR)|VT_BYREF) != V_VT(pVarMenuStrings) && ((VT_ARRAY|VT_VARIANT)|VT_BYREF) != V_VT(pVarMenuStrings) && (VT_ARRAY|VT_VARIANT) != V_VT(pVarMenuStrings)) return E_INVALIDARG; if ((VT_ARRAY|VT_I4) != V_VT(pVarMenuStates) && ((VT_ARRAY|VT_I4)|VT_BYREF) != V_VT(pVarMenuStates) && ((VT_ARRAY|VT_VARIANT)|VT_BYREF) != V_VT(pVarMenuStates) && (VT_ARRAY|VT_VARIANT) != V_VT(pVarMenuStates)) return E_INVALIDARG; if ((VT_ARRAY|VT_BSTR) == V_VT(pVarMenuStrings)) { psaStrings = V_ARRAY(pVarMenuStrings); } if ((VT_ARRAY|VT_VARIANT) == V_VT(pVarMenuStrings)) { psaStrings = V_ARRAY(pVarMenuStrings); } else if ((VT_ARRAY|VT_BSTR|VT_BYREF) == V_VT(pVarMenuStrings)) { if (NULL == V_ARRAYREF(pVarMenuStrings)) return E_INVALIDARG; psaStrings = *(V_ARRAYREF(pVarMenuStrings)); } else if ((VT_ARRAY|VT_VARIANT|VT_BYREF) == V_VT(pVarMenuStrings)) { if (NULL == V_ARRAYREF(pVarMenuStrings)) return E_INVALIDARG; psaStrings = *(V_ARRAYREF(pVarMenuStrings)); } if ((VT_ARRAY|VT_I4) == V_VT(pVarMenuStates)) { psaStates = V_ARRAY(pVarMenuStates); } if ((VT_ARRAY|VT_VARIANT) == V_VT(pVarMenuStates)) { psaStates = V_ARRAY(pVarMenuStates); } else if ((VT_ARRAY|VT_I4|VT_BYREF) == V_VT(pVarMenuStates)) { if (NULL == V_ARRAYREF(pVarMenuStates)) return E_INVALIDARG; psaStates = *(V_ARRAYREF(pVarMenuStates)); } else if ((VT_ARRAY|VT_VARIANT|VT_BYREF) == V_VT(pVarMenuStates)) { if (NULL == V_ARRAYREF(pVarMenuStates)) return E_INVALIDARG; psaStates = *(V_ARRAYREF(pVarMenuStates)); } if (NULL == psaStrings || NULL == psaStates) return E_INVALIDARG; SafeArrayGetLBound(psaStrings, 1, &lLBound); SafeArrayGetUBound(psaStrings, 1, &lUBound); SafeArrayGetLBound(psaStates, 1, &lLBoundState); SafeArrayGetUBound(psaStates, 1, &lUBoundState); if (lLBound != lLBoundState || lUBound != lUBoundState) return E_INVALIDARG; if (m_pMenuStrings) { SafeArrayDestroy(m_pMenuStrings); m_pMenuStrings = NULL; } if (m_pMenuStates) { SafeArrayDestroy(m_pMenuStates); m_pMenuStates = NULL; } // An empty array was passed in // The context menu has been cleared if (lLBound ==lUBound ) goto cleanup; if (FAILED(hr = SafeArrayCopy(psaStrings, &m_pMenuStrings))) goto cleanup; if (FAILED(hr = SafeArrayCopy(psaStates, &m_pMenuStates))) goto cleanup; cleanup: if (FAILED(hr)) { if (m_pMenuStrings) { SafeArrayDestroy(m_pMenuStrings); m_pMenuStrings = NULL; } if (m_pMenuStates) { SafeArrayDestroy(m_pMenuStates); m_pMenuStates = NULL; } } return hr; } // Get menu strings from JScript array, or object that supports IDispatchEx // For iterating through JScript arrays, we expect the elements // to be accessable by ordinals starting at 0, i.e., a 0 based array // HRESULT CProxyFrame::SetContextMenuDispEx(LPVARIANT pVarMenuStrings, LPVARIANT pVarMenuStates) { HRESULT hr = S_OK; ULONG i=0; ULONG ulStringsLen = 0; ULONG ulStatesLen = 0; IDispatch* pdStrings = NULL; IDispatch* pdStates = NULL; IDispatchEx* pdexStrings = NULL; IDispatchEx* pdexStates = NULL; CDispExArray dispStrings; CDispExArray dispStates; VARIANT varString; VARIANT varState; SAFEARRAYBOUND rgsabound[1] = {0}; LONG ix[1] = {0}; if (VT_DISPATCH != V_VT(pVarMenuStrings) || VT_DISPATCH != V_VT(pVarMenuStates)) return E_INVALIDARG; VariantInit(&varString); VariantInit(&varState); pdStrings = V_DISPATCH(pVarMenuStrings); pdStates = V_DISPATCH(pVarMenuStates); _ASSERTE(pdStrings); _ASSERTE(pdStates); if (FAILED(hr = pdStrings->QueryInterface(IID_IDispatchEx, (LPVOID*) &pdexStrings))) { return E_INVALIDARG; } dispStrings.Attach(pdexStrings); if (FAILED(hr = pdStates->QueryInterface(IID_IDispatchEx, (LPVOID*) &pdexStates))) { return E_INVALIDARG; } dispStates.Attach(pdexStates); if (FAILED(dispStrings.HrGetLength(&ulStringsLen))) goto cleanup; if (FAILED(dispStates.HrGetLength(&ulStatesLen))) goto cleanup; // Make sure that arrays are equal length if (ulStringsLen != ulStatesLen) return E_INVALIDARG; if (m_pMenuStrings) { SafeArrayDestroy(m_pMenuStrings); m_pMenuStrings = NULL; } if (m_pMenuStates) { SafeArrayDestroy(m_pMenuStates); m_pMenuStates = NULL; } // An empty array was passed in // The context menu has been cleared if (ulStringsLen <= 0) goto cleanup; rgsabound[0].lLbound = 0; rgsabound[0].cElements = ulStringsLen; m_pMenuStrings = SafeArrayCreate(VT_BSTR, 1, rgsabound); _ASSERTE(m_pMenuStrings); if (NULL == m_pMenuStrings) { hr = E_OUTOFMEMORY; goto cleanup; } m_pMenuStates = SafeArrayCreate(VT_I4, 1, rgsabound); _ASSERTE(m_pMenuStates); if (NULL == m_pMenuStates) { hr = E_OUTOFMEMORY; goto cleanup; } // For iterating through JScript arrays, we expect the elements // to be accessable by ordinals starting at 0, i.e., a 0 based array hr = S_OK; for (i=0; i < ulStringsLen && hr != S_FALSE; i++) { if (FAILED(hr = dispStrings.HrGetElement(i, &varString))) goto cleanup; if (FAILED(hr = dispStates.HrGetElement(i, &varState))) goto cleanup; if (VT_BSTR != V_VT(&varString) || VT_I4 != V_VT(&varState)) { hr = E_INVALIDARG; goto cleanup; } ix[0] = i; if (FAILED(hr = SafeArrayPutElement(m_pMenuStrings, ix, (LPVOID) V_BSTR(&varString)))) goto cleanup; if (FAILED(hr = SafeArrayPutElement(m_pMenuStates, ix, (LPVOID) &(V_I4(&varState))))) goto cleanup; VariantClear ( &varString ); VariantClear ( &varState ); } cleanup: if (FAILED(hr)) { if (m_pMenuStrings) { SafeArrayDestroy(m_pMenuStrings); m_pMenuStrings = NULL; } if (m_pMenuStates) { SafeArrayDestroy(m_pMenuStates); m_pMenuStates = NULL; } } return hr; } // DocumentTitle property implementation; read only. // Get the property from the HTML document. // HRESULT CProxyFrame::GetDocumentTitle ( CComBSTR& bstrTitle ) { HRESULT hr = S_OK; DISPID dispid; DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; CComVariant varResult; CComPtr piHtmlDoc = NULL; hr = HrGetDoc( &piHtmlDoc ); if ( SUCCEEDED ( hr ) ) { AssureActivated(); hr = piHtmlDoc->GetIDsOfNames ( IID_NULL, &g_wszHTMLTitlePropName, 1, LOCALE_SYSTEM_DEFAULT, &dispid ); _ASSERTE ( SUCCEEDED ( hr ) ); if ( FAILED ( hr ) ) { return hr; } hr = piHtmlDoc->Invoke ( dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &varResult, NULL, NULL ); _ASSERTE ( SUCCEEDED ( hr ) ); if ( FAILED ( hr ) ) { return hr; } hr = varResult.ChangeType ( VT_BSTR ); _ASSERTE ( SUCCEEDED ( hr ) ); if ( FAILED ( hr ) ) { return hr; } bstrTitle = varResult.bstrVal; } return hr; } // Implements getting the control's BrowseMode property // HRESULT CProxyFrame::GetBrowseMode ( VARIANT_BOOL *pVal ) { *pVal = m_vbBrowseMode; return S_OK; } // Implements setting the control's BrowseMode property // HRESULT CProxyFrame::SetBrowseMode ( VARIANT_BOOL newVal ) { HRESULT hr = S_FALSE; // Indicates value was set, but actual mode was not changed. _ASSERTE ( m_pSite ); // If we're still reading the property bag, just set the value, don't change the text; // it hasn't been loaded yet. if ( NULL == m_pSite->GetCommandTarget() ) { m_vbBrowseMode = newVal; hr = S_OK; } else { if ( m_vbBrowseMode != newVal ) { AssureActivated (); m_bfModeSwitched = TRUE; if ( newVal && m_pCtl->IsUserMode () ) // newVal means "switching to browse mode" { CComPtr spStream = NULL; HrGetIsDirty ( m_bfPreserveDirtyFlagAcrossBrowseMode ); hr = m_pSite->HrSaveToStreamAndFilter ( &spStream, m_dwFilterOutFlags ); if ( SUCCEEDED ( hr ) ) { m_bstrLoadText.Empty (); // Preserve the byte order mark, or else it will not be reloaded properly hr = m_pSite->HrStreamToBstr ( spStream, &m_bstrLoadText, TRUE ); } } m_vbBrowseMode = newVal; // Let Trident know the ambient property has changed. CComQIPtrspioc ( m_pSite->GetObjectUnknown() ); if ( spioc ) { m_bfIsLoading = TRUE; spioc->OnAmbientPropertyChange ( DISPID_AMBIENT_USERMODE ); } } } return hr; } // Implements getting the control's UseDivOnCarriageReturn property // HRESULT CProxyFrame::GetDivOnCr ( VARIANT_BOOL *pVal ) { *pVal = m_vbUseDivOnCr; return S_OK; } // Implements setting the control's UseDivOnCarriageReturn property // HRESULT CProxyFrame::SetDivOnCr ( VARIANT_BOOL newVal ) { HRESULT hr = S_OK; CComVariant varDefBlock; m_vbUseDivOnCr = newVal; // Reinitialize if we haven't loaded our properties before this point. if ( READYSTATE_UNINITIALIZED == m_readyState ) { // InitializeDocString takes m_vbUseDivOnCr into account InitializeDocString (); } return hr; } // Implements getting the control's read-only Busy property // HRESULT CProxyFrame::GetBusy ( VARIANT_BOOL *pVal ) { #pragma warning(disable: 4310) // cast truncates constant value *pVal = ( m_bfIsLoading ) ? VARIANT_TRUE : VARIANT_FALSE; #pragma warning(default: 4310) // cast truncates constant value return S_OK; } // Implements setting the control's ActivateActiveXControls property // HRESULT CProxyFrame::HrSetPropActivateControls(BOOL activateControls) { HRESULT hr = S_OK; if (m_fActivated) { if (SUCCEEDED(hr = HrTridentSetPropBool(IDM_NOACTIVATENORMALOLECONTROLS, !activateControls))) m_fActivateControls = activateControls; } else m_fActivateControls = activateControls; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's ActivateActiveXControls property // HRESULT CProxyFrame::HrGetPropActivateControls(BOOL& activateControls) { HRESULT hr = S_OK; activateControls = m_fActivateControls; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements setting the control's ActivateApplets property // HRESULT CProxyFrame::HrSetPropActivateApplets(BOOL activateApplets) { HRESULT hr = S_OK; if (m_fActivated) { if (SUCCEEDED(hr = HrTridentSetPropBool(IDM_NOACTIVATEJAVAAPPLETS, !activateApplets))) m_fActivateApplets = activateApplets; } else m_fActivateApplets = activateApplets; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's ActivateApplets property // HRESULT CProxyFrame::HrGetPropActivateApplets(BOOL& activateApplets) { HRESULT hr = S_OK; activateApplets = m_fActivateApplets; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements setting the control's ActivateDTCs property // HRESULT CProxyFrame::HrSetPropActivateDTCs(BOOL activateDTCs) { HRESULT hr = S_OK; if (m_fActivated) { if (SUCCEEDED(hr = HrTridentSetPropBool(IDM_NOACTIVATEDESIGNTIMECONTROLS, !activateDTCs))) m_fActivateDTCs = activateDTCs; } else m_fActivateDTCs = activateDTCs; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's ActivateDTCs property // HRESULT CProxyFrame::HrGetPropActivateDTCs(BOOL& activateDTCs) { HRESULT hr = S_OK; activateDTCs = m_fActivateDTCs; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements setting the control's ShowDetails property // HRESULT CProxyFrame::HrSetPropShowAllTags(BOOL showAllTags) { HRESULT hr = S_OK; if (m_fActivated) { if (SUCCEEDED(hr = HrTridentSetPropBool(IDM_SHOWALLTAGS, showAllTags))) m_fShowAllTags = showAllTags; } else m_fShowAllTags = showAllTags; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's ShowDetails property // HRESULT CProxyFrame::HrGetPropShowAllTags(BOOL& showAllTags) { HRESULT hr = S_OK; showAllTags = m_fShowAllTags; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements setting the control's ShowBorders property // HRESULT CProxyFrame::HrSetPropShowBorders(BOOL showBorders) { HRESULT hr = S_OK; if (m_fActivated) { if (SUCCEEDED(hr = HrTridentSetPropBool(IDM_SHOWZEROBORDERATDESIGNTIME, showBorders))) m_fShowBorders = showBorders; } else m_fShowBorders = showBorders; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's ShowBorders property // HRESULT CProxyFrame::HrGetPropShowBorders(BOOL& showBorders) { HRESULT hr = S_OK; showBorders = m_fShowBorders; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements setting the control's Appearance property // HRESULT CProxyFrame::HrSetDisplay3D(BOOL bVal) { m_fDisplay3D = bVal; return S_OK; } // Implements getting the control's Appearance property // HRESULT CProxyFrame::HrGetDisplay3D(BOOL& bVal) { bVal = m_fDisplay3D; return S_OK; } // Implements setting the control's Scrollbars property // HRESULT CProxyFrame::HrSetScrollbars(BOOL bVal) { m_fScrollbars = bVal; return S_OK; } // Implements getting the control's Scrollbars property // HRESULT CProxyFrame::HrGetScrollbars(BOOL& bVal) { bVal = m_fScrollbars; return S_OK; } // Implements setting the control's ScrollbarAppearance property // HRESULT CProxyFrame::HrSetDisplayFlatScrollbars(BOOL bVal) { m_fDisplayFlatScrollbars = bVal; return S_OK; } // Implements getting the control's ScrollbarAppearance property // HRESULT CProxyFrame::HrGetDisplayFlatScrollbars(BOOL& bVal) { bVal = m_fDisplayFlatScrollbars; return S_OK; } // Implements setting the control's AbsoluteDropMode property // HRESULT CProxyFrame::HrSetAbsoluteDropMode(BOOL dropMode) { HRESULT hr = S_OK; if (m_fActivated) { VARIANT var; VariantInit(&var); V_VT(&var) = VT_BOOL; #pragma warning(disable: 4310) // cast truncates constant value V_BOOL(&var) = (dropMode) ? VARIANT_TRUE : VARIANT_FALSE; #pragma warning(default: 4310) // cast truncates constant value if (SUCCEEDED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_SET_2D_DROP_MODE, MSOCMDEXECOPT_DONTPROMPTUSER, &var, NULL))) m_fAbsoluteDropMode = dropMode; } else m_fAbsoluteDropMode = dropMode; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's AbsoluteDropMode property // HRESULT CProxyFrame::HrGetAbsoluteDropMode(BOOL& dropMode) { HRESULT hr = S_OK; dropMode = m_fAbsoluteDropMode; return hr; } // Implements setting the control's SnapToGrid property // HRESULT CProxyFrame::HrSetSnapToGrid(BOOL snapToGrid) { HRESULT hr = S_OK; if (m_fActivated) { VARIANT var; POINT pt = {0}; VariantInit(&var); if ( snapToGrid ) { pt.y = m_ulSnapToGridY; pt.x = m_ulSnapToGridX; } else { pt.y = 0; pt.x = 0; } V_VT(&var) = VT_BYREF; V_BYREF(&var) = &pt; if (SUCCEEDED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_SET_ALIGNMENT, MSOCMDEXECOPT_DONTPROMPTUSER, &var, NULL))) m_fSnapToGrid = snapToGrid; } else m_fSnapToGrid = snapToGrid; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's SnapToGrid property // HRESULT CProxyFrame::HrGetSnapToGrid(BOOL& snapToGrid) { HRESULT hr = S_OK; snapToGrid = m_fSnapToGrid; return hr; } // Implements setting the control's SnapToGridX property // HRESULT CProxyFrame::HrSetSnapToGridX(LONG snapToGridX) { HRESULT hr = S_OK; if ( 0 >= snapToGridX ) { return DE_E_INVALIDARG; } if (m_fActivated) { VARIANT var; POINT pt = {0}; VariantInit(&var); pt.x = snapToGridX; pt.y = m_ulSnapToGridY; V_VT(&var) = VT_BYREF; V_BYREF(&var) = &pt; if (SUCCEEDED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_SET_ALIGNMENT, MSOCMDEXECOPT_DONTPROMPTUSER, &var, NULL))) m_ulSnapToGridX = snapToGridX; } else m_ulSnapToGridX = snapToGridX; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's SnapToGridX property // HRESULT CProxyFrame::HrGetSnapToGridX(LONG& snapToGridX) { HRESULT hr = S_OK; snapToGridX = m_ulSnapToGridX; return hr; } // Implements setting the control's SnapToGridY property // HRESULT CProxyFrame::HrSetSnapToGridY(LONG snapToGridY) { HRESULT hr = S_OK; if ( 0 >= snapToGridY ) { return DE_E_INVALIDARG; } if (m_fActivated) { VARIANT var; POINT pt = {0}; VariantInit(&var); pt.y = snapToGridY; pt.x = m_ulSnapToGridX; V_VT(&var) = VT_BYREF; V_BYREF(&var) = &pt; if (SUCCEEDED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_SET_ALIGNMENT, MSOCMDEXECOPT_DONTPROMPTUSER, &var, NULL))) m_ulSnapToGridY = snapToGridY; } else m_ulSnapToGridY = snapToGridY; _ASSERTE(SUCCEEDED(hr)); return hr; } // Implements getting the control's SnapToGridY property // HRESULT CProxyFrame::HrGetSnapToGridY(LONG& snapToGridY) { HRESULT hr = S_OK; snapToGridY = m_ulSnapToGridY; return hr; } // Implements setting the control's DocumentHTML property // HRESULT CProxyFrame::HrSetDocumentHTML(BSTR bVal) { HRESULT hr = S_OK; _ASSERTE(bVal); if (NULL == bVal) return E_INVALIDARG; if (m_pCtl->IsUserMode ()) { hr = DE_E_UNEXPECTED; AssureActivated (); if ( m_fActivated ) { m_bstrBaseURL = L""; m_bfPreserveDirtyFlagAcrossBrowseMode = FALSE; if ( 0 == SysStringLen ( bVal ) ) { CComBSTR bstrMT = GetInitialHTML (); hr = LoadBSTRDeferred ( bstrMT ); } else { hr = LoadBSTRDeferred ( bVal ); } if ( FAILED ( hr ) ) { goto error; } // We've reset the contents of the control. Go back to default save mechanism. m_pSite->SetSaveAsUnicode ( FALSE ); } } error: return hr; } // Implements getting the control's DocumentHTML property // HRESULT CProxyFrame::HrGetDocumentHTML(BSTR* bVal) { HRESULT hr = S_OK; BOOL bfWasDirty = FALSE; _ASSERTE(bVal); if (NULL == bVal) return E_INVALIDARG; if ( m_bfIsLoading ) return DE_E_UNEXPECTED; // This is invalid while document is still loading. if ( FAILED ( hr = m_pSite->HrIsDirtyIPersistStreamInit(bfWasDirty) ) ) { _ASSERTE ( SUCCEEDED ( hr ) ); bfWasDirty = FALSE; // what else can we do in a situation like this? } AssureActivated (); if (m_fActivated) { _ASSERTE(m_pSite); hr = m_pSite->HrSaveToBstr(bVal, m_dwFilterOutFlags ); // Preserve original dirty state. if ( bfWasDirty ) { SetDirtyFlag ( TRUE ); } } return hr; } // Implements setting the control's SourceCodePreservation property // HRESULT CProxyFrame::HrSetPreserveSource(BOOL bVal) { m_fPreserveSource = bVal; if (m_fPreserveSource) m_dwFilterFlags = filterAll; else m_dwFilterFlags = filterDTCs | filterASP; return S_OK; } // Implements getting the control's SourceCodePreservation property // HRESULT CProxyFrame::HrGetPreserveSource(BOOL& bVal) { bVal = m_fPreserveSource; return S_OK; } // Implements getting the control's read-only IsDirty property // HRESULT CProxyFrame::HrGetIsDirty(BOOL& bVal) { HRESULT hr = S_OK; bVal = FALSE; AssureActivated (); if (m_fActivated) { hr = m_pSite->HrIsDirtyIPersistStreamInit(bVal); } return hr; } // Implements getting the BaseURL property // HRESULT CProxyFrame::GetBaseURL ( CComBSTR& bstrBaseURL ) { AssureActivated (); if ( NULL == m_bstrBaseURL.m_str ) { bstrBaseURL = L""; } else { bstrBaseURL = m_bstrBaseURL; } return S_OK; } // Implements setting the BaseURL property. // NOTE: // The BaseURL can't be (effectively) changed if there's a tag in // the document. Our pluggable Protocol's CombineURL is never called in this case, // so don't misguide the user by changing the property. // // Pay attention to m_bfBaseURLFromBASETag before calling to set the value from // the routine parsing the tag! // HRESULT CProxyFrame::SetBaseURL ( CComBSTR& bstrBaseURL ) { HRESULT hr = S_OK; _ASSERTE ( bstrBaseURL ); // Non-persisted property. Ignore if not in UserMode. if ( m_pCtl->IsUserMode () ) { if ( m_bfBaseURLFromBASETag ) { return S_FALSE; } else { if ( NULL == m_bstrBaseURL.m_str ) { m_bstrBaseURL = L""; } // If this test succeedes, the user has done something like x.BaseURL = x.DOM.url or // x.BaseURL = y.DOM.url. // Response: bstrBaseURL may be the bare protocol prefix, or a prefix with a URL attached // for example: dhtmled0:(http://www.microsoft.com). // Strip off the prefix and parens (if they exist) and use the interior URL. if ( 0 == _wcsnicmp ( bstrBaseURL.m_str, g_wszProtocolPrefix, wcslen ( g_wszProtocolPrefix ) ) ) { CComBSTR bstrNew = bstrBaseURL.m_str; // There must be a colon; it would be possibe to have a legitimate base url beginning with g_wszProtocolPrefix WCHAR* pwcURL = wcschr ( bstrNew, (WCHAR)':' ); if ( NULL != pwcURL ) { // Find the first open paren: pwcURL = wcschr ( pwcURL, (WCHAR)'(' ); if ( NULL == pwcURL ) { bstrBaseURL = L""; // No (...)? Set the Base to empty. Input must have been bare protocol ID. } else { pwcURL++; // Step past the paren. // Strip of dhtmledXXX:( ...to... ) and set the BaseURL to what remains. _ASSERTE ( (WCHAR)')' == pwcURL[wcslen(pwcURL)-1] ); if ( (WCHAR)')' == pwcURL[wcslen(pwcURL)-1] ) { pwcURL[wcslen(pwcURL)-1] = (WCHAR)'\0'; bstrBaseURL = pwcURL; } else { // Unexpected: ill formed pluggable protocol id: // starts with dhtml[n[n]]:( but does not end with ). // If we skipped it, we would crash. Best to use an empty base URL. bstrBaseURL = L""; } } } } if ( 0 != wcscmp ( m_bstrBaseURL.m_str, bstrBaseURL.m_str ) ) { m_bstrBaseURL = bstrBaseURL; m_bfIsLoading = TRUE; // Can't Exec without a command target: if ( NULL != m_pSite->GetCommandTarget() ) { // Reload the page, revaluating relative links. hr = HrExecCommand(&CGID_MSHTML, IDM_REFRESH, MSOCMDEXECOPT_DONTPROMPTUSER, NULL, NULL); } } } } return hr; } /////////////////////////////////////////////////////////////////////////////////////////// // // Accelerator handler implementations // /////////////////////////////////////////////////////////////////////////////////////////// // Nudge accelerator handler // Nudge the selection in the given direction by one pixle if SnaptoGrid is off, or by // the SnaptoGridX/Y quantity if SnapToGrid is on. // HRESULT CProxyFrame::HrNudge(DENudgeDirection dir) { HRESULT hr = S_FALSE; OLECMDF cmdf = (OLECMDF) 0; VARIANT var; LPVARIANT pVarIn = &var; LONG lXDelta = m_fSnapToGrid ? m_ulSnapToGridX : 1; LONG lYDelta = m_fSnapToGrid ? m_ulSnapToGridY : 1; if (FAILED(hr = HrQueryStatus(&GUID_TriEditCommandGroup, IDM_TRIED_NUDGE_ELEMENT, &cmdf))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } if (cmdf & OLECMDF_SUPPORTED && cmdf & OLECMDF_ENABLED) { LPPOINT lpPoint = new POINT; if (NULL == lpPoint) { hr = E_OUTOFMEMORY; goto cleanup; } _ASSERTE(lpPoint); lpPoint->x = 0; lpPoint->y = 0; // Set increment to snap to absolute grid, not relative grid. // Find the selections current position and set increment modulo that position. // This assures the first nudge snaps to a grid corner. if ( m_fSnapToGrid ) { POINT ptSelPos; if ( SUCCEEDED ( GetSelectionPos ( &ptSelPos ) ) ) { LONG lXNorm = ptSelPos.x % lXDelta; LONG lYNorm = ptSelPos.y % lYDelta; lXDelta = lXNorm ? lXNorm : lXDelta; lYDelta = lYNorm ? lYNorm : lYDelta; } } switch(dir) { case deNudgeUp: { lpPoint->x = 0; lpPoint->y = -lYDelta; } break; case deNudgeDown: { lpPoint->x = 0; lpPoint->y = lYDelta; } break; case deNudgeLeft: { lpPoint->x = -lXDelta; lpPoint->y = 0; } break; case deNudgeRight: { lpPoint->x = lXDelta; lpPoint->y = 0; } break; default: // move right by default { lpPoint->x = lXDelta; lpPoint->y = 0; } break; } VariantInit(pVarIn); V_VT(pVarIn) = VT_BYREF; V_BYREF(pVarIn) = lpPoint; if (FAILED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_NUDGE_ELEMENT, MSOCMDEXECOPT_DONTPROMPTUSER, pVarIn, NULL))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } hr = S_OK; } else hr = S_FALSE; cleanup: return hr; } // Accelerator handler // Toggle the absolute positioned property of the selected object // HRESULT CProxyFrame::HrToggleAbsolutePositioned() { HRESULT hr = S_FALSE; OLECMDF cmdf = (OLECMDF) 0; if (FAILED(hr = HrQueryStatus(&GUID_TriEditCommandGroup, IDM_TRIED_MAKE_ABSOLUTE, &cmdf))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } if (cmdf & OLECMDF_SUPPORTED && cmdf & OLECMDF_ENABLED) { if (FAILED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_MAKE_ABSOLUTE, MSOCMDEXECOPT_DONTPROMPTUSER, NULL, NULL))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } hr = S_OK; } cleanup: return hr; } // Accelerator handler // Make a link out of the current selection (with UI.) // HRESULT CProxyFrame::HrHyperLink() { HRESULT hr = S_FALSE; OLECMDF cmdf = (OLECMDF) 0; if (FAILED(hr = HrQueryStatus(&GUID_TriEditCommandGroup, IDM_TRIED_HYPERLINK, &cmdf))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } if (cmdf & OLECMDF_SUPPORTED && cmdf & OLECMDF_ENABLED) { if (FAILED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_HYPERLINK, MSOCMDEXECOPT_PROMPTUSER, NULL, NULL))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } hr = S_OK; } cleanup: return hr; } // Accelerator handler // Increase the indent of the current selection. // HRESULT CProxyFrame::HrIncreaseIndent() { HRESULT hr = S_FALSE; OLECMDF cmdf = (OLECMDF) 0; if (FAILED(hr = HrQueryStatus(&GUID_TriEditCommandGroup, IDM_TRIED_INDENT, &cmdf))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } if (cmdf & OLECMDF_SUPPORTED && cmdf & OLECMDF_ENABLED) { if (FAILED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_INDENT, MSOCMDEXECOPT_DONTPROMPTUSER, NULL, NULL))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } hr = S_OK; } cleanup: return hr; } // Accelerator handler // Decrease the indent of the current selection. // HRESULT CProxyFrame::HrDecreaseIndent() { HRESULT hr = S_FALSE; OLECMDF cmdf = (OLECMDF) 0; if (FAILED(hr = HrQueryStatus(&GUID_TriEditCommandGroup, IDM_TRIED_OUTDENT, &cmdf))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } if (cmdf & OLECMDF_SUPPORTED && cmdf & OLECMDF_ENABLED) { if (FAILED(hr = HrExecCommand(&GUID_TriEditCommandGroup, IDM_TRIED_OUTDENT, MSOCMDEXECOPT_DONTPROMPTUSER, NULL, NULL))) { _ASSERTE(SUCCEEDED(hr)); goto cleanup; } hr = S_OK; } cleanup: return hr; } // Check for and handle control-specific accelerators. If none is found, call TriEdit to handle it. // HRESULT CProxyFrame::HrHandleAccelerator(LPMSG lpmsg) { HRESULT hr = S_FALSE; BOOL fControl = (0x8000 & GetKeyState(VK_CONTROL)); BOOL fShift = (0x8000 & GetKeyState(VK_SHIFT)); BOOL fAlt = (0x8000 & GetKeyState(VK_MENU)); if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == VK_UP) { hr = HrNudge(deNudgeUp); } else if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == VK_DOWN) { hr = HrNudge(deNudgeDown); } else if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == VK_LEFT) { hr = HrNudge(deNudgeLeft); } else if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == VK_RIGHT) { hr = HrNudge(deNudgeRight); } else if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == 'K' && fControl) { hr = HrToggleAbsolutePositioned(); } else if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == 'L' && fControl && !fAlt) { hr = HrHyperLink(); } else if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == 'T' && !fShift && fControl) { hr = HrIncreaseIndent(); } else if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == 'T' && fShift && fControl) { hr = HrDecreaseIndent(); } else if (lpmsg->message == WM_KEYDOWN && lpmsg->wParam == VK_TAB && fControl) { // Process control-tab keys as belonging to the container; this allows the user // to tab out of the control in non-MDI apps. MDI uses control-tab to switch // windows, thus these apps (like VID) do not pass them to us. IOleControlSite* piControlSite = m_pCtl->GetControlSite (); _ASSERTE ( piControlSite ); if ( NULL != piControlSite ) { // Eat the control key, but preserve shift to perform reverse tabbing. // KEYMOD_SHIFT = 0x00000001, but isn't defined in any header... DWORD dwModifiers = fShift ? 1 : 0; hr = piControlSite->TranslateAccelerator ( lpmsg, dwModifiers ); } } return hr; } /////////////////////////////////////////////////////////////////////////////////////////// // // BaseURL helper routines // /////////////////////////////////////////////////////////////////////////////////////////// // Override the default BaseURL if there is one or more tags // in the document. If successful, set m_bfBaseURLFromBASETag to TRUE // If multiple BASE tags exist, simply use the last one. // Equivilent script: baseurl = document.all.tags("BASE")[].href, // where is derived. // HRESULT CProxyFrame::SetBaseURLFromBaseHref () { HRESULT hr = S_OK; CComBSTR bstrBase; if ( !m_bfBaseURLFromBASETag ) { if ( SUCCEEDED ( hr ) ) { CComPtr spHtmlDoc = NULL; hr = HrGetDoc( &spHtmlDoc ); if ( spHtmlDoc && SUCCEEDED ( hr ) ) { CComPtr spAll = NULL; hr = spHtmlDoc->get_all ( &spAll ); if ( spAll && SUCCEEDED ( hr ) ) { CComVariant varTag = L"BASE"; IDispatch* piDispTags = NULL; hr = spAll->tags ( varTag, &piDispTags ); if ( piDispTags && SUCCEEDED ( hr ) ) { CComQIPtr spBases (piDispTags); piDispTags->Release (); piDispTags = NULL; if ( spBases ) { long cBases = 0; hr = spBases->get_length ( &cBases ); if ( SUCCEEDED ( hr ) && ( 0 != cBases ) ) { CComVariant varName; varName.vt = VT_I2; for ( varName.iVal = 0; varName.iVal < cBases; varName.iVal++ ) { IDispatch* piDispBase = NULL; CComVariant varValue; hr = spBases->item ( varName, varName, &piDispBase ); if ( piDispBase && SUCCEEDED ( hr ) ) { CComQIPtr spElem ( piDispBase ); piDispBase->Release (); piDispBase = NULL; if ( spElem ) { varValue.Clear (); hr = spElem->getAttribute ( L"HREF", FALSE, &varValue ); if ( SUCCEEDED ( hr ) ) { hr = varValue.ChangeType ( VT_BSTR ); if ( SUCCEEDED ( hr ) ) { if ( 0 != SysStringLen ( varValue.bstrVal ) ) { bstrBase = varValue.bstrVal; } } } } } } if ( 0 != bstrBase.Length () ) { hr = SetBaseURL ( bstrBase ); // This clears m_bfBaseURLIsDefault m_bfBaseURLFromBASETag = TRUE; } } } } } } } } return hr; } // Set the m_bstrBaseURL value using m_bstrCurDocPath. // With URLs, it may be impossible to be certain about the correct BaseURL, // so make an intellegent guess. With files, it should be deterministic. // HRESULT CProxyFrame::SetBaseURLFromCurDocPath ( BOOL bfIsURL ) { m_bfBaseURLFromBASETag = FALSE; // We're reloading: whipe this out. if ( bfIsURL ) { return SetBaseURLFromURL ( m_bstrCurDocPath ); } else { return SetBaseURLFromFileName ( m_bstrCurDocPath ); } } // Given a URL_COMPONENTS with nScheme set to INTERNET_SCHEME_FILE, // modify the path part to reflect the base path, reconstruct the URL, // and set m_bstrBaseURL. // Separators may be \ or /. // HRESULT CProxyFrame::SetBaseUrlFromFileUrlComponents ( URL_COMPONENTS & urlc ) { TCHAR* pszPath; BOOL bfBackSlash = TRUE; HRESULT hr = S_OK; _ASSERTE ( INTERNET_SCHEME_FILE == urlc.nScheme ); _ASSERTE ( urlc.dwUrlPathLength ); if ( urlc.dwUrlPathLength <= 0) { return E_UNEXPECTED; } pszPath = new TCHAR [urlc.dwUrlPathLength + 3]; // Extra room for \0, dot, and /. if ( NULL != pszPath ) { TCHAR c = 0; int iPos = 0; // Scan backwards and modify in copy (never in BSTR, please) for beginning, '/' or '\' memcpy ( pszPath, urlc.lpszUrlPath, ( urlc.dwUrlPathLength + 1 ) * sizeof(TCHAR) ); for ( iPos = urlc.dwUrlPathLength - 1; iPos >= 0; iPos-- ) { c = pszPath[iPos]; pszPath[iPos] = '\0'; // Delete first, ask questions later. '\' must go. if ( '\\' == c ) { break; } if ( '/' == c ) { bfBackSlash = FALSE; break; } } // Space was reserved for an additional two characters, if needed. // If empty, add a dot. if ( 0 == _tcslen ( pszPath ) ) { _tcscat ( pszPath, TEXT(".") ); } // Add a / or \. if ( bfBackSlash ) { _tcscat ( pszPath, TEXT("\\") ); } else { _tcscat ( pszPath, TEXT("/") ); } urlc.lpszUrlPath = pszPath; urlc.dwUrlPathLength = _tcslen ( pszPath ); DWORD dwLen = 0; #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnInternetCreateUrl ); (*m_pfnInternetCreateUrl)( &urlc, 0, NULL, &dwLen ); // Get the size required. #else InternetCreateUrl ( &urlc, 0, NULL, &dwLen ); // Get the size required. #endif // LATE_BIND_URLMON_WININET _ASSERTE ( 0 != dwLen ); TCHAR* pszURL = new TCHAR [ dwLen + 1 ]; _ASSERTE ( pszURL ); if ( NULL != pszURL ) { // Incredibly, on Win98, the URL is terminated with a single byte \0. // Intializing this buffer to zero assures full termination of the string. dwLen += 1; memset ( pszURL, 0, sizeof(TCHAR) * dwLen ); #ifdef LATE_BIND_URLMON_WININET if ( (*m_pfnInternetCreateUrl)( &urlc, 0, pszURL, &dwLen ) ) #else if ( InternetCreateUrl ( &urlc, 0, pszURL, &dwLen ) ) #endif // LATE_BIND_URLMON_WININET { m_bstrBaseURL = pszURL; } else { hr = HRESULT_FROM_WIN32 ( GetLastError () ); } delete [] pszURL; } delete [] pszPath; } else { return E_FAIL; } return hr; } // The most complicated scenario for "guessing" at the base URL. // URLs like http://www.x.com/stuff could be either a file or a directory; // a default page might actually be loaded. We guess based on whether or // not the last item in the path contains a period. If so, we eliminate it. // We make sure the path ends with a '/'. // HRESULT CProxyFrame::SetBaseUrlFromUrlComponents ( URL_COMPONENTS & urlc ) { _ASSERTE ( INTERNET_SCHEME_FILE != urlc.nScheme ); BOOL bfPeriodIncluded = FALSE; HRESULT hr = S_OK; if ( 0 == urlc.dwSchemeLength ) { m_bstrBaseURL = L""; return S_FALSE; } // Scan backwards over path for beginning, '/' TCHAR c = 0; int iPos = 0; for ( iPos = urlc.dwUrlPathLength - 1; iPos >= 0; iPos-- ) { c = urlc.lpszUrlPath[iPos]; if ( '/' == c ) { break; } if ( '.' == c ) { bfPeriodIncluded = TRUE; } } if ( bfPeriodIncluded ) { if ( 0 > iPos ) iPos = 0; urlc.lpszUrlPath[iPos] = '\0'; // Truncate at the '/', or beginning urlc.dwUrlPathLength = _tcslen ( urlc.lpszUrlPath ); } // Recreate the URL: DWORD dwLen = 0; #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnInternetCreateUrl ); (*m_pfnInternetCreateUrl)( &urlc, 0, NULL, &dwLen ); // Get the size required. #else InternetCreateUrl ( &urlc, 0, NULL, &dwLen ); // Get the size required. #endif // LATE_BIND_URLMON_WININET _ASSERTE ( 0 != dwLen ); TCHAR* pszURL = new TCHAR [ dwLen + 1 ]; _ASSERTE ( pszURL ); if ( NULL != pszURL ) { dwLen += 1; memset ( pszURL, 0, sizeof(TCHAR) * dwLen ); #ifdef LATE_BIND_URLMON_WININET if ( (*m_pfnInternetCreateUrl)( &urlc, 0, pszURL, &dwLen ) ) #else if ( InternetCreateUrl ( &urlc, 0, pszURL, &dwLen ) ) #endif { m_bstrBaseURL = pszURL; // Append a '/' if needed. WCHAR wc = m_bstrBaseURL.m_str[m_bstrBaseURL.Length () - 1]; if ( ( WCHAR('/') != wc ) && ( NULL != urlc.lpszHostName ) ) // hostname: special case for user pluggable protocols { m_bstrBaseURL += L"/"; } } else { hr = HRESULT_FROM_WIN32 ( GetLastError () ); } delete [] pszURL; } return hr; } // Crack the URL, determine if it's a file scheme or other, and call the appropriate handler. // HRESULT CProxyFrame::SetBaseURLFromURL ( const CComBSTR& bstrURL ) { USES_CONVERSION; HRESULT hr = S_OK; URL_COMPONENTS urlc; TCHAR *ptszScheme = NULL; TCHAR *ptszHostName = NULL; TCHAR *ptszUrlPath = NULL; BOOL fSuccess = FALSE; TCHAR* tszURL = NULL; _ASSERTE ( 0 != bstrURL.Length () ); tszURL = OLE2T ( bstrURL ); _ASSERTE ( tszURL ); if ( NULL == tszURL ) { return E_OUTOFMEMORY; } memset ( &urlc, 0, sizeof ( urlc ) ); urlc.dwStructSize = sizeof ( urlc ); urlc.dwSchemeLength = 1; urlc.dwHostNameLength = 1; urlc.dwUrlPathLength = 1; #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnInternetCrackUrl ); fSuccess = (*m_pfnInternetCrackUrl)( tszURL, 0, 0, &urlc ); #else fSuccess = InternetCrackUrl ( tszURL, 0, 0, &urlc ); #endif // LATE_BIND_URLMON_WININET if ( !fSuccess ) { return E_FAIL; } if ( 0 != urlc.dwSchemeLength ) { urlc.dwSchemeLength++; ptszScheme = new TCHAR[urlc.dwSchemeLength]; urlc.lpszScheme = ptszScheme; if ( NULL == ptszScheme ) goto ONERROR; } if ( 0 != urlc.dwHostNameLength ) { urlc.dwHostNameLength++; ptszHostName = new TCHAR[urlc.dwHostNameLength]; urlc.lpszHostName = ptszHostName; if ( NULL == ptszHostName ) goto ONERROR; } if ( 0 != urlc.dwUrlPathLength ) { urlc.dwUrlPathLength++; ptszUrlPath = new TCHAR[urlc.dwUrlPathLength]; urlc.lpszUrlPath = ptszUrlPath; if ( NULL == ptszUrlPath ) goto ONERROR; } #ifdef LATE_BIND_URLMON_WININET fSuccess = (*m_pfnInternetCrackUrl)( tszURL, 0, 0, &urlc ); #else fSuccess = InternetCrackUrl ( tszURL, 0, 0, &urlc ); #endif if ( fSuccess ) { if ( INTERNET_SCHEME_FILE == urlc.nScheme ) { hr = SetBaseUrlFromFileUrlComponents ( urlc ); } else { hr = SetBaseUrlFromUrlComponents ( urlc ); } } ONERROR: if ( ptszScheme ) delete [] ptszScheme; if ( ptszHostName ) delete [] ptszHostName; if ( ptszUrlPath ) delete [] ptszUrlPath; return hr; } // Given a UNC file name, set the m_bstrBaseURL member variable. // if bstrFName is empty, set m_bstrBaseURL to empty. // Else, scan backward to the first "\" or the beginning of the string. // Truncate the string at this point. If the resultant string is empty, // add ".". Then, add "\". // HRESULT CProxyFrame::SetBaseURLFromFileName ( const CComBSTR& bstrFName ) { if ( 0 == bstrFName.Length () ) { m_bstrBaseURL = L""; } else { WCHAR* pwzstr = new WCHAR[bstrFName.Length () + 1]; _ASSERTE ( pwzstr ); if ( NULL != pwzstr ) { WCHAR wc = 0; int iPos = 0; // Scan backwards and modify in copy (never in BSTR, please) for beginning or '\' memcpy ( pwzstr, bstrFName.m_str, sizeof(WCHAR) * (bstrFName.Length () + 1) ); for ( iPos = wcslen ( pwzstr ) - 1; iPos >= 0; iPos-- ) { wc = pwzstr[iPos]; pwzstr[iPos] = WCHAR('\0'); // Delete first, ask questions later. '\' must go. if ( WCHAR('\\') == wc ) { break; } } m_bstrBaseURL = pwzstr; delete [] pwzstr; // If empty, add a '.' if ( 0 == m_bstrBaseURL.Length () ) { m_bstrBaseURL += L"."; } m_bstrBaseURL += L"\\"; } else { return E_FAIL; } } return S_OK; } /////////////////////////////////////////////////////////////////////////////////////////// // // Security oriented routines // /////////////////////////////////////////////////////////////////////////////////////////// // This is a critical security issue: // The pluggable protocol's ParseURL is called with PARSE_SECURITY_URL in the SFS control. // If the BaseURL is empty, and if we're hosted in Trident, we should return the // URL of the hosting page. // If there is no Trident host, say we're hosted in VB, return the bootdrive + : + /. // Bootdrive is not always C. // HRESULT CProxyFrame::GetSecurityURL (CComBSTR& bstrSecurityURL ) { HRESULT hr = S_OK; IOleClientSite *piClientSiteUnreffed = NULL; bstrSecurityURL = L""; piClientSiteUnreffed = m_pCtl->m_spClientSite; if ( NULL != piClientSiteUnreffed ) { CComPtr spContainer = NULL; hr = piClientSiteUnreffed->GetContainer ( &spContainer ); if ( SUCCEEDED ( hr ) && spContainer ) { CComQIPtr spHostDoc ( spContainer ); if ( spHostDoc ) { CComPtr spHostLoc = NULL; spHostDoc->get_location ( &spHostLoc ); if ( spHostLoc ) { BSTR bsOut; hr = spHostLoc->get_href ( &bsOut ); if ( SUCCEEDED ( hr ) ) { bstrSecurityURL.Empty (); bstrSecurityURL.Attach ( bsOut ); } } } else { // If we are not hosted in Trident, use local machine access: TCHAR tszDrive[4]; GetModuleFileName ( _Module.m_hInst, tszDrive, 3 ); // Get X:\. _ASSERTE ( TCHAR(':') == tszDrive[1] ); _ASSERTE ( TCHAR('\\') == tszDrive[2] ); bstrSecurityURL = tszDrive; hr = S_OK; } } } return hr; } /////////////////////////////////////////////////////////////////////////////////////////// // // Pluggable protocol oriented routines // /////////////////////////////////////////////////////////////////////////////////////////// // Register our pluggable protocol handler so dhtmledN[N...] is loaded by our code. // HRESULT CProxyFrame::RegisterPluggableProtocol() { HRESULT hr; // Get InternetSession CComPtr srpSession; #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnCoInternetGetSession ); hr = (*m_pfnCoInternetGetSession)(0, &srpSession, 0); #else hr = CoInternetGetSession (0, &srpSession, 0); #endif // LATE_BIND_URLMON_WININET if ( FAILED ( hr ) ) { return hr; } if(m_pProtInfo == NULL) { hr = CComObject::CreateInstance(&m_pProtInfo); if ( FAILED ( hr ) ) { return hr; } // CreateInstance - doesnt AddRef m_pProtInfo->GetUnknown()->AddRef(); } hr = srpSession->RegisterNameSpace( static_cast(m_pProtInfo), CLSID_DHTMLEdProtocol, m_wszProtocol, 0, NULL, 0); if ( FAILED ( hr ) ) { return hr; } CComQIPtr piPic ( m_pProtInfo ); _ASSERTE ( piPic ); piPic->SetProxyFrame ( (SIZE_T*)this ); ATLTRACE( _T("CProxyFrame::Registered ProtocolInfo\n")); return NOERROR; } // Unregister the pluggable protocol handler installed in RegisterPluggableProtocol // HRESULT CProxyFrame::UnRegisterPluggableProtocol() { if(m_pProtInfo == NULL) return E_UNEXPECTED; // Get InternetSession HRESULT hr; CComPtr srpSession; #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnCoInternetGetSession ); hr = (*m_pfnCoInternetGetSession)(0, &srpSession, 0); #else hr = CoInternetGetSession (0, &srpSession, 0); #endif // LATE_BIND_URLMON_WININET if(SUCCEEDED(hr)) { // UnRegister Protocol srpSession->UnregisterNameSpace( static_cast(m_pProtInfo), m_wszProtocol); } m_pProtInfo->GetUnknown()->Release(); m_pProtInfo = NULL; ATLTRACE(_T("CProxyFrame::UnRegistered ProtocolInfo\n")); return NOERROR; } // Workhorse routine that actually performs the loading of the control, including filtering. // ParseAndBind calls this to retrieve the data to be displayed in the control. // HRESULT CProxyFrame::GetFilteredStream ( IStream** ppStream ) { USES_CONVERSION; HRESULT hr = S_OK; LPTSTR pFileName = NULL; CComPtr piStream; BOOL bfLoadingFromBSTR = ( 0 != m_bstrLoadText.Length () ); *ppStream = NULL; m_bfReloadAttempted = TRUE; if ( !bfLoadingFromBSTR ) { _ASSERTE(m_bstrCurDocPath); pFileName = OLE2T(m_bstrCurDocPath); _ASSERTE(pFileName); if (NULL == pFileName) return E_OUTOFMEMORY; } if ( bfLoadingFromBSTR ) { hr = m_pSite->HrBstrToStream(m_bstrLoadText, &piStream); } else if ( m_bfIsURL ) { hr = m_pSite->HrURLToStream(pFileName, &piStream); } else { hr = m_pSite->HrFileToStream(pFileName, &piStream); } if (FAILED( hr )) { m_bstrCurDocPath.Empty (); m_bstrBaseURL.Empty (); // Get TriEdit into a reasonable state by loading an empty document // If we reinstanced successfully, this should never fail // Also, this will make ignoring the above assert benign if (FAILED(m_pSite->HrBstrToStream(m_bstrInitialDoc, ppStream))) { _ASSERTE(SUCCEEDED(hr)); } } else { if ( m_vbBrowseMode ) { piStream.p->AddRef (); *ppStream = piStream; } else { hr = m_pSite->HrFilter(TRUE, piStream, ppStream, m_dwFilterFlags); } if (FAILED(hr)) { m_pSite->HrBstrToStream(m_bstrInitialDoc, ppStream); } else { m_dwFilterOutFlags = m_dwFilterFlags; } } // Store the result to return from the (indirectly) called routine, // but don't return an error to ParseAndBind! if ( FAILED(hr) && ( ! bfLoadingFromBSTR ) ) { m_hrDeferredLoadError = hr; // Stash this away, we'll pic it up in LoadDocument hr = S_OK; } return hr; } /////////////////////////////////////////////////////////////////////////////////////////// // // Document event handling routines // /////////////////////////////////////////////////////////////////////////////////////////// HRESULT CProxyFrame::OnTriEditEvent ( const GUID& iidEventInterface, DISPID dispid ) { HRESULT hr = S_OK; if ( DIID_HTMLDocumentEvents == iidEventInterface ) { switch ( dispid ) { case DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS: case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN: if ( DISPID_HTMLDOCUMENTEVENTS_ONMOUSEDOWN == dispid ) { m_pCtl->Fire_onmousedown(); } else if ( DISPID_HTMLDOCUMENTEVENTS_ONKEYPRESS == dispid ) { m_pCtl->Fire_onkeypress(); } // Make the control UIActive if it was clicked in. Since the DocObject swallows the clicks, // the control isn't activated automatically. // Not needed in browse mode. if ( !m_pCtl->m_bUIActive && ! m_vbBrowseMode ) { m_pCtl->DoVerbUIActivate ( NULL, NULL ); if ( m_hWndObj != NULL ) { ::SetFocus( m_hWndObj ); } } break; case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEMOVE: m_pCtl->Fire_onmousemove(); break; case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEUP: m_pCtl->Fire_onmouseup(); // onclick is not delivered in edit mode. First one lost in broswe mode. m_pCtl->Fire_onclick(); break; case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOUT: m_pCtl->Fire_onmouseout(); break; case DISPID_HTMLDOCUMENTEVENTS_ONMOUSEOVER: m_pCtl->Fire_onmouseover(); break; case DISPID_HTMLDOCUMENTEVENTS_ONCLICK: // We do not fire the onclick event in response. // It is only delivered in browse mode, and in addition, // the first onclick is lost. We fire on onmouseup. //m_pCtl->Fire_onclick(); // The addition of the DesignMode property, in addition to // makes links exploitable for cross-zone access in the SFS control. // We must disable clicks (user and script) in the SFS control to prevent this. if ( m_pCtl->IsSafeForScripting ()) { CComPtr spHtmlDoc; hr = HrGetDoc ( &spHtmlDoc ); if ( SUCCEEDED ( hr ) && spHtmlDoc ) { CComPtr spWindow; hr = spHtmlDoc->get_parentWindow ( &spWindow ); if ( SUCCEEDED ( hr ) && spWindow ) { CComPtr spEvt; hr = spWindow->get_event ( &spEvt ); if ( SUCCEEDED ( hr ) && spEvt ) { CComVariant varFalse(VARIANT_FALSE); spEvt->put_cancelBubble ( VARIANT_TRUE ); spEvt->put_returnValue ( varFalse ); } } } } break; case DISPID_HTMLDOCUMENTEVENTS_ONDBLCLICK: m_pCtl->Fire_ondblclick(); break; case DISPID_HTMLDOCUMENTEVENTS_ONKEYDOWN: m_pCtl->Fire_onkeydown(); break; case DISPID_HTMLDOCUMENTEVENTS_ONKEYUP: m_pCtl->Fire_onkeyup(); break; case DISPID_HTMLDOCUMENTEVENTS_ONREADYSTATECHANGE: m_pCtl->Fire_onreadystatechange(); break; default: _ASSERTE ( TRUE ); break; } } else if ( DIID_HTMLWindowEvents == iidEventInterface ) { // I expected to get these, but I'm not... switch ( dispid ) { case DISPID_HTMLWINDOWEVENTS_ONLOAD: case DISPID_HTMLWINDOWEVENTS_ONUNLOAD: case DISPID_HTMLWINDOWEVENTS_ONHELP: case DISPID_HTMLWINDOWEVENTS_ONFOCUS: case DISPID_HTMLWINDOWEVENTS_ONBLUR: case DISPID_HTMLWINDOWEVENTS_ONERROR: case DISPID_HTMLWINDOWEVENTS_ONRESIZE: case DISPID_HTMLWINDOWEVENTS_ONSCROLL: case DISPID_HTMLWINDOWEVENTS_ONBEFOREUNLOAD: hr = S_OK; break; default: _ASSERTE ( TRUE ); break; } } return hr; } /////////////////////////////////////////////////////////////////////////////////////////// // // Dynamic loading routines, used in 4.0 versions // /////////////////////////////////////////////////////////////////////////////////////////// #ifdef LATE_BIND_URLMON_WININET // Load Urlmon and Wininet and get the proc addresses of every routine we use. // We must be able to register the control, even if these libraries are not installed. // NOTE: // This routine loads ANSI versions. Needs addaptation for UNICODE. // BOOL CProxyFrame::DynLoadLibraries () { m_hUlrMon = LoadLibrary ( TEXT("URLMON.DLL") ); m_hWinINet = LoadLibrary ( TEXT("WININET.DLL") ); if ( ( NULL == m_hUlrMon ) || ( NULL == m_hWinINet ) ) { DynUnloadLibraries (); return FALSE; } m_pfnCoInternetCombineUrl = (PFNCoInternetCombineUrl)GetProcAddress ( m_hUlrMon, "CoInternetCombineUrl" ); _ASSERTE ( m_pfnCoInternetCombineUrl ); m_pfnCoInternetParseUrl = (PFNCoInternetParseUrl)GetProcAddress ( m_hUlrMon, "CoInternetParseUrl" ); _ASSERTE ( m_pfnCoInternetParseUrl ); m_pfnCreateURLMoniker = (PFNCreateURLMoniker)GetProcAddress ( m_hUlrMon, "CreateURLMoniker" ); _ASSERTE ( m_pfnCreateURLMoniker ); m_pfnCoInternetGetSession = (PFNCoInternetGetSession)GetProcAddress ( m_hUlrMon, "CoInternetGetSession" ); _ASSERTE ( m_pfnCoInternetGetSession ); m_pfnURLOpenBlockingStream = (PFNURLOpenBlockingStream)GetProcAddress ( m_hUlrMon, "URLOpenBlockingStreamA" ); _ASSERTE ( m_pfnURLOpenBlockingStream ); m_pfnDeleteUrlCacheEntry = (PFNDeleteUrlCacheEntry)GetProcAddress ( m_hWinINet, "DeleteUrlCacheEntry" ); _ASSERTE ( m_pfnDeleteUrlCacheEntry ); m_pfnInternetCreateUrl = (PFNInternetCreateUrl)GetProcAddress ( m_hWinINet, "InternetCreateUrlA" ); _ASSERTE ( m_pfnInternetCreateUrl ); m_pfnInternetCrackUrl = (PFNInternetCrackURL)GetProcAddress ( m_hWinINet, "InternetCrackUrlA" ); _ASSERTE ( m_pfnInternetCrackUrl ); return ( m_pfnCoInternetCombineUrl && m_pfnCoInternetParseUrl && m_pfnCreateURLMoniker && m_pfnCoInternetGetSession && m_pfnURLOpenBlockingStream && m_pfnDeleteUrlCacheEntry && m_pfnInternetCreateUrl && m_pfnInternetCrackUrl ); } // Release the libraries loaded by DynLoadLibraries // void CProxyFrame::DynUnloadLibraries () { if ( NULL != m_hUlrMon ) { FreeLibrary ( m_hUlrMon ); m_hUlrMon = NULL; } if ( NULL != m_hWinINet ) { FreeLibrary ( m_hWinINet ); m_hWinINet = NULL; } m_pfnCoInternetCombineUrl = NULL; m_pfnCoInternetParseUrl = NULL; m_pfnCreateURLMoniker = NULL; m_pfnCoInternetGetSession = NULL; m_pfnURLOpenBlockingStream = NULL; m_pfnDeleteUrlCacheEntry = NULL; m_pfnInternetCreateUrl = NULL; m_pfnInternetCrackUrl = NULL; } #endif // LATE_BIND_URLMON_WININET /////////////////////////////////////////////////////////////////////////////////////////// // // Utility routines // /////////////////////////////////////////////////////////////////////////////////////////// // Return the IHTMLDocument2 pointer from the hosted doc. // HRESULT CProxyFrame::HrGetDoc(IHTMLDocument2 **ppDoc) { HRESULT hr = E_FAIL; IUnknown* lpUnk = m_pSite->GetObjectUnknown(); if (FALSE == m_fActivated) return DE_E_UNEXPECTED; _ASSERTE(ppDoc); if (NULL == ppDoc) return DE_E_INVALIDARG; _ASSERTE(lpUnk); if ( m_bfIsLoading ) return DE_E_UNEXPECTED; // This is invalid while document is still loading. if (lpUnk != NULL) { // Request the "document" object from the MSHTML *ppDoc = NULL; hr = lpUnk->QueryInterface(IID_IHTMLDocument2, (void **)ppDoc); } _ASSERTE(SUCCEEDED(hr)); // this should always succeed return hr; } // Helper routine to set any Boolean Trident property // HRESULT CProxyFrame::HrTridentSetPropBool(ULONG cmd, BOOL bVal) { HRESULT hr = S_OK; VARIANT varIn; VariantInit(&varIn); V_VT(&varIn) = VT_BOOL; #pragma warning(disable: 4310) // cast truncates constant value bVal ? V_BOOL(&varIn) = VARIANT_TRUE : V_BOOL(&varIn) = VARIANT_FALSE; #pragma warning(default: 4310) // cast truncates constant value hr = HrExecCommand(&CGID_MSHTML, cmd, MSOCMDEXECOPT_DONTPROMPTUSER, &varIn, NULL); // this should always succeed since all props // should be set in correct phases of Trident creation _ASSERTE(SUCCEEDED(hr)); return hr; } // Helper routine to get any Boolean Trident property // HRESULT CProxyFrame::HrTridentGetPropBool(ULONG cmd, BOOL& bVal) { HRESULT hr = S_OK; OLECMDF cmdf = (OLECMDF) 0; if (SUCCEEDED(HrQueryStatus(&CGID_MSHTML, cmd, &cmdf))) { bVal = (cmdf & OLECMDF_ENABLED) == OLECMDF_ENABLED ? TRUE : FALSE; } // this should always succeed since all props // should be set in correct phases of Trident creation _ASSERTE(SUCCEEDED(hr)); return hr; } // Store the BSTR so LoadFilteredStream can access it, and load a URL with our protocol // to kick off the load/resolve/display through the pluggable protocol handler. // // Clear the BaseURL, and mark the control "Loading..." // HRESULT CProxyFrame::LoadBSTRDeferred ( BSTR bVal ) { HRESULT hr = E_FAIL; _ASSERTE ( m_pUnkTriEdit ); m_bstrLoadText = bVal; CComPtr srpMoniker; CComPtr srpBindCtx; CComQIPtr srpPM (m_pUnkTriEdit); _ASSERTE ( srpPM ); if ( srpPM ) { #ifdef LATE_BIND_URLMON_WININET _ASSERTE ( m_pfnCreateURLMoniker ); hr = (*m_pfnCreateURLMoniker)( NULL, m_wszProtocolPrefix, &srpMoniker ); #else hr = CreateURLMoniker ( NULL, m_wszProtocolPrefix, &srpMoniker ); #endif // LATE_BIND_URLMON_WININET _ASSERTE ( SUCCEEDED( hr ) ); if ( SUCCEEDED ( hr ) ) { hr = ::CreateBindCtx(NULL, &srpBindCtx); _ASSERTE ( SUCCEEDED( hr ) ); if ( SUCCEEDED ( hr ) ) { m_bfIsLoading = TRUE; m_bfBaseURLFromBASETag = FALSE; hr = srpPM->Load(FALSE, srpMoniker, srpBindCtx, STGM_READ); _ASSERTE ( SUCCEEDED( hr ) ); } } } return hr; } // Set the document stream's dirty flag // HRESULT CProxyFrame::SetDirtyFlag ( BOOL bfMakeDirty ) { CComVariant varDirty; varDirty = bfMakeDirty ? true : false; return HrExecCommand(&CGID_MSHTML, IDM_SETDIRTY, MSOCMDEXECOPT_DONTPROMPTUSER, &varDirty, NULL); } // properties that can be set only after TriEdit is in running state HRESULT CProxyFrame::HrSetRuntimeProperties() { HRESULT hr = S_OK; if (FAILED(hr = HrSetPropActivateControls(m_fActivateControls))) { _ASSERTE(SUCCEEDED(hr)); goto error; } if (FAILED(hr = HrSetPropActivateApplets(m_fActivateApplets))) { _ASSERTE(SUCCEEDED(hr)); goto error; } if (FAILED(hr = HrSetPropActivateDTCs(m_fActivateDTCs))) { _ASSERTE(SUCCEEDED(hr)); goto error; } // toggle properties if (FAILED(hr = HrSetPropShowAllTags(m_fShowAllTags))) { _ASSERTE(SUCCEEDED(hr)); goto error; } if (FAILED(hr = HrSetPropShowBorders(m_fShowBorders))) { _ASSERTE(SUCCEEDED(hr)); goto error; } error: return hr; } HRESULT CProxyFrame::HrGetCurrentDocumentPath(BSTR* bVal) { HRESULT hr = S_OK; _ASSERTE(bVal); if (NULL == bVal) return E_INVALIDARG; *bVal = m_bstrCurDocPath.Copy (); return hr; } // properties that can only be set after UIActivation HRESULT CProxyFrame::HrSetDocLoadedProperties() { HRESULT hr = S_OK; BOOL bGoodUndoBehavior = TRUE; bGoodUndoBehavior = TRUE; if (FAILED(HrTridentGetPropBool(IDM_GOOD_UNDO_BEHAVIOR, bGoodUndoBehavior))) { _ASSERTE(SUCCEEDED(hr)); goto error; } if (FAILED(hr = HrSetAbsoluteDropMode(m_fAbsoluteDropMode))) { _ASSERTE(SUCCEEDED(hr)); goto error; } if (FAILED(hr = HrSetSnapToGridX(m_ulSnapToGridX))) { _ASSERTE(SUCCEEDED(hr)); goto error; } if (FAILED(hr = HrSetSnapToGridY(m_ulSnapToGridY))) { _ASSERTE(SUCCEEDED(hr)); goto error; } if (FAILED(hr = HrSetSnapToGrid(m_fSnapToGrid))) { _ASSERTE(SUCCEEDED(hr)); goto error; } error: return hr; } // HrExecInsertTable helper. Extract the safearrys // HRESULT CProxyFrame::HrGetTableSafeArray(IDEInsertTableParam* pTable, LPVARIANT pVarIn) { HRESULT hr = S_OK; UINT i = 0; SAFEARRAY FAR* psa = NULL; SAFEARRAYBOUND rgsabound[1] = {0}; LONG ix[1] = {0}; VARIANT varElem; LONG nNumRows = 0; LONG nNumCols = 0; BSTR bstrTableAttrs = NULL; BSTR bstrCellAttrs = NULL; BSTR bstrCaption = NULL; _ASSERTE(pTable); if (FAILED(hr = pTable->get_NumRows(&nNumRows))) { _ASSERTE(SUCCEEDED(hr)); return hr; } if (FAILED(hr = pTable->get_NumCols(&nNumCols))) { _ASSERTE(SUCCEEDED(hr)); return hr; } if (FAILED(hr = pTable->get_TableAttrs(&bstrTableAttrs))) { _ASSERTE(SUCCEEDED(hr)); return hr; } _ASSERTE(bstrTableAttrs); if (FAILED(hr = pTable->get_CellAttrs(&bstrCellAttrs))) { _ASSERTE(SUCCEEDED(hr)); return hr; } _ASSERTE(bstrCellAttrs); if (FAILED(hr = pTable->get_Caption(&bstrCaption))) { _ASSERTE(SUCCEEDED(hr)); return hr; } _ASSERTE(bstrCaption); rgsabound[0].lLbound = 0; rgsabound[0].cElements = 5; psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound); _ASSERTE(psa); if(NULL == psa) return E_OUTOFMEMORY; VariantInit(pVarIn); V_VT(pVarIn) = VT_ARRAY; V_ARRAY(pVarIn) = psa; i=0; // elmement 1: number of rows ix[0] = i; VariantInit(&varElem); V_VT(&varElem) = VT_I4; V_I4(&varElem) = nNumRows; hr = SafeArrayPutElement(psa, ix, &varElem); VariantClear(&varElem); ++i; // elmement 2: number of columns ix[0] = i; VariantInit(&varElem); V_VT(&varElem) = VT_I4; V_I4(&varElem) = nNumCols; hr = SafeArrayPutElement(psa, ix, &varElem); VariantClear(&varElem); ++i; // elmement 3: table tag attributes ix[0] = i; VariantInit(&varElem); V_VT(&varElem) = VT_BSTR; V_BSTR(&varElem) = bstrTableAttrs; hr = SafeArrayPutElement(psa, ix, &varElem); VariantClear(&varElem); ++i; // elmement 4: cell attributes ix[0] = i; VariantInit(&varElem); V_VT(&varElem) = VT_BSTR; V_BSTR(&varElem) = bstrCellAttrs; hr = SafeArrayPutElement(psa, ix, &varElem); VariantClear(&varElem); ++i; // elmement 5: table caption // VK bug 15857: don't include caption if it's empty. if ( 0 != SysStringLen ( bstrCaption ) ) { ix[0] = i; VariantInit(&varElem); V_VT(&varElem) = VT_BSTR; V_BSTR(&varElem) = bstrCaption; hr = SafeArrayPutElement(psa, ix, &varElem); VariantClear(&varElem); ++i; } return hr; } // Determine which object is selected, and return its position // HRESULT CProxyFrame::GetSelectionPos ( LPPOINT lpWhere ) { HRESULT hr = E_FAIL; CComPtr spHtmlDoc = NULL; CComPtr spSelectionObj = NULL; CComPtr spRangeDisp = NULL; CComPtr spElement = NULL; lpWhere->x = 0; lpWhere->y = 0; hr = HrGetDoc ( &spHtmlDoc ); if ( SUCCEEDED ( hr ) ) { hr = spHtmlDoc->get_selection ( &spSelectionObj ); if ( SUCCEEDED ( hr ) ) { hr = spSelectionObj->createRange ( &spRangeDisp ); if (SUCCEEDED ( hr ) ) { CComQIPtr spTextRange ( spRangeDisp ); if ( spTextRange ) { hr = spTextRange->parentElement(&spElement); } else { CComQIPtr spControlRange ( spRangeDisp ); if ( spControlRange ) { hr = spControlRange->commonParentElement(&spElement); } } if ( spElement ) { CComPtr spStyle = NULL; hr = spElement->get_style ( &spStyle ); if ( spStyle ) { spStyle->get_pixelTop ( &( lpWhere->y ) ); spStyle->get_pixelLeft ( &( lpWhere->x ) ); } } } } } return hr; } // If the current document is loaded from a URL, return the empty string. // If it's loaded from a file, strip the path part off and return just the file name. // Return S_FALSE for a URL or no file name. S_OK if a file name is supplied. // HRESULT CProxyFrame::GetCurDocNameWOPath ( CComBSTR& bstrDocName ) { bstrDocName = L""; if ( m_bfIsURL ) { return S_FALSE; } if ( 0 == m_bstrCurDocPath.Length () ) { return S_FALSE; } bstrDocName = m_bstrCurDocPath; // Truncate at first backslash: _wcsrev ( bstrDocName ); wcstok ( bstrDocName, OLESTR( "\\" ) ); _wcsrev ( bstrDocName ); return S_OK; } // Used by ShowContextMenu to properly offset the position of the click // HRESULT CProxyFrame::GetScrollPos ( LPPOINT lpPos ) { HRESULT hr = E_FAIL; CComPtr spHtmlDoc = NULL; CComPtr spBodyElem = NULL; _ASSERTE ( lpPos ); hr = HrGetDoc ( &spHtmlDoc ); // It's possible that the user clicked while the doc was still loading. // If so, just return 0, 0. if ( DE_E_UNEXPECTED == hr ) { lpPos->x = lpPos->y = 0; return S_FALSE; } _ASSERTE ( spHtmlDoc ); if ( SUCCEEDED ( hr ) ) { hr = spHtmlDoc->get_body ( &spBodyElem ); _ASSERTE ( spBodyElem ); if ( SUCCEEDED ( hr ) ) { CComQIPtr spHtmlTextCont ( spBodyElem ); if ( spHtmlTextCont ) { LONG lxPos = 0; LONG lyPos = 0; hr = spHtmlTextCont->get_scrollLeft ( &lxPos ); _ASSERTE ( SUCCEEDED ( hr ) ); if ( SUCCEEDED ( hr ) ) { hr = spHtmlTextCont->get_scrollTop ( &lyPos ); _ASSERTE ( SUCCEEDED ( hr ) ); if ( SUCCEEDED ( hr ) ) { lpPos->x = lxPos; lpPos->y = lyPos; } } } else { hr = E_NOINTERFACE; _ASSERTE ( SUCCEEDED ( hr ) ); } } } return hr; } HRESULT CProxyFrame::GetContainer ( LPOLECONTAINER* ppContainer ) { _ASSERTE ( m_pCtl ); _ASSERTE ( m_pCtl->m_spClientSite ); if ( m_pCtl->m_spClientSite ) { return m_pCtl->m_spClientSite->GetContainer ( ppContainer ); } return E_NOTIMPL; } // For the Safe for Scripting control, make sure the URL specified comes from // the same host as the SecurityURL, the URL of the hosting container.. // Note that this makes the SFS control virtually useless in VB, which returns // the Boot Drive Root Folder as the Security URL. // HRESULT CProxyFrame::CheckCrossZoneSecurity ( BSTR urlToLoad ) { HRESULT hr = S_OK; CComPtr srpSec; CComBSTR bstrSecURL; hr = GetSecurityURL ( bstrSecURL ); _ASSERTE ( SUCCEEDED ( hr ) ); if ( SUCCEEDED ( hr ) ) { #ifdef LATE_BIND_URLMON_WININET hr = (m_pfnCoInternetCreateSecurityManager)( NULL, &srpSec, 0 ); #else hr = CoInternetCreateSecurityManager( NULL, &srpSec, 0 ); #endif // LATE_BIND_URLMON_WININET if ( SUCCEEDED ( hr ) && srpSec ) { BYTE* pbSidToLoad = NULL; BYTE* pbDSidSecURL = NULL; DWORD dwSizeToLoad = INTERNET_MAX_URL_LENGTH; DWORD dwSizeSecURL = INTERNET_MAX_URL_LENGTH; pbSidToLoad = new BYTE [INTERNET_MAX_URL_LENGTH]; pbDSidSecURL = new BYTE [INTERNET_MAX_URL_LENGTH]; hr = srpSec->GetSecurityId ( urlToLoad, pbSidToLoad, &dwSizeToLoad, 0 ); _ASSERTE ( SUCCEEDED ( hr ) ); if ( SUCCEEDED ( hr ) ) { hr = srpSec->GetSecurityId ( bstrSecURL, pbDSidSecURL, &dwSizeSecURL, 0 ); _ASSERTE ( SUCCEEDED ( hr ) ); if ( SUCCEEDED ( hr ) ) { hr = DE_E_ACCESS_DENIED; if ( ( dwSizeToLoad == dwSizeSecURL ) && ( 0 == memcmp ( pbSidToLoad, pbDSidSecURL, dwSizeToLoad ) ) ) { hr = S_OK; } } } delete [] pbSidToLoad; delete [] pbDSidSecURL; } else { // BUG 597887: If CoInternetCreateSecurityManager returns NULL and success, return error: if ( !srpSec ) { hr = E_UNEXPECTED; } } } return hr; } // A specialization of CheckCrossZoneSecurity which works on the current selection. // Bug 547802 indicated a regression in execCommand, so we will assure safety ourselves. // HRESULT CProxyFrame::CheckCrossZoneSecurityOfSelection () { HRESULT hr = S_OK; CComPtr spDOM; CComPtr srpSP; CComPtr srpEditor; CComPtr srpSelSvc; CComPtr sprMarkupCont; CComPtr spSelDoc; CComPtr spHostLoc; CComBSTR bstrHref; hr = HrGetDoc( &spDOM ); if ( FAILED(hr) ) goto ONERROR; hr = spDOM->QueryInterface(IID_IServiceProvider, (LPVOID *)&srpSP); if ( FAILED(hr) ) goto ONERROR; hr = srpSP->QueryService(SID_SHTMLEditServices, IID_IHTMLEditServices, (void **)&srpEditor); if ( FAILED(hr) ) goto ONERROR; hr = srpEditor->GetSelectionServices(NULL, &srpSelSvc); if ( FAILED(hr) ) goto ONERROR; if ( !srpSelSvc ) goto ONERROR; hr = srpSelSvc->GetMarkupContainer(&sprMarkupCont); if ( FAILED(hr) ) goto ONERROR; if ( !sprMarkupCont ) goto ONERROR; hr = sprMarkupCont->QueryInterface(&spSelDoc); if ( FAILED(hr) ) goto ONERROR; hr = spSelDoc->get_location ( &spHostLoc ); if ( FAILED(hr) ) goto ONERROR; if ( !spHostLoc ) goto ONERROR; hr = spHostLoc->get_href ( &bstrHref ); if ( FAILED(hr) ) goto ONERROR; if ( !bstrHref ) goto ONERROR; hr = CheckCrossZoneSecurity ( bstrHref ); return hr; ONERROR: return DE_E_ACCESS_DENIED; } HRESULT CProxyFrame::OnProgress(ULONG, ULONG, ULONG ulStatusCode, LPCWSTR) { if ( BINDSTATUS_REDIRECTING == ulStatusCode ) { // If we're the SFS control, cancel on Redirect. Otherwise, ignore it. if ( m_pCtl->IsSafeForScripting ()) { m_bfSFSRedirect = TRUE; } } return E_NOTIMPL; }