You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1971 lines
54 KiB
1971 lines
54 KiB
//+=============================================================================
|
|
//
|
|
// File : persist.cxx
|
|
//
|
|
// contents : implementation of CPersistData xtag
|
|
//
|
|
//=============================================================================
|
|
#include "headers.h"
|
|
|
|
#pragma MARK_DATA(__FILE__)
|
|
#pragma MARK_CODE(__FILE__)
|
|
#pragma MARK_CONST(__FILE__)
|
|
|
|
#ifndef X_MSHTML_H_
|
|
#define X_MSHTML_H_
|
|
#include "mshtml.h" // for IHTML*ELement*
|
|
#endif
|
|
|
|
#ifndef X_PERHIST_H_
|
|
#define X_PERHIST_H_
|
|
#include "perhist.h" // For IPersistHistory
|
|
#endif
|
|
|
|
#ifndef X_DISPEX_H_
|
|
#define X_DISPEX_H_
|
|
#include "dispex.h" // For IDispatchEx
|
|
#endif
|
|
|
|
#ifndef X_PERSIST_HXX_
|
|
#define X_PERSIST_HXX_
|
|
#include "persist.hxx"
|
|
#endif
|
|
|
|
#ifndef __X_IEXTAG_H_
|
|
#define __X_IEXTAG_H_
|
|
#include "iextag.h"
|
|
#endif
|
|
|
|
#ifndef __X_UTILS_HXX_
|
|
#define __X_UTILS_HXX_
|
|
#include "utils.hxx"
|
|
#endif
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : Init
|
|
//
|
|
// Synopsis : this method is called by MSHTML.dll to initialize peer object
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::Init(IElementBehaviorSite * pPeerSite)
|
|
{
|
|
_pPeerSite = pPeerSite;
|
|
_pPeerSite->AddRef();
|
|
|
|
_pPeerSite->QueryInterface(IID_IElementBehaviorSiteOM, (void**)&_pPeerSiteOM);
|
|
|
|
// now register our 2 events
|
|
if (_pPeerSiteOM)
|
|
{
|
|
_pPeerSiteOM->RegisterEvent (_T("onload"), 0, NULL);
|
|
_pPeerSiteOM->RegisterEvent (_T("onsave"), 0, NULL);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : Notify
|
|
//
|
|
// Synopsis : when document is ready for modifications, setup timer calls
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::Notify(LONG lEvent, VARIANT *)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// member : queryType
|
|
//
|
|
// Synopsis : IPersistData method, this is used by the calling routines in order
|
|
// to avoid cocreating the XML object. This way they can query any of the
|
|
// persist Tags, and find out what persist behaiors it supprots.
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT
|
|
CPersistDataPeer::queryType(long lType, VARIANT_BOOL * pfSupportsType)
|
|
{
|
|
if (!pfSupportsType)
|
|
return E_POINTER;
|
|
|
|
*pfSupportsType = (lType == (long)_eState) ? VB_TRUE : VB_FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : Save
|
|
//
|
|
// Synopsis : Implementation of the IHTMLPersistData method
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::save(IUnknown * pUnk,
|
|
long lType,
|
|
VARIANT_BOOL *pfBroadcast)
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstrEvent;
|
|
|
|
if (!pfBroadcast)
|
|
{
|
|
hr = E_POINTER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*pfBroadcast = VB_TRUE;
|
|
|
|
// cache the OM pointers
|
|
InitOM(pUnk, lType);
|
|
|
|
bstrEvent = SysAllocString(L"onsave");
|
|
|
|
if (!bstrEvent)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// allow script to save data
|
|
hr = (FireEvent(bstrEvent, pfBroadcast, TRUE));
|
|
SysFreeString(bstrEvent);
|
|
|
|
Cleanup:
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : Load
|
|
//
|
|
// Synopsis : Implementation of the IHTMLPersistData method
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::load(IUnknown * pUnk,
|
|
long lType,
|
|
VARIANT_BOOL *pfContinue)
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstrEvent;
|
|
|
|
if (!pfContinue)
|
|
{
|
|
hr = E_POINTER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*pfContinue = VB_TRUE;
|
|
|
|
// cache the OM pointers
|
|
InitOM(pUnk, lType, INIT_USE_CACHED);
|
|
|
|
bstrEvent = SysAllocString(L"onload");
|
|
if (!bstrEvent)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// allow event handlers the chance to load from the cach
|
|
hr = (FireEvent(bstrEvent, pfContinue, FALSE));
|
|
SysFreeString(bstrEvent);
|
|
|
|
Cleanup:
|
|
return( hr );
|
|
}
|
|
|
|
//+--------------------------------------------------------------------------------
|
|
//
|
|
// Member : FireEvent
|
|
//
|
|
// Synopsis : helper method to fire the persistence events
|
|
//
|
|
//---------------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::FireEvent(BSTR bstrEvent, VARIANT_BOOL * pfContinue, BOOL fIsSaveEvent)
|
|
{
|
|
IHTMLEventObj * pEventObj = NULL;
|
|
IHTMLEventObj2 * pEO2 = NULL;
|
|
LONG lCookie;
|
|
HRESULT hr = E_PENDING;
|
|
VARIANT varRet;
|
|
|
|
if (!_pPeerSiteOM)
|
|
goto Cleanup;
|
|
|
|
VariantInit(&varRet);
|
|
|
|
// create an event object
|
|
hr = _pPeerSiteOM->CreateEventObject(&pEventObj);
|
|
if (hr || !pEventObj)
|
|
goto Cleanup;
|
|
|
|
// Now populate the event object with whatever properties are
|
|
// appropriate for this event:
|
|
hr = pEventObj->QueryInterface(IID_IHTMLEventObj2, (void**)&pEO2);
|
|
if (hr==S_OK)
|
|
{
|
|
BSTR bstrEventType = SysAllocString( (fIsSaveEvent) ? L"save" : L"load" );
|
|
|
|
// we need to set the event type, the event type is either
|
|
// "load" or "save".
|
|
if (bstrEventType)
|
|
{
|
|
pEO2->put_type( bstrEventType );
|
|
SysFreeString(bstrEventType);
|
|
}
|
|
}
|
|
|
|
// get the event cookie to fire the event
|
|
hr = _pPeerSiteOM->GetEventCookie (bstrEvent, &lCookie);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = _pPeerSiteOM->FireEvent (lCookie, pEventObj);
|
|
|
|
if (pfContinue)
|
|
{
|
|
hr = pEventObj->get_returnValue(&varRet);
|
|
if (!hr)
|
|
*pfContinue = ((V_VT(&varRet) == VT_BOOL) &&
|
|
(V_BOOL(&varRet) == VB_FALSE))? VB_FALSE : VB_TRUE;
|
|
}
|
|
|
|
Cleanup:
|
|
VariantClear(&varRet);
|
|
ReleaseInterface(pEventObj);
|
|
ReleaseInterface(pEO2);
|
|
return ( hr );
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------------------
|
|
//
|
|
// Member : ClearOMInterfaces ()
|
|
//
|
|
// Synopsis : this helper function is called after the Save/Load persistenceCache
|
|
// operations are finished, and is responsible for freeing up any of hte OM
|
|
// interfaces that were cached.
|
|
//
|
|
//------------------------------------------------------------------------
|
|
void
|
|
CPersistDataPeer::ClearOMInterfaces()
|
|
{
|
|
ClearInterface(&_pRoot);
|
|
ClearInterface(&_pInnerXMLDoc);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersistDataPeer::InitOM
|
|
//
|
|
// Synopsis: IHTMLPersistData OM method implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::InitOM(IUnknown * pUnk, long lType, DWORD dwFlags /* ==0*/)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
long lElemID = -1;
|
|
BSTR bstrTag =NULL;
|
|
CBufferedStr cbsID;
|
|
IXMLDOMNodeList * pChildren = NULL;
|
|
IXMLDOMElement * pNewChild = NULL;
|
|
IXMLDOMNode * pSubTree = NULL;
|
|
|
|
if (!pUnk)
|
|
return E_INVALIDARG;
|
|
|
|
if (_pInnerXMLDoc)
|
|
{
|
|
if (dwFlags & INIT_USE_CACHED)
|
|
goto Cleanup;
|
|
else
|
|
ClearOMInterfaces();
|
|
}
|
|
|
|
// make sure we know what we have been given.
|
|
hr = (pUnk->QueryInterface(IID_IXMLDOMDocument, (void**)&_pInnerXMLDoc));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// find the data value...
|
|
hr = _pInnerXMLDoc->get_documentElement( &_pRoot );
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// but in most cases the xmlObject is a big bucket, and we would like
|
|
// to set the _pRoot to the subtree associated with our own _pPeerElement.
|
|
// if this root doesn't exist, create it. we try to find an ID first, if
|
|
// if it is not there, then create a unique name for this child subtree
|
|
// but appending the srcID to the tagName.
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
goto Cleanup;
|
|
|
|
hr = pPeerElement->get_id(&bstrTag);
|
|
if (hr || !bstrTag || !SysStringLen(bstrTag))
|
|
{
|
|
SysFreeString(bstrTag);
|
|
|
|
hr = pPeerElement->get_sourceIndex(&lElemID);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = pPeerElement->get_tagName(&bstrTag);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = cbsID.QuickAppend(bstrTag);
|
|
SysFreeString(bstrTag);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = cbsID.QuickAppend(lElemID);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
bstrTag = SysAllocString(cbsID);
|
|
}
|
|
|
|
if (!bstrTag)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// now we can actually see if there is a child of this name
|
|
// We need to do this by looping over the childNode collection
|
|
// and looking for one with our name.
|
|
//--------------------------------------------------------------
|
|
hr = _pRoot->get_childNodes(&pChildren);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
long i, iLen;
|
|
|
|
hr = pChildren->get_length(&iLen);
|
|
if (FAILED(hr))
|
|
goto Cleanup;
|
|
|
|
for (i=0; i < iLen; i++)
|
|
{
|
|
IXMLDOMNode * pTemp = NULL;
|
|
BSTR bstrNodeName = NULL;
|
|
BOOL fEqual = FALSE;
|
|
|
|
hr= pChildren->get_item(i, &pTemp);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
hr = pTemp->get_nodeName( &bstrNodeName );
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
fEqual = ! _wcsicmp(bstrTag, bstrNodeName);
|
|
|
|
SysFreeString(bstrNodeName);
|
|
|
|
if (fEqual)
|
|
{
|
|
// transfer ownership
|
|
pSubTree = pTemp;
|
|
break;
|
|
}
|
|
|
|
ClearInterface( &pTemp );
|
|
}
|
|
}
|
|
|
|
if(pSubTree)
|
|
{
|
|
// yes there's a child so use it, we have a domnode so we need to
|
|
// qi for the element
|
|
ClearInterface(&_pRoot);
|
|
hr = pSubTree->QueryInterface(IID_IXMLDOMElement, (void**)&_pRoot);
|
|
}
|
|
else
|
|
{
|
|
// no child yet so lets create one.
|
|
hr = _pInnerXMLDoc->createElement(bstrTag, &pNewChild);
|
|
if (hr || ! pNewChild)
|
|
goto Cleanup;
|
|
|
|
hr = _pRoot->appendChild(pNewChild, NULL);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
ClearInterface(&_pRoot);
|
|
_pRoot=pNewChild;
|
|
pNewChild = NULL; // transfer ownership
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
ReleaseInterface(pPeerElement);
|
|
SysFreeString(bstrTag);
|
|
ReleaseInterface(pChildren);
|
|
ReleaseInterface(pSubTree);
|
|
ReleaseInterface(pNewChild);
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersistDataPeer::getAttribute
|
|
//
|
|
// Synopsis: IHTMLPersistData OM method implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::getAttribute (BSTR strName, VARIANT * pvarValue )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!pvarValue)
|
|
{
|
|
hr = E_POINTER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
VariantClear(pvarValue);
|
|
|
|
if (!strName)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = InitOM();
|
|
if (hr)
|
|
{
|
|
// no OM pointers so fail silently
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// get the child of the root that has the name strName
|
|
if (_pRoot)
|
|
{
|
|
hr = _pRoot->getAttribute(strName, pvarValue);
|
|
if (hr ==S_FALSE)
|
|
hr = S_OK;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr ;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersistDataPeer::setAttribute
|
|
//
|
|
// Synopsis: IHTMLPersistData OM method implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::setAttribute (BSTR strName, VARIANT varValue)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!strName)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = InitOM();
|
|
if (hr)
|
|
{
|
|
// no OM pointers so fail silently
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// save this value as an attribute on the root.
|
|
if (_pRoot)
|
|
{
|
|
// TODO - look into just passing the variant into the xml object
|
|
// and let them worry about all the types... We do the processing below
|
|
// to be safe for now.
|
|
VARIANT * pvar = NULL;
|
|
CVariant cvarTemp;
|
|
|
|
if ((V_VT(&varValue)==VT_BSTR) ||
|
|
V_VT(&varValue)==(VT_BYREF|VT_BSTR))
|
|
{
|
|
pvar = (V_VT(&varValue) & VT_BYREF) ?
|
|
V_VARIANTREF(&varValue) : &varValue;
|
|
}
|
|
else if ((V_VT(&varValue)==VT_BOOL ||
|
|
V_VT(&varValue)==(VT_BYREF|VT_BOOL)))
|
|
{
|
|
// sadly, do our own bool conversion...
|
|
VARIANT_BOOL vbFlag = (V_VT(&varValue)==VT_BOOL) ?
|
|
V_BOOL(&varValue) :
|
|
V_BOOL( V_VARIANTREF(&varValue) );
|
|
|
|
V_VT(&cvarTemp) = VT_BSTR;
|
|
V_BSTR(&cvarTemp) = vbFlag ? SysAllocString(L"true") :
|
|
SysAllocString(L"false");
|
|
|
|
pvar = & cvarTemp;
|
|
}
|
|
else
|
|
{
|
|
pvar = &varValue;
|
|
|
|
hr = VariantChangeTypeEx(pvar, pvar, LCID_SCRIPTING, 0, VT_BSTR);
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
hr = _pRoot->setAttribute(strName, *pvar);
|
|
if (hr ==S_FALSE)
|
|
hr = S_OK;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersistDataPeer::removeDataValue
|
|
//
|
|
// Synopsis: IHTMLPersistData OM method implementation
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::removeAttribute (BSTR strName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!strName)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = InitOM();
|
|
if (hr)
|
|
{
|
|
// no OM pointers so fail silently
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// get the child of the root that has the name strName
|
|
if (_pRoot)
|
|
{
|
|
hr = _pRoot->removeAttribute(strName);
|
|
if (hr ==S_FALSE)
|
|
hr = S_OK;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr ;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CPersistDataPeer::get_XMLDocument
|
|
//
|
|
// Synopsis: IHTMLPersistData OM proeprty implementation. this is the default
|
|
// property for this object, and as such it exposes the XMLOM
|
|
// of the user data.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::get_XMLDocument (IDispatch ** ppDisp)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!ppDisp)
|
|
{
|
|
hr = E_POINTER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*ppDisp = NULL;
|
|
|
|
hr = InitOM();
|
|
if (hr)
|
|
{
|
|
// no OM pointers so fail silently
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (_pInnerXMLDoc)
|
|
{
|
|
hr = _pInnerXMLDoc->QueryInterface(IID_IDispatch,
|
|
(void**)ppDisp);
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// Member : GetSaveCategory
|
|
//
|
|
// Synopsis : this helper function turns the tagName fo the
|
|
// pPeerElement, into the category for its save operations.
|
|
//
|
|
//+-----------------------------------------------------------
|
|
ENUM_SAVE_CATEGORY
|
|
CPersistDataPeer::GetSaveCategory()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CVariant cvarTag;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
ENUM_SAVE_CATEGORY escRet = ESC_UNKNOWN;
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
goto Cleanup;
|
|
|
|
V_VT(&cvarTag) = VT_BSTR;
|
|
hr = pPeerElement->get_tagName(&V_BSTR(&cvarTag));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// there's got to be a better way to do this, but for
|
|
// now I will try to put the most common ones in front.
|
|
if (0==_wcsicmp(V_BSTR(&cvarTag), L"input"))
|
|
{
|
|
// but wait!!!! don't save password type inputs
|
|
BSTR bstrType = SysAllocString(L"type");
|
|
CVariant cvarVal;
|
|
|
|
escRet = ESC_INTRINSIC;
|
|
if (!bstrType)
|
|
goto Cleanup;
|
|
|
|
hr = (pPeerElement->getAttribute(bstrType, 0, &cvarVal));
|
|
SysFreeString(bstrType);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
if (V_VT(&cvarVal) == VT_BSTR &&
|
|
0==_wcsicmp(V_BSTR(&cvarVal), L"password"))
|
|
{
|
|
escRet = ESC_PASSWORD;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if (0==_wcsicmp(V_BSTR(&cvarTag), L"script"))
|
|
{
|
|
escRet = ESC_SCRIPT;
|
|
}
|
|
else if (0==_wcsicmp(V_BSTR(&cvarTag), L"select") ||
|
|
0==_wcsicmp(V_BSTR(&cvarTag), L"textarea") ||
|
|
0==_wcsicmp(V_BSTR(&cvarTag), L"richtext") ||
|
|
0==_wcsicmp(V_BSTR(&cvarTag), L"button") ||
|
|
0==_wcsicmp(V_BSTR(&cvarTag), L"fieldset")
|
|
)
|
|
{
|
|
escRet = ESC_INTRINSIC;
|
|
}
|
|
else if (0==_wcsicmp(V_BSTR(&cvarTag), L"object") ||
|
|
0==_wcsicmp(V_BSTR(&cvarTag), L"embed") ||
|
|
0==_wcsicmp(V_BSTR(&cvarTag), L"applet")
|
|
)
|
|
{
|
|
escRet = ESC_CONTROL;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
ReleaseInterface(pPeerElement);
|
|
return escRet;
|
|
}
|
|
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// member : GetEngineClsidForLanguage ()
|
|
//
|
|
// synopsis : another helper method that returns the CLSID of the
|
|
// scripdEngine associated with the language progID.
|
|
//
|
|
//+-----------------------------------------------------------
|
|
HRESULT
|
|
CPersistDataPeer::GetEngineClsidForLanguage(CLSID * pclsid,
|
|
IHTMLDocument2 * pBrowseDoc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BSTR bstrLanguage = NULL;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
|
|
|
|
if (!pclsid)
|
|
return E_POINTER;
|
|
|
|
*pclsid = IID_NULL;
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// what language script engine to instantiate?
|
|
hr = (pPeerElement->get_language(&bstrLanguage));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
if (!bstrLanguage || !SysStringLen(bstrLanguage))
|
|
{
|
|
IHTMLElementCollection * pCollection = NULL;
|
|
|
|
hr = E_FAIL;
|
|
|
|
if (bstrLanguage)
|
|
{
|
|
SysFreeString(bstrLanguage);
|
|
bstrLanguage = NULL;
|
|
}
|
|
|
|
// use the default language, we get this from the language of
|
|
// first script block in the document.
|
|
if (!FAILED(pBrowseDoc->get_scripts(&pCollection)))
|
|
{
|
|
CVariant cvarID;
|
|
CVariant cvarEmpty;
|
|
IDispatch * pDisp;
|
|
|
|
V_VT(&cvarID) = VT_I4;
|
|
V_I4(&cvarID) = 0;
|
|
|
|
if (!FAILED(pCollection->item(cvarID, cvarEmpty, &pDisp)))
|
|
{
|
|
IHTMLElement * pFirstScript = NULL;
|
|
|
|
if (pDisp &&
|
|
!FAILED(pDisp->QueryInterface(IID_IHTMLElement,
|
|
(void**)&pFirstScript)))
|
|
{
|
|
pFirstScript->get_language(&bstrLanguage);
|
|
if (bstrLanguage && SysStringLen(bstrLanguage))
|
|
hr = CLSIDFromProgID ( bstrLanguage, pclsid );
|
|
|
|
ReleaseInterface(pFirstScript);
|
|
}
|
|
|
|
ReleaseInterface(pDisp);
|
|
}
|
|
|
|
ReleaseInterface(pCollection);
|
|
}
|
|
|
|
if (hr)
|
|
{
|
|
hr = CLSIDFromProgID ( L"JScript", pclsid );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = CLSIDFromProgID ( bstrLanguage, pclsid );
|
|
}
|
|
|
|
Cleanup:
|
|
SysFreeString(bstrLanguage);
|
|
ReleaseInterface(pPeerElement);
|
|
return ( hr );
|
|
}
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// Member GetScriptEngine
|
|
//
|
|
// Synopsis : helper method - this creates the appropriate script
|
|
// engine, and parses in the text of hte script block that we
|
|
// are interested in.
|
|
//
|
|
//+-----------------------------------------------------------
|
|
|
|
IActiveScript *
|
|
CPersistDataPeer::GetScriptEngine(IHTMLDocument2 * pBrowseDoc, ULONG * puFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BSTR bstrCode = NULL;
|
|
CLSID clsID;
|
|
CLSID clsIDTarget;
|
|
IActiveScript * pScriptEngine = NULL;
|
|
IActiveScriptSite * pScriptSite = NULL;
|
|
IActiveScriptParse * pASP = NULL;
|
|
IHTMLScriptElement * pScriptElem = NULL;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
|
|
|
|
hr = (GetEngineClsidForLanguage(&clsID, pBrowseDoc));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// set the return flags, so that these tests only need to be done once
|
|
// some callers need to know if the script block is jscript, vbscript,
|
|
// of something else
|
|
if (!FAILED(CLSIDFromProgID ( _T("JScript"), &clsIDTarget )) &&
|
|
(clsID == clsIDTarget))
|
|
{
|
|
*puFlags = SCRIPT_ENGINE_JSCRIPT;
|
|
}
|
|
else if (!FAILED(CLSIDFromProgID ( _T("VBScript"), &clsIDTarget )) &&
|
|
(clsID == clsIDTarget))
|
|
{
|
|
*puFlags = SCRIPT_ENGINE_VBSCRIPT;
|
|
}
|
|
else
|
|
*puFlags = SCRIPT_ENGINE_OTHER;
|
|
|
|
|
|
// create the script engine
|
|
hr = CoCreateInstance( clsID, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IActiveScript,(void **)&pScriptEngine);
|
|
if ( hr )
|
|
goto Cleanup;
|
|
|
|
// get the scriptSite interface from ourselves.
|
|
hr = (QueryInterface(IID_IActiveScriptSite, (void**) &pScriptSite));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = pScriptEngine->SetScriptSite(pScriptSite);
|
|
ReleaseInterface(pScriptSite); // engine should have addref'd
|
|
if ( FAILED(hr) )
|
|
goto Error;
|
|
|
|
// prepare to load out text
|
|
hr = (pScriptEngine->QueryInterface (IID_IActiveScriptParse, (void**)&pASP));
|
|
if ( hr )
|
|
goto Error;
|
|
|
|
hr = (pASP->InitNew());
|
|
if ( hr )
|
|
goto Error;
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// get the text of the script element
|
|
hr = (pPeerElement->QueryInterface(IID_IHTMLScriptElement, (void**)&pScriptElem));
|
|
if ( hr )
|
|
goto Error;
|
|
|
|
hr = (pScriptElem->get_text(&bstrCode));
|
|
if ( hr || !bstrCode)
|
|
goto Error;
|
|
|
|
hr = pASP->ParseScriptText( bstrCode,
|
|
NULL, // Item Name (Namespace)
|
|
NULL, // context
|
|
NULL, // delimiter
|
|
0, // srcContext Cookie
|
|
0, // ulStarting Line
|
|
SCRIPTTEXT_ISVISIBLE, //dwFlags
|
|
NULL, // pVarResult
|
|
NULL ); // pException Info
|
|
if ( hr )
|
|
goto Error;
|
|
|
|
hr = pScriptEngine->SetScriptState(SCRIPTSTATE_CONNECTED);
|
|
if ( FAILED(hr) )
|
|
goto Error;
|
|
|
|
Cleanup:
|
|
SysFreeString(bstrCode);
|
|
ReleaseInterface(pPeerElement);
|
|
ReleaseInterface(pScriptElem);
|
|
ReleaseInterface(pASP);
|
|
return pScriptEngine;
|
|
|
|
Error:
|
|
ClearInterface(&pScriptEngine);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// Member BuildNewScriptBlock
|
|
//
|
|
// Synopsis : helper method, this does the real work of snapshotting a
|
|
// script block. IF the script is VBSCRIPT or JSCRIPT the appropriate
|
|
// syntax is used. for any other language, we convert to JScript.
|
|
//
|
|
//+-----------------------------------------------------------
|
|
HRESULT
|
|
CPersistDataPeer::BuildNewScriptBlock(CBufferedStr * pstrBuffer, ULONG *puFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IDispatch * pScriptNameSpace = NULL;
|
|
IDispatchEx * pDispScript = NULL;
|
|
IDispatchEx * pBrowseWin = NULL;
|
|
DISPID dispidName;
|
|
BSTR bstrName = NULL;
|
|
IHTMLWindow2 * pWin = NULL;
|
|
IActiveScript * pScriptEngine = NULL;
|
|
IDispatch * pdispBrowseDoc = NULL;
|
|
IHTMLDocument2 * pBrowseDoc = NULL;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = (pPeerElement->get_document(&pdispBrowseDoc));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = (pdispBrowseDoc->QueryInterface(IID_IHTMLDocument2,
|
|
(void**)&pBrowseDoc));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
pScriptEngine = GetScriptEngine(pBrowseDoc, puFlags);
|
|
if (!pScriptEngine)
|
|
goto Cleanup; // fail silently
|
|
|
|
|
|
// loop over all the dispatches in the script engine's name
|
|
// space and get thier values.from..pSrcDoc
|
|
hr = pScriptEngine->GetScriptDispatch( NULL, &pScriptNameSpace );
|
|
if ( hr )
|
|
goto Cleanup;
|
|
|
|
hr = (pScriptNameSpace->QueryInterface( IID_IDispatchEx, (void**)&pDispScript));
|
|
if ( hr )
|
|
goto Cleanup;
|
|
|
|
// inorder to derefernce the script variables we need the browse
|
|
// documents window.
|
|
hr = (pBrowseDoc->get_parentWindow(&pWin));
|
|
if(hr)
|
|
goto Cleanup;
|
|
|
|
hr = (pWin->QueryInterface(IID_IDispatchEx, (void**)&pBrowseWin));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// now run through all the objects in this script engine and
|
|
// find their values
|
|
hr = (pDispScript->GetNextDispID(0, DISPID_STARTENUM, &dispidName));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
while (dispidName)
|
|
{
|
|
hr = (pDispScript->GetMemberName(dispidName, &bstrName));
|
|
if (!hr && bstrName)
|
|
{
|
|
DISPID dispidBrowser;
|
|
|
|
if (!FAILED(pBrowseWin->GetDispID(bstrName, fdexNameCaseSensitive, &dispidBrowser)))
|
|
{
|
|
DISPPARAMS dp;
|
|
CVariant cvarRes;
|
|
BOOL fNeedsQuotesInScript = FALSE;
|
|
|
|
dp.rgvarg = NULL;
|
|
dp.rgdispidNamedArgs = NULL;
|
|
dp.cArgs = 0;
|
|
dp.cNamedArgs = 0;
|
|
|
|
hr = (pBrowseWin->Invoke(dispidBrowser,
|
|
IID_NULL,
|
|
LCID_SCRIPTING,
|
|
DISPATCH_PROPERTYGET,
|
|
&dp,
|
|
&cvarRes,
|
|
NULL,
|
|
NULL));
|
|
|
|
|
|
fNeedsQuotesInScript = (V_VT(&cvarRes) == VT_BSTR);
|
|
|
|
// Filter out VT_IDISPATCH & VT_UNKNOWN
|
|
if (!hr &&
|
|
V_VT(&cvarRes) != VT_DISPATCH &&
|
|
V_VT(&cvarRes) != VT_UNKNOWN &&
|
|
!FAILED(cvarRes.CoerceVariantArg(VT_BSTR)))
|
|
{
|
|
// assume JSCript if not VBScript
|
|
LPTSTR pstrAssign = (*puFlags & SCRIPT_ENGINE_VBSCRIPT) ?
|
|
_T("\n\r dim ") :
|
|
_T("\n\r var ");
|
|
|
|
|
|
// concatenate on the end of the buffer.
|
|
// "\n\r var <name> = " <value>"; "
|
|
hr = pstrBuffer->QuickAppend(pstrAssign);
|
|
if (hr)
|
|
goto Cleanup;
|
|
hr = pstrBuffer->QuickAppend(bstrName);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// if vbscript, put assignment on its own line.
|
|
if (*puFlags & SCRIPT_ENGINE_VBSCRIPT)
|
|
{
|
|
hr = pstrBuffer->QuickAppend(_T("\n\r "));
|
|
if (hr)
|
|
goto Cleanup;
|
|
hr = pstrBuffer->QuickAppend(bstrName);
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
// now handle outputing the assignment itself, careful
|
|
// to only put quotes around things that were strings.
|
|
hr = pstrBuffer->QuickAppend(_T(" = "));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
if (fNeedsQuotesInScript )
|
|
{
|
|
hr = pstrBuffer->QuickAppend(_T("\""));
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (V_VT(&cvarRes) != VT_EMPTY)
|
|
{
|
|
hr = pstrBuffer->QuickAppend(V_BSTR(&cvarRes));
|
|
}
|
|
else
|
|
{
|
|
// someone had dim x = Empty in their script
|
|
// or var x = document.expando
|
|
// and this doesn't coerc to a bstr (returned S_FALSE)
|
|
// don't reset the use quotes flag,
|
|
// for jscript convert to null, for vb
|
|
if (!(*puFlags & SCRIPT_ENGINE_VBSCRIPT))
|
|
{
|
|
hr = pstrBuffer->QuickAppend(_T("null"));
|
|
}
|
|
else
|
|
hr = pstrBuffer->QuickAppend(_T("Empty"));
|
|
}
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
if (fNeedsQuotesInScript )
|
|
{
|
|
hr = pstrBuffer->QuickAppend(_T("\""));
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
// VBSCRIPT doesn need ';' at the eol
|
|
if (!(*puFlags & SCRIPT_ENGINE_VBSCRIPT))
|
|
{
|
|
hr = pstrBuffer->QuickAppend(_T(";"));
|
|
}
|
|
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
SysFreeString(bstrName);
|
|
bstrName = NULL;
|
|
}
|
|
|
|
hr = (pDispScript->GetNextDispID(0, dispidName, &dispidName));
|
|
if (hr==S_FALSE)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
else if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if (pScriptEngine)
|
|
pScriptEngine->Close();
|
|
if (bstrName)
|
|
SysFreeString(bstrName);
|
|
ReleaseInterface(pPeerElement);
|
|
ReleaseInterface(pBrowseDoc);
|
|
ReleaseInterface(pScriptEngine);
|
|
ReleaseInterface(pdispBrowseDoc);
|
|
ReleaseInterface(pWin);
|
|
ReleaseInterface(pBrowseWin);
|
|
ReleaseInterface(pDispScript);
|
|
ReleaseInterface(pScriptNameSpace);
|
|
// no partial string in an error case...
|
|
if (hr)
|
|
pstrBuffer->Set();
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// member : SaveHandler_GenericTag
|
|
//
|
|
// synopsis : this does the generic handling when a tag has the
|
|
// persistence peer. It simply gets the outerHTML and saves that
|
|
// in the XML object. on the load the element will be hit
|
|
// with a put_outerHTML of this string.
|
|
//
|
|
//+-----------------------------------------------------------
|
|
HRESULT
|
|
CPersistDataPeer::SaveHandler_GenericTag()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CVariant cvarOuter;
|
|
IHTMLElement * pPeerParent = NULL;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// for elements in the head, we don't want to do this since
|
|
// there is a tree limitation that doesn't allow the outerHTML
|
|
// to be set.
|
|
hr = pPeerElement->get_parentElement(&pPeerParent);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// the right way to do this is to ask the question:
|
|
// are we in the body/frameset, NOT is our parent the
|
|
// head, so...
|
|
while (pPeerParent)
|
|
{
|
|
CVariant cvarTag;
|
|
IHTMLElement * pTemp = NULL;
|
|
|
|
V_VT(&cvarTag) = VT_BSTR;
|
|
hr = pPeerParent->get_tagName(&V_BSTR(&cvarTag));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
if (0==_wcsicmp(V_BSTR(&cvarTag), L"body") ||
|
|
0==_wcsicmp(V_BSTR(&cvarTag), L"frameset"))
|
|
break;
|
|
|
|
hr = pPeerParent->get_parentElement(&pTemp);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
ClearInterface(&pPeerParent);
|
|
pPeerParent = pTemp;
|
|
}
|
|
|
|
if (!pPeerParent)
|
|
goto Cleanup;
|
|
|
|
hr = pPeerElement->get_outerHTML(&V_BSTR(&cvarOuter));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
V_VT(&cvarOuter) = VT_BSTR;
|
|
|
|
// now save the outerHTML string
|
|
hr = _pRoot->setAttribute(_T("__NEW_TAG_OUTER"), cvarOuter);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
Cleanup:
|
|
ReleaseInterface(pPeerElement);
|
|
ReleaseInterface(pPeerParent);
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : LoadHandler_GenericTag()
|
|
//
|
|
// Synopsis : this restores the outerHTML of the tag as it is loaded
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
HRESULT
|
|
CPersistDataPeer::LoadHandler_GenericTag()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CVariant cvarOuter;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
|
|
if (!_pRoot)
|
|
goto Cleanup;
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// now get the outerHTML string
|
|
hr = _pRoot->getAttribute(_T("__NEW_TAG_OUTER"), &cvarOuter);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = cvarOuter.CoerceVariantArg(VT_BSTR);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = pPeerElement->put_outerHTML(V_BSTR(&cvarOuter));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
Cleanup:
|
|
ReleaseInterface(pPeerElement);
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------------
|
|
//
|
|
// Member : SaveHandler_ScriptTag()
|
|
//
|
|
// Synopsis : saves the script into the xml store. We do this in the standard way:
|
|
// 1- create a private script engin,
|
|
// 2- load it with the text of the peer's script block
|
|
// 3 - walk through its namespace pulling out the variables
|
|
// 4 - create a list of name value pairs as the new script block
|
|
// 5 - save this block in the appropriate place
|
|
//
|
|
//+------------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::SaveHandler_ScriptTag()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CVariant cvarText;
|
|
BSTR bstrLanguage = NULL;
|
|
CLSID clsIDTarget;
|
|
CLSID clsIDLang;
|
|
CBufferedStr cbsNewScript;
|
|
IDispatch * pdispBrowseDoc = NULL;
|
|
IHTMLDocument2 * pBrowseDoc = NULL;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
ULONG uFlags = 0;
|
|
|
|
|
|
if (!_pRoot)
|
|
goto Cleanup;
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = pPeerElement->get_document(&pdispBrowseDoc);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = pdispBrowseDoc->QueryInterface(IID_IHTMLDocument2,
|
|
(void**)&pBrowseDoc);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// get the new script block, and the language that it
|
|
// originated from
|
|
hr = BuildNewScriptBlock(&cbsNewScript, &uFlags);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// Language can only be set in design mode, as a result
|
|
// we can't change the script block since we don't know the
|
|
// syntax for an arbitrary script engine. so, only save
|
|
// script if this is javascript or vbscript.
|
|
if (uFlags & SCRIPT_ENGINE_OTHER)
|
|
goto Cleanup;
|
|
|
|
V_VT(&cvarText) = VT_BSTR;
|
|
V_BSTR(&cvarText) = SysAllocString((LPTSTR)cbsNewScript);
|
|
if (!V_BSTR(&cvarText))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = _pRoot->setAttribute(_T("__NEW_SCRIPT_TEXT"), cvarText);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
Cleanup:
|
|
SysFreeString(bstrLanguage);
|
|
ReleaseInterface(pPeerElement);
|
|
ReleaseInterface(pBrowseDoc);
|
|
ReleaseInterface(pdispBrowseDoc);
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : LoadHandler_ScriptTag()
|
|
//
|
|
// Synopsis : does the load - but wait...
|
|
// NYI - put_text is design time only, so the peer needs a different way
|
|
// of sliding this in.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistDataPeer::LoadHandler_ScriptTag()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CVariant cvarText;
|
|
|
|
if (!_pRoot)
|
|
goto Cleanup;
|
|
|
|
// try to get the new text from the saved XML
|
|
hr = _pRoot->getAttribute(_T("__NEW_SCRIPT_TEXT"), &cvarText);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// if there is script text, then we need to set this into the
|
|
// browse window's namespace
|
|
|
|
|
|
Cleanup:
|
|
if (hr == S_FALSE) hr = S_OK;
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//===============================================================
|
|
//
|
|
// Class : CPersistShortcut
|
|
//
|
|
// synopsis : derives from CPersistDataPeer and does the special handling
|
|
// related to the shortcut functionality
|
|
//
|
|
//===============================================================
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------
|
|
//
|
|
// Member : save
|
|
//
|
|
// synopsis: over ride of parent functionality to deal with shortcut
|
|
// specific stuff...
|
|
//
|
|
//-----------------------------------------------------------------
|
|
HRESULT
|
|
CPersistShortcut::save(IUnknown * pUnk,
|
|
long lType,
|
|
VARIANT_BOOL *pfContinue)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (lType != (long)htmlPersistStateFavorite)
|
|
{
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = super::save(pUnk, lType, pfContinue);
|
|
|
|
if (hr || (*pfContinue == VB_FALSE))
|
|
goto Cleanup;
|
|
|
|
// do the particular tag handling related to shortcuts
|
|
switch (GetSaveCategory())
|
|
{
|
|
case ESC_UNKNOWN:
|
|
case ESC_INTRINSIC:
|
|
case ESC_CONTROL:
|
|
hr = SaveHandler_GenericTag();
|
|
break;
|
|
|
|
// case ESC_SCRIPT:
|
|
// hr = SaveHandler_ScriptTag();
|
|
// break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : Load
|
|
//
|
|
// Synopsis : Implementation of the IHTMLPersistenceCache method
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistShortcut::load(IUnknown * pUnk,
|
|
long lType,
|
|
VARIANT_BOOL *pfDoDefault)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (lType != (long)htmlPersistStateFavorite)
|
|
{
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// fire the event first, since the pPeerElement is about to be
|
|
// removed from the tree entirely. Of course, if the user sets
|
|
// expandoes on this, they will get lost, but the alternative is to
|
|
// loose access to the xmlcache. So, if you think about it, they get
|
|
// outerHTML behaviour, and using XMLCache behavior means
|
|
// that you better cancel default behavior.
|
|
hr = super::load(pUnk, lType, pfDoDefault);
|
|
// is default load behavior canceled ?
|
|
if (hr || (*pfDoDefault == VB_FALSE))
|
|
goto Cleanup;
|
|
|
|
switch (GetSaveCategory())
|
|
{
|
|
case ESC_UNKNOWN:
|
|
case ESC_INTRINSIC:
|
|
case ESC_CONTROL:
|
|
hr = LoadHandler_GenericTag();
|
|
break;
|
|
|
|
// case ESC_SCRIPT:
|
|
// hr = LoadHandler_ScriptTag();
|
|
// break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//===============================================================
|
|
//
|
|
// Class : CPersistHistory
|
|
//
|
|
// synopsis : derives from CPersistDataPeer and does the special handling
|
|
// related to the History functionality
|
|
//
|
|
//===============================================================
|
|
|
|
|
|
//+----------------------------------------------------------------
|
|
//
|
|
// Member : save
|
|
//
|
|
// synopsis: over ride of parent functionality to deal with shortcut
|
|
// specific stuff...
|
|
//
|
|
//-----------------------------------------------------------------
|
|
HRESULT
|
|
CPersistHistory::save(IUnknown * pUnk,
|
|
long lType,
|
|
VARIANT_BOOL *pfContinue)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (lType != (long)htmlPersistStateHistory)
|
|
{
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = super::save(pUnk, lType, pfContinue);
|
|
if (hr || (*pfContinue == VB_FALSE))
|
|
goto Cleanup;
|
|
|
|
// do any tag specific handling that history needs
|
|
switch (GetSaveCategory())
|
|
{
|
|
// if we don't know anything special to do with this tag, just
|
|
// transfer over its outerHTML on the assumption that the author
|
|
// knows what they are doing
|
|
case ESC_UNKNOWN:
|
|
case ESC_INTRINSIC:
|
|
hr = SaveHandler_GenericTag();
|
|
break;
|
|
|
|
// case ESC_SCRIPT:
|
|
// hr = SaveHandler_ScriptTag();
|
|
// break;
|
|
|
|
default :
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : Load
|
|
//
|
|
// Synopsis : Implementation of the IHTMLPersistenceCache method
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistHistory::load(IUnknown * pUnk,
|
|
long lType,
|
|
VARIANT_BOOL *pfDoDefault)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (lType != (long)htmlPersistStateHistory)
|
|
{
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// cache the OM pointers
|
|
hr = InitOM(pUnk, lType);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = super::load(pUnk, lType, pfDoDefault);
|
|
// is default load behavior canceled ?
|
|
if (hr || (*pfDoDefault == VB_FALSE))
|
|
goto Cleanup;
|
|
|
|
// ISSUE (carled) NYI - script retoration presents a unique challenge
|
|
// because this needs to execute inline instead of the original
|
|
// script block text.
|
|
switch(GetSaveCategory())
|
|
{
|
|
// case ESC_SCRIPT:
|
|
// hr = LoadHandler_ScriptTag();
|
|
// break;
|
|
|
|
case ESC_UNKNOWN:
|
|
case ESC_INTRINSIC:
|
|
hr = LoadHandler_GenericTag();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//===============================================================
|
|
//
|
|
// Class : CPersistSnapshot
|
|
//
|
|
// synopsis : derives from CPersistDataPeer and does the special handling
|
|
// related to the run-time same (snapshot) functionality
|
|
//
|
|
//===============================================================
|
|
|
|
|
|
//+----------------------------------------------------------------
|
|
//
|
|
// Member : CPersistSnapshot::save
|
|
//
|
|
// synopsis: over ride of parent functionality to deal with shortcut
|
|
// specific stuff...
|
|
// Since it doesn't make much sense to allow theXMLOM to be acccessed
|
|
// in this case, calling super:: pretty much only fires the event.
|
|
//
|
|
//-----------------------------------------------------------------
|
|
HRESULT
|
|
CPersistSnapshot::save(IUnknown * pUnk,
|
|
long lType,
|
|
VARIANT_BOOL *pfContinue)
|
|
{
|
|
HRESULT hr;
|
|
CVariant cvarTag;
|
|
IHTMLDocument2 * pDesignDoc = NULL;
|
|
|
|
if (lType != (long)htmlPersistStateSnapshot)
|
|
{
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!pUnk)
|
|
{
|
|
hr = E_POINTER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = pUnk->QueryInterface(IID_IHTMLDocument2, (void**)&pDesignDoc);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// allow the onsave event to be fired.
|
|
hr = super::save(NULL, lType, pfContinue);
|
|
if (hr || (*pfContinue == VB_FALSE))
|
|
goto Cleanup;
|
|
|
|
// now that the author has had the oppurtunity to set expandoes,
|
|
// and value properties, we start the real work of snapshot saveing.
|
|
switch (GetSaveCategory())
|
|
{
|
|
case ESC_CONTROL:
|
|
hr = TransferControlValues(pDesignDoc);
|
|
break;
|
|
|
|
case ESC_SCRIPT:
|
|
hr = TransferScriptValues(pDesignDoc);
|
|
break;
|
|
|
|
case ESC_UNKNOWN:
|
|
// if we don't know anything special to do with this tag, just
|
|
// transfer over its outerHTML on the assumption that the author
|
|
// knows what they are doing
|
|
case ESC_INTRINSIC:
|
|
hr = TransferIntrinsicValues(pDesignDoc);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
ReleaseInterface(pDesignDoc);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Member : CPersistSnapshot::Load
|
|
//
|
|
// Synopsis : Implementation of the IHTMLPersistenceCache method
|
|
// Since it doesn't make much sense to allow theXMLOM to be acccessed
|
|
// in this case, calling super:: pretty much only fires the event.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistSnapshot::load(IUnknown * pUnk,
|
|
long lType,
|
|
VARIANT_BOOL *pfDoDefault)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (lType != (long)htmlPersistStateSnapshot)
|
|
{
|
|
hr = S_OK;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = super::load(pUnk, lType, pfDoDefault);
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// member : GetDesignElem
|
|
//
|
|
// Synposis : this helper function is responsible for finding the
|
|
// design document's element counterpart to our pPeerElement.
|
|
//
|
|
//+-----------------------------------------------------------
|
|
IHTMLElement *
|
|
CPersistSnapshot::GetDesignElem(IHTMLDocument2 * pDesignDoc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CVariant cvarID;
|
|
CVariant cvarEmpty;
|
|
IDispatch * pDisp = NULL;
|
|
IHTMLElement * pRetElem = NULL;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
IHTMLElementCollection * pCollection = NULL;
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
V_VT(&cvarID) = VT_BSTR;
|
|
hr = pPeerElement->get_id(&V_BSTR(&cvarID));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// first get the documents all collection.
|
|
hr = pDesignDoc->get_all(&pCollection);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// now find out elements coutner part inthe design document,
|
|
// if it exits. Elements that were created by inline scripts,
|
|
// or other script driven means, may not be found.
|
|
hr = pCollection->item(cvarID, cvarEmpty, &pDisp);
|
|
if (hr || !pDisp)
|
|
goto Cleanup;
|
|
|
|
// make sure that we do not have a collection...
|
|
hr = pDisp->QueryInterface(IID_IHTMLElement, (void**)&pRetElem);
|
|
|
|
Cleanup:
|
|
ReleaseInterface(pPeerElement);
|
|
ReleaseInterface(pDisp);
|
|
ReleaseInterface(pCollection);
|
|
return pRetElem;
|
|
}
|
|
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// member : TransferIntrinsicValues
|
|
//
|
|
// synopsis : this transfers the value of the browse-control to the
|
|
// design control by setting the outerHTML of the design control
|
|
// to that of the browseControl
|
|
//
|
|
//+-----------------------------------------------------------
|
|
HRESULT
|
|
CPersistSnapshot::TransferIntrinsicValues (IHTMLDocument2 *pDesignDoc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BSTR bstrOuter = NULL;
|
|
IHTMLElement * pDesignElem = NULL;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = pPeerElement->get_outerHTML(&bstrOuter);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
pDesignElem = GetDesignElem(pDesignDoc);
|
|
if (!pDesignElem)
|
|
goto Cleanup;
|
|
|
|
hr = pDesignElem->put_outerHTML(bstrOuter);
|
|
|
|
Cleanup:
|
|
SysFreeString(bstrOuter);
|
|
ReleaseInterface(pPeerElement);
|
|
ReleaseInterface(pDesignElem);
|
|
return( hr );
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// Member : TransferControlValues
|
|
//
|
|
// Synopsis : Transfers the values of the control by cooking up a
|
|
// stream and calling IPersitHistory::Save on the browse-control
|
|
// and then feeding that stream into IPersistHistory::Load on
|
|
// the design-control, Then when the design document is saved,
|
|
// IPersistPropertyBag::save will get called (it is this that finally
|
|
// allows the controls state to be written out as the altHTML attribute.
|
|
//
|
|
//+-----------------------------------------------------------
|
|
HRESULT
|
|
CPersistSnapshot::TransferControlValues (IHTMLDocument2 *pDesignDoc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IHTMLElement * pDesignElem = NULL;
|
|
IHTMLElement * pPeerElement = NULL;
|
|
IPersistHistory * pSrcPH = NULL;
|
|
IPersistHistory * pDesignPH = NULL;
|
|
IStream * pStream = NULL;
|
|
HGLOBAL hg = NULL;
|
|
static LARGE_INTEGER i64Zero = {0, 0};
|
|
|
|
_pPeerSite->GetElement(&pPeerElement);
|
|
if (!pPeerElement)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pDesignElem = GetDesignElem(pDesignDoc);
|
|
if (!pDesignElem)
|
|
goto Cleanup;
|
|
|
|
// step 1. get the appropriate IPersistHistory interfaces.
|
|
hr = pPeerElement->QueryInterface(IID_IPersistHistory, (void**)&pSrcPH);
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = (pDesignElem->QueryInterface(IID_IPersistHistory, (void**)&pDesignPH));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// step 2. create a temporary stream
|
|
hg = GlobalAlloc(GMEM_MOVEABLE, 0);
|
|
if (!hg)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = (CreateStreamOnHGlobal(hg, TRUE, &pStream));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// Step 3. save the browse object
|
|
hr = (pSrcPH->SaveHistory(pStream));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// Step 4. reset the stream
|
|
hr = (pStream->Seek(i64Zero, STREAM_SEEK_SET, NULL));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
// Stpe 5. Load the design object.
|
|
hr = (pDesignPH->LoadHistory(pStream, NULL));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
Cleanup:
|
|
// ISSUE (carled) is there a memory leak here? the hGlobal was allocated,
|
|
// a stream was created, and freed but where does the memory get freed?
|
|
ReleaseInterface(pPeerElement);
|
|
ReleaseInterface(pStream);
|
|
ReleaseInterface(pSrcPH);
|
|
ReleaseInterface(pDesignPH);
|
|
ReleaseInterface(pDesignElem);
|
|
return( (hr == E_NOINTERFACE)? S_OK : hr );
|
|
}
|
|
|
|
|
|
//+-----------------------------------------------------------
|
|
//
|
|
// Member : TransferScriptValues
|
|
//
|
|
// Synopsis: transfers the values of script blocks, by loading this
|
|
// script into a private script engine, and then iterating over all
|
|
// the values and getting the current values from the browse document.
|
|
// These (Variable, Value) pairs are then spit out as the new innerHTML
|
|
// of the design-script element.
|
|
//
|
|
//+-----------------------------------------------------------
|
|
|
|
HRESULT
|
|
CPersistSnapshot::TransferScriptValues (IHTMLDocument2 *pDesignDoc)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IHTMLElement * pDesignElem = NULL;
|
|
BSTR bstrNewBlock = NULL;
|
|
CBufferedStr cbsNewScript;
|
|
ULONG uFlags = 0;
|
|
IHTMLScriptElement * pScriptElem = NULL;
|
|
|
|
pDesignElem = GetDesignElem(pDesignDoc);
|
|
if (!pDesignElem)
|
|
goto Cleanup;
|
|
|
|
// get the text of the new script block
|
|
hr = (BuildNewScriptBlock(&cbsNewScript, &uFlags));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
bstrNewBlock = SysAllocString((LPTSTR)cbsNewScript);
|
|
if (!bstrNewBlock)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the new script assignments
|
|
hr = (pDesignElem->QueryInterface(IID_IHTMLScriptElement, (void**)&pScriptElem));
|
|
if ( hr )
|
|
goto Cleanup;
|
|
|
|
hr = (pScriptElem->put_text(bstrNewBlock));
|
|
if ( hr )
|
|
goto Cleanup;
|
|
|
|
// since all the namespaces exist on the "window"
|
|
// we need to make sure that the script block we
|
|
// write out is in a language whose syntax we know. As
|
|
// a result a script block from any ActiveScript language
|
|
// will get persisted, but unless it is VBScript or JScript,
|
|
// in the saveing, it is translated into JScript.
|
|
if (uFlags & SCRIPT_ENGINE_OTHER)
|
|
{
|
|
BSTR bstrForceLanguage = SysAllocString(L"JScript");
|
|
if (!bstrForceLanguage)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = (pDesignElem->put_language(bstrForceLanguage));
|
|
SysFreeString(bstrForceLanguage);
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
SysFreeString(bstrNewBlock);
|
|
ReleaseInterface(pScriptElem);
|
|
ReleaseInterface(pDesignElem);
|
|
return( hr );
|
|
}
|
|
|
|
|