Leaked source code of windows server 2003
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

  1. //+=============================================================================
  2. //
  3. // File : persist.cxx
  4. //
  5. // contents : implementation of CPersistData xtag
  6. //
  7. //=============================================================================
  8. #include "headers.h"
  9. #pragma MARK_DATA(__FILE__)
  10. #pragma MARK_CODE(__FILE__)
  11. #pragma MARK_CONST(__FILE__)
  12. #ifndef X_MSHTML_H_
  13. #define X_MSHTML_H_
  14. #include "mshtml.h" // for IHTML*ELement*
  15. #endif
  16. #ifndef X_PERHIST_H_
  17. #define X_PERHIST_H_
  18. #include "perhist.h" // For IPersistHistory
  19. #endif
  20. #ifndef X_DISPEX_H_
  21. #define X_DISPEX_H_
  22. #include "dispex.h" // For IDispatchEx
  23. #endif
  24. #ifndef X_PERSIST_HXX_
  25. #define X_PERSIST_HXX_
  26. #include "persist.hxx"
  27. #endif
  28. #ifndef __X_IEXTAG_H_
  29. #define __X_IEXTAG_H_
  30. #include "iextag.h"
  31. #endif
  32. #ifndef __X_UTILS_HXX_
  33. #define __X_UTILS_HXX_
  34. #include "utils.hxx"
  35. #endif
  36. //+----------------------------------------------------------------------------
  37. //
  38. // Member : Init
  39. //
  40. // Synopsis : this method is called by MSHTML.dll to initialize peer object
  41. //
  42. //-----------------------------------------------------------------------------
  43. HRESULT
  44. CPersistDataPeer::Init(IElementBehaviorSite * pPeerSite)
  45. {
  46. _pPeerSite = pPeerSite;
  47. _pPeerSite->AddRef();
  48. _pPeerSite->QueryInterface(IID_IElementBehaviorSiteOM, (void**)&_pPeerSiteOM);
  49. // now register our 2 events
  50. if (_pPeerSiteOM)
  51. {
  52. _pPeerSiteOM->RegisterEvent (_T("onload"), 0, NULL);
  53. _pPeerSiteOM->RegisterEvent (_T("onsave"), 0, NULL);
  54. }
  55. return S_OK;
  56. }
  57. //+----------------------------------------------------------------------------
  58. //
  59. // Member : Notify
  60. //
  61. // Synopsis : when document is ready for modifications, setup timer calls
  62. //
  63. //-----------------------------------------------------------------------------
  64. HRESULT
  65. CPersistDataPeer::Notify(LONG lEvent, VARIANT *)
  66. {
  67. return S_OK;
  68. }
  69. //+----------------------------------------------------------------------------
  70. //
  71. // member : queryType
  72. //
  73. // Synopsis : IPersistData method, this is used by the calling routines in order
  74. // to avoid cocreating the XML object. This way they can query any of the
  75. // persist Tags, and find out what persist behaiors it supprots.
  76. //-----------------------------------------------------------------------------
  77. HRESULT
  78. CPersistDataPeer::queryType(long lType, VARIANT_BOOL * pfSupportsType)
  79. {
  80. if (!pfSupportsType)
  81. return E_POINTER;
  82. *pfSupportsType = (lType == (long)_eState) ? VB_TRUE : VB_FALSE;
  83. return S_OK;
  84. }
  85. //+----------------------------------------------------------------------------
  86. //
  87. // Member : Save
  88. //
  89. // Synopsis : Implementation of the IHTMLPersistData method
  90. //
  91. //-----------------------------------------------------------------------------
  92. HRESULT
  93. CPersistDataPeer::save(IUnknown * pUnk,
  94. long lType,
  95. VARIANT_BOOL *pfBroadcast)
  96. {
  97. HRESULT hr;
  98. BSTR bstrEvent;
  99. if (!pfBroadcast)
  100. {
  101. hr = E_POINTER;
  102. goto Cleanup;
  103. }
  104. *pfBroadcast = VB_TRUE;
  105. // cache the OM pointers
  106. InitOM(pUnk, lType);
  107. bstrEvent = SysAllocString(L"onsave");
  108. if (!bstrEvent)
  109. {
  110. hr = E_OUTOFMEMORY;
  111. goto Cleanup;
  112. }
  113. // allow script to save data
  114. hr = (FireEvent(bstrEvent, pfBroadcast, TRUE));
  115. SysFreeString(bstrEvent);
  116. Cleanup:
  117. return( hr );
  118. }
  119. //+----------------------------------------------------------------------------
  120. //
  121. // Member : Load
  122. //
  123. // Synopsis : Implementation of the IHTMLPersistData method
  124. //
  125. //-----------------------------------------------------------------------------
  126. HRESULT
  127. CPersistDataPeer::load(IUnknown * pUnk,
  128. long lType,
  129. VARIANT_BOOL *pfContinue)
  130. {
  131. HRESULT hr;
  132. BSTR bstrEvent;
  133. if (!pfContinue)
  134. {
  135. hr = E_POINTER;
  136. goto Cleanup;
  137. }
  138. *pfContinue = VB_TRUE;
  139. // cache the OM pointers
  140. InitOM(pUnk, lType, INIT_USE_CACHED);
  141. bstrEvent = SysAllocString(L"onload");
  142. if (!bstrEvent)
  143. {
  144. hr = E_OUTOFMEMORY;
  145. goto Cleanup;
  146. }
  147. // allow event handlers the chance to load from the cach
  148. hr = (FireEvent(bstrEvent, pfContinue, FALSE));
  149. SysFreeString(bstrEvent);
  150. Cleanup:
  151. return( hr );
  152. }
  153. //+--------------------------------------------------------------------------------
  154. //
  155. // Member : FireEvent
  156. //
  157. // Synopsis : helper method to fire the persistence events
  158. //
  159. //---------------------------------------------------------------------------------
  160. HRESULT
  161. CPersistDataPeer::FireEvent(BSTR bstrEvent, VARIANT_BOOL * pfContinue, BOOL fIsSaveEvent)
  162. {
  163. IHTMLEventObj * pEventObj = NULL;
  164. IHTMLEventObj2 * pEO2 = NULL;
  165. LONG lCookie;
  166. HRESULT hr = E_PENDING;
  167. VARIANT varRet;
  168. if (!_pPeerSiteOM)
  169. goto Cleanup;
  170. VariantInit(&varRet);
  171. // create an event object
  172. hr = _pPeerSiteOM->CreateEventObject(&pEventObj);
  173. if (hr || !pEventObj)
  174. goto Cleanup;
  175. // Now populate the event object with whatever properties are
  176. // appropriate for this event:
  177. hr = pEventObj->QueryInterface(IID_IHTMLEventObj2, (void**)&pEO2);
  178. if (hr==S_OK)
  179. {
  180. BSTR bstrEventType = SysAllocString( (fIsSaveEvent) ? L"save" : L"load" );
  181. // we need to set the event type, the event type is either
  182. // "load" or "save".
  183. if (bstrEventType)
  184. {
  185. pEO2->put_type( bstrEventType );
  186. SysFreeString(bstrEventType);
  187. }
  188. }
  189. // get the event cookie to fire the event
  190. hr = _pPeerSiteOM->GetEventCookie (bstrEvent, &lCookie);
  191. if (hr)
  192. goto Cleanup;
  193. hr = _pPeerSiteOM->FireEvent (lCookie, pEventObj);
  194. if (pfContinue)
  195. {
  196. hr = pEventObj->get_returnValue(&varRet);
  197. if (!hr)
  198. *pfContinue = ((V_VT(&varRet) == VT_BOOL) &&
  199. (V_BOOL(&varRet) == VB_FALSE))? VB_FALSE : VB_TRUE;
  200. }
  201. Cleanup:
  202. VariantClear(&varRet);
  203. ReleaseInterface(pEventObj);
  204. ReleaseInterface(pEO2);
  205. return ( hr );
  206. }
  207. //+-----------------------------------------------------------------------
  208. //
  209. // Member : ClearOMInterfaces ()
  210. //
  211. // Synopsis : this helper function is called after the Save/Load persistenceCache
  212. // operations are finished, and is responsible for freeing up any of hte OM
  213. // interfaces that were cached.
  214. //
  215. //------------------------------------------------------------------------
  216. void
  217. CPersistDataPeer::ClearOMInterfaces()
  218. {
  219. ClearInterface(&_pRoot);
  220. ClearInterface(&_pInnerXMLDoc);
  221. }
  222. //---------------------------------------------------------------------------
  223. //
  224. // Member: CPersistDataPeer::InitOM
  225. //
  226. // Synopsis: IHTMLPersistData OM method implementation
  227. //
  228. //---------------------------------------------------------------------------
  229. HRESULT
  230. CPersistDataPeer::InitOM(IUnknown * pUnk, long lType, DWORD dwFlags /* ==0*/)
  231. {
  232. HRESULT hr = S_OK;
  233. IHTMLElement * pPeerElement = NULL;
  234. long lElemID = -1;
  235. BSTR bstrTag =NULL;
  236. CBufferedStr cbsID;
  237. IXMLDOMNodeList * pChildren = NULL;
  238. IXMLDOMElement * pNewChild = NULL;
  239. IXMLDOMNode * pSubTree = NULL;
  240. if (!pUnk)
  241. return E_INVALIDARG;
  242. if (_pInnerXMLDoc)
  243. {
  244. if (dwFlags & INIT_USE_CACHED)
  245. goto Cleanup;
  246. else
  247. ClearOMInterfaces();
  248. }
  249. // make sure we know what we have been given.
  250. hr = (pUnk->QueryInterface(IID_IXMLDOMDocument, (void**)&_pInnerXMLDoc));
  251. if (hr)
  252. goto Cleanup;
  253. // find the data value...
  254. hr = _pInnerXMLDoc->get_documentElement( &_pRoot );
  255. if (hr)
  256. goto Cleanup;
  257. // but in most cases the xmlObject is a big bucket, and we would like
  258. // to set the _pRoot to the subtree associated with our own _pPeerElement.
  259. // if this root doesn't exist, create it. we try to find an ID first, if
  260. // if it is not there, then create a unique name for this child subtree
  261. // but appending the srcID to the tagName.
  262. _pPeerSite->GetElement(&pPeerElement);
  263. if (!pPeerElement)
  264. goto Cleanup;
  265. hr = pPeerElement->get_id(&bstrTag);
  266. if (hr || !bstrTag || !SysStringLen(bstrTag))
  267. {
  268. SysFreeString(bstrTag);
  269. hr = pPeerElement->get_sourceIndex(&lElemID);
  270. if (hr)
  271. goto Cleanup;
  272. hr = pPeerElement->get_tagName(&bstrTag);
  273. if (hr)
  274. goto Cleanup;
  275. hr = cbsID.QuickAppend(bstrTag);
  276. SysFreeString(bstrTag);
  277. if (hr)
  278. goto Cleanup;
  279. hr = cbsID.QuickAppend(lElemID);
  280. if (hr)
  281. goto Cleanup;
  282. bstrTag = SysAllocString(cbsID);
  283. }
  284. if (!bstrTag)
  285. {
  286. hr = E_OUTOFMEMORY;
  287. goto Cleanup;
  288. }
  289. // now we can actually see if there is a child of this name
  290. // We need to do this by looping over the childNode collection
  291. // and looking for one with our name.
  292. //--------------------------------------------------------------
  293. hr = _pRoot->get_childNodes(&pChildren);
  294. if (FAILED(hr))
  295. goto Cleanup;
  296. if (hr == S_OK)
  297. {
  298. long i, iLen;
  299. hr = pChildren->get_length(&iLen);
  300. if (FAILED(hr))
  301. goto Cleanup;
  302. for (i=0; i < iLen; i++)
  303. {
  304. IXMLDOMNode * pTemp = NULL;
  305. BSTR bstrNodeName = NULL;
  306. BOOL fEqual = FALSE;
  307. hr= pChildren->get_item(i, &pTemp);
  308. if (FAILED(hr))
  309. break;
  310. hr = pTemp->get_nodeName( &bstrNodeName );
  311. if (FAILED(hr))
  312. break;
  313. fEqual = ! _wcsicmp(bstrTag, bstrNodeName);
  314. SysFreeString(bstrNodeName);
  315. if (fEqual)
  316. {
  317. // transfer ownership
  318. pSubTree = pTemp;
  319. break;
  320. }
  321. ClearInterface( &pTemp );
  322. }
  323. }
  324. if(pSubTree)
  325. {
  326. // yes there's a child so use it, we have a domnode so we need to
  327. // qi for the element
  328. ClearInterface(&_pRoot);
  329. hr = pSubTree->QueryInterface(IID_IXMLDOMElement, (void**)&_pRoot);
  330. }
  331. else
  332. {
  333. // no child yet so lets create one.
  334. hr = _pInnerXMLDoc->createElement(bstrTag, &pNewChild);
  335. if (hr || ! pNewChild)
  336. goto Cleanup;
  337. hr = _pRoot->appendChild(pNewChild, NULL);
  338. if (hr)
  339. goto Cleanup;
  340. ClearInterface(&_pRoot);
  341. _pRoot=pNewChild;
  342. pNewChild = NULL; // transfer ownership
  343. }
  344. Cleanup:
  345. ReleaseInterface(pPeerElement);
  346. SysFreeString(bstrTag);
  347. ReleaseInterface(pChildren);
  348. ReleaseInterface(pSubTree);
  349. ReleaseInterface(pNewChild);
  350. return( hr );
  351. }
  352. //---------------------------------------------------------------------------
  353. //
  354. // Member: CPersistDataPeer::getAttribute
  355. //
  356. // Synopsis: IHTMLPersistData OM method implementation
  357. //
  358. //---------------------------------------------------------------------------
  359. HRESULT
  360. CPersistDataPeer::getAttribute (BSTR strName, VARIANT * pvarValue )
  361. {
  362. HRESULT hr = S_OK;
  363. if (!pvarValue)
  364. {
  365. hr = E_POINTER;
  366. goto Cleanup;
  367. }
  368. VariantClear(pvarValue);
  369. if (!strName)
  370. {
  371. hr = E_INVALIDARG;
  372. goto Cleanup;
  373. }
  374. hr = InitOM();
  375. if (hr)
  376. {
  377. // no OM pointers so fail silently
  378. hr = S_OK;
  379. goto Cleanup;
  380. }
  381. // get the child of the root that has the name strName
  382. if (_pRoot)
  383. {
  384. hr = _pRoot->getAttribute(strName, pvarValue);
  385. if (hr ==S_FALSE)
  386. hr = S_OK;
  387. }
  388. Cleanup:
  389. return hr ;
  390. }
  391. //---------------------------------------------------------------------------
  392. //
  393. // Member: CPersistDataPeer::setAttribute
  394. //
  395. // Synopsis: IHTMLPersistData OM method implementation
  396. //
  397. //---------------------------------------------------------------------------
  398. HRESULT
  399. CPersistDataPeer::setAttribute (BSTR strName, VARIANT varValue)
  400. {
  401. HRESULT hr = S_OK;
  402. if (!strName)
  403. {
  404. hr = E_INVALIDARG;
  405. goto Cleanup;
  406. }
  407. hr = InitOM();
  408. if (hr)
  409. {
  410. // no OM pointers so fail silently
  411. hr = S_OK;
  412. goto Cleanup;
  413. }
  414. // save this value as an attribute on the root.
  415. if (_pRoot)
  416. {
  417. // TODO - look into just passing the variant into the xml object
  418. // and let them worry about all the types... We do the processing below
  419. // to be safe for now.
  420. VARIANT * pvar = NULL;
  421. CVariant cvarTemp;
  422. if ((V_VT(&varValue)==VT_BSTR) ||
  423. V_VT(&varValue)==(VT_BYREF|VT_BSTR))
  424. {
  425. pvar = (V_VT(&varValue) & VT_BYREF) ?
  426. V_VARIANTREF(&varValue) : &varValue;
  427. }
  428. else if ((V_VT(&varValue)==VT_BOOL ||
  429. V_VT(&varValue)==(VT_BYREF|VT_BOOL)))
  430. {
  431. // sadly, do our own bool conversion...
  432. VARIANT_BOOL vbFlag = (V_VT(&varValue)==VT_BOOL) ?
  433. V_BOOL(&varValue) :
  434. V_BOOL( V_VARIANTREF(&varValue) );
  435. V_VT(&cvarTemp) = VT_BSTR;
  436. V_BSTR(&cvarTemp) = vbFlag ? SysAllocString(L"true") :
  437. SysAllocString(L"false");
  438. pvar = & cvarTemp;
  439. }
  440. else
  441. {
  442. pvar = &varValue;
  443. hr = VariantChangeTypeEx(pvar, pvar, LCID_SCRIPTING, 0, VT_BSTR);
  444. if (hr)
  445. goto Cleanup;
  446. }
  447. hr = _pRoot->setAttribute(strName, *pvar);
  448. if (hr ==S_FALSE)
  449. hr = S_OK;
  450. }
  451. Cleanup:
  452. return hr;
  453. }
  454. //---------------------------------------------------------------------------
  455. //
  456. // Member: CPersistDataPeer::removeDataValue
  457. //
  458. // Synopsis: IHTMLPersistData OM method implementation
  459. //
  460. //---------------------------------------------------------------------------
  461. HRESULT
  462. CPersistDataPeer::removeAttribute (BSTR strName)
  463. {
  464. HRESULT hr = S_OK;
  465. if (!strName)
  466. {
  467. hr = E_INVALIDARG;
  468. goto Cleanup;
  469. }
  470. hr = InitOM();
  471. if (hr)
  472. {
  473. // no OM pointers so fail silently
  474. hr = S_OK;
  475. goto Cleanup;
  476. }
  477. // get the child of the root that has the name strName
  478. if (_pRoot)
  479. {
  480. hr = _pRoot->removeAttribute(strName);
  481. if (hr ==S_FALSE)
  482. hr = S_OK;
  483. }
  484. Cleanup:
  485. return hr ;
  486. }
  487. //---------------------------------------------------------------------------
  488. //
  489. // Member: CPersistDataPeer::get_XMLDocument
  490. //
  491. // Synopsis: IHTMLPersistData OM proeprty implementation. this is the default
  492. // property for this object, and as such it exposes the XMLOM
  493. // of the user data.
  494. //
  495. //---------------------------------------------------------------------------
  496. HRESULT
  497. CPersistDataPeer::get_XMLDocument (IDispatch ** ppDisp)
  498. {
  499. HRESULT hr = S_OK;
  500. if (!ppDisp)
  501. {
  502. hr = E_POINTER;
  503. goto Cleanup;
  504. }
  505. *ppDisp = NULL;
  506. hr = InitOM();
  507. if (hr)
  508. {
  509. // no OM pointers so fail silently
  510. hr = S_OK;
  511. goto Cleanup;
  512. }
  513. if (_pInnerXMLDoc)
  514. {
  515. hr = _pInnerXMLDoc->QueryInterface(IID_IDispatch,
  516. (void**)ppDisp);
  517. }
  518. Cleanup:
  519. return hr;
  520. }
  521. //+-----------------------------------------------------------
  522. //
  523. // Member : GetSaveCategory
  524. //
  525. // Synopsis : this helper function turns the tagName fo the
  526. // pPeerElement, into the category for its save operations.
  527. //
  528. //+-----------------------------------------------------------
  529. ENUM_SAVE_CATEGORY
  530. CPersistDataPeer::GetSaveCategory()
  531. {
  532. HRESULT hr = S_OK;
  533. CVariant cvarTag;
  534. IHTMLElement * pPeerElement = NULL;
  535. ENUM_SAVE_CATEGORY escRet = ESC_UNKNOWN;
  536. _pPeerSite->GetElement(&pPeerElement);
  537. if (!pPeerElement)
  538. goto Cleanup;
  539. V_VT(&cvarTag) = VT_BSTR;
  540. hr = pPeerElement->get_tagName(&V_BSTR(&cvarTag));
  541. if (hr)
  542. goto Cleanup;
  543. // there's got to be a better way to do this, but for
  544. // now I will try to put the most common ones in front.
  545. if (0==_wcsicmp(V_BSTR(&cvarTag), L"input"))
  546. {
  547. // but wait!!!! don't save password type inputs
  548. BSTR bstrType = SysAllocString(L"type");
  549. CVariant cvarVal;
  550. escRet = ESC_INTRINSIC;
  551. if (!bstrType)
  552. goto Cleanup;
  553. hr = (pPeerElement->getAttribute(bstrType, 0, &cvarVal));
  554. SysFreeString(bstrType);
  555. if (hr)
  556. goto Cleanup;
  557. if (V_VT(&cvarVal) == VT_BSTR &&
  558. 0==_wcsicmp(V_BSTR(&cvarVal), L"password"))
  559. {
  560. escRet = ESC_PASSWORD;
  561. goto Cleanup;
  562. }
  563. }
  564. else if (0==_wcsicmp(V_BSTR(&cvarTag), L"script"))
  565. {
  566. escRet = ESC_SCRIPT;
  567. }
  568. else if (0==_wcsicmp(V_BSTR(&cvarTag), L"select") ||
  569. 0==_wcsicmp(V_BSTR(&cvarTag), L"textarea") ||
  570. 0==_wcsicmp(V_BSTR(&cvarTag), L"richtext") ||
  571. 0==_wcsicmp(V_BSTR(&cvarTag), L"button") ||
  572. 0==_wcsicmp(V_BSTR(&cvarTag), L"fieldset")
  573. )
  574. {
  575. escRet = ESC_INTRINSIC;
  576. }
  577. else if (0==_wcsicmp(V_BSTR(&cvarTag), L"object") ||
  578. 0==_wcsicmp(V_BSTR(&cvarTag), L"embed") ||
  579. 0==_wcsicmp(V_BSTR(&cvarTag), L"applet")
  580. )
  581. {
  582. escRet = ESC_CONTROL;
  583. }
  584. Cleanup:
  585. ReleaseInterface(pPeerElement);
  586. return escRet;
  587. }
  588. //+-----------------------------------------------------------
  589. //
  590. // member : GetEngineClsidForLanguage ()
  591. //
  592. // synopsis : another helper method that returns the CLSID of the
  593. // scripdEngine associated with the language progID.
  594. //
  595. //+-----------------------------------------------------------
  596. HRESULT
  597. CPersistDataPeer::GetEngineClsidForLanguage(CLSID * pclsid,
  598. IHTMLDocument2 * pBrowseDoc)
  599. {
  600. HRESULT hr = S_OK;
  601. BSTR bstrLanguage = NULL;
  602. IHTMLElement * pPeerElement = NULL;
  603. if (!pclsid)
  604. return E_POINTER;
  605. *pclsid = IID_NULL;
  606. _pPeerSite->GetElement(&pPeerElement);
  607. if (!pPeerElement)
  608. {
  609. hr = E_FAIL;
  610. goto Cleanup;
  611. }
  612. // what language script engine to instantiate?
  613. hr = (pPeerElement->get_language(&bstrLanguage));
  614. if (hr)
  615. goto Cleanup;
  616. if (!bstrLanguage || !SysStringLen(bstrLanguage))
  617. {
  618. IHTMLElementCollection * pCollection = NULL;
  619. hr = E_FAIL;
  620. if (bstrLanguage)
  621. {
  622. SysFreeString(bstrLanguage);
  623. bstrLanguage = NULL;
  624. }
  625. // use the default language, we get this from the language of
  626. // first script block in the document.
  627. if (!FAILED(pBrowseDoc->get_scripts(&pCollection)))
  628. {
  629. CVariant cvarID;
  630. CVariant cvarEmpty;
  631. IDispatch * pDisp;
  632. V_VT(&cvarID) = VT_I4;
  633. V_I4(&cvarID) = 0;
  634. if (!FAILED(pCollection->item(cvarID, cvarEmpty, &pDisp)))
  635. {
  636. IHTMLElement * pFirstScript = NULL;
  637. if (pDisp &&
  638. !FAILED(pDisp->QueryInterface(IID_IHTMLElement,
  639. (void**)&pFirstScript)))
  640. {
  641. pFirstScript->get_language(&bstrLanguage);
  642. if (bstrLanguage && SysStringLen(bstrLanguage))
  643. hr = CLSIDFromProgID ( bstrLanguage, pclsid );
  644. ReleaseInterface(pFirstScript);
  645. }
  646. ReleaseInterface(pDisp);
  647. }
  648. ReleaseInterface(pCollection);
  649. }
  650. if (hr)
  651. {
  652. hr = CLSIDFromProgID ( L"JScript", pclsid );
  653. }
  654. }
  655. else
  656. {
  657. hr = CLSIDFromProgID ( bstrLanguage, pclsid );
  658. }
  659. Cleanup:
  660. SysFreeString(bstrLanguage);
  661. ReleaseInterface(pPeerElement);
  662. return ( hr );
  663. }
  664. //+-----------------------------------------------------------
  665. //
  666. // Member GetScriptEngine
  667. //
  668. // Synopsis : helper method - this creates the appropriate script
  669. // engine, and parses in the text of hte script block that we
  670. // are interested in.
  671. //
  672. //+-----------------------------------------------------------
  673. IActiveScript *
  674. CPersistDataPeer::GetScriptEngine(IHTMLDocument2 * pBrowseDoc, ULONG * puFlags)
  675. {
  676. HRESULT hr = S_OK;
  677. BSTR bstrCode = NULL;
  678. CLSID clsID;
  679. CLSID clsIDTarget;
  680. IActiveScript * pScriptEngine = NULL;
  681. IActiveScriptSite * pScriptSite = NULL;
  682. IActiveScriptParse * pASP = NULL;
  683. IHTMLScriptElement * pScriptElem = NULL;
  684. IHTMLElement * pPeerElement = NULL;
  685. hr = (GetEngineClsidForLanguage(&clsID, pBrowseDoc));
  686. if (hr)
  687. goto Cleanup;
  688. // set the return flags, so that these tests only need to be done once
  689. // some callers need to know if the script block is jscript, vbscript,
  690. // of something else
  691. if (!FAILED(CLSIDFromProgID ( _T("JScript"), &clsIDTarget )) &&
  692. (clsID == clsIDTarget))
  693. {
  694. *puFlags = SCRIPT_ENGINE_JSCRIPT;
  695. }
  696. else if (!FAILED(CLSIDFromProgID ( _T("VBScript"), &clsIDTarget )) &&
  697. (clsID == clsIDTarget))
  698. {
  699. *puFlags = SCRIPT_ENGINE_VBSCRIPT;
  700. }
  701. else
  702. *puFlags = SCRIPT_ENGINE_OTHER;
  703. // create the script engine
  704. hr = CoCreateInstance( clsID, NULL, CLSCTX_INPROC_SERVER,
  705. IID_IActiveScript,(void **)&pScriptEngine);
  706. if ( hr )
  707. goto Cleanup;
  708. // get the scriptSite interface from ourselves.
  709. hr = (QueryInterface(IID_IActiveScriptSite, (void**) &pScriptSite));
  710. if (hr)
  711. goto Cleanup;
  712. hr = pScriptEngine->SetScriptSite(pScriptSite);
  713. ReleaseInterface(pScriptSite); // engine should have addref'd
  714. if ( FAILED(hr) )
  715. goto Error;
  716. // prepare to load out text
  717. hr = (pScriptEngine->QueryInterface (IID_IActiveScriptParse, (void**)&pASP));
  718. if ( hr )
  719. goto Error;
  720. hr = (pASP->InitNew());
  721. if ( hr )
  722. goto Error;
  723. _pPeerSite->GetElement(&pPeerElement);
  724. if (!pPeerElement)
  725. {
  726. hr = E_FAIL;
  727. goto Cleanup;
  728. }
  729. // get the text of the script element
  730. hr = (pPeerElement->QueryInterface(IID_IHTMLScriptElement, (void**)&pScriptElem));
  731. if ( hr )
  732. goto Error;
  733. hr = (pScriptElem->get_text(&bstrCode));
  734. if ( hr || !bstrCode)
  735. goto Error;
  736. hr = pASP->ParseScriptText( bstrCode,
  737. NULL, // Item Name (Namespace)
  738. NULL, // context
  739. NULL, // delimiter
  740. 0, // srcContext Cookie
  741. 0, // ulStarting Line
  742. SCRIPTTEXT_ISVISIBLE, //dwFlags
  743. NULL, // pVarResult
  744. NULL ); // pException Info
  745. if ( hr )
  746. goto Error;
  747. hr = pScriptEngine->SetScriptState(SCRIPTSTATE_CONNECTED);
  748. if ( FAILED(hr) )
  749. goto Error;
  750. Cleanup:
  751. SysFreeString(bstrCode);
  752. ReleaseInterface(pPeerElement);
  753. ReleaseInterface(pScriptElem);
  754. ReleaseInterface(pASP);
  755. return pScriptEngine;
  756. Error:
  757. ClearInterface(&pScriptEngine);
  758. goto Cleanup;
  759. }
  760. //+-----------------------------------------------------------
  761. //
  762. // Member BuildNewScriptBlock
  763. //
  764. // Synopsis : helper method, this does the real work of snapshotting a
  765. // script block. IF the script is VBSCRIPT or JSCRIPT the appropriate
  766. // syntax is used. for any other language, we convert to JScript.
  767. //
  768. //+-----------------------------------------------------------
  769. HRESULT
  770. CPersistDataPeer::BuildNewScriptBlock(CBufferedStr * pstrBuffer, ULONG *puFlags)
  771. {
  772. HRESULT hr = S_OK;
  773. IDispatch * pScriptNameSpace = NULL;
  774. IDispatchEx * pDispScript = NULL;
  775. IDispatchEx * pBrowseWin = NULL;
  776. DISPID dispidName;
  777. BSTR bstrName = NULL;
  778. IHTMLWindow2 * pWin = NULL;
  779. IActiveScript * pScriptEngine = NULL;
  780. IDispatch * pdispBrowseDoc = NULL;
  781. IHTMLDocument2 * pBrowseDoc = NULL;
  782. IHTMLElement * pPeerElement = NULL;
  783. _pPeerSite->GetElement(&pPeerElement);
  784. if (!pPeerElement)
  785. {
  786. hr = E_FAIL;
  787. goto Cleanup;
  788. }
  789. hr = (pPeerElement->get_document(&pdispBrowseDoc));
  790. if (hr)
  791. goto Cleanup;
  792. hr = (pdispBrowseDoc->QueryInterface(IID_IHTMLDocument2,
  793. (void**)&pBrowseDoc));
  794. if (hr)
  795. goto Cleanup;
  796. pScriptEngine = GetScriptEngine(pBrowseDoc, puFlags);
  797. if (!pScriptEngine)
  798. goto Cleanup; // fail silently
  799. // loop over all the dispatches in the script engine's name
  800. // space and get thier values.from..pSrcDoc
  801. hr = pScriptEngine->GetScriptDispatch( NULL, &pScriptNameSpace );
  802. if ( hr )
  803. goto Cleanup;
  804. hr = (pScriptNameSpace->QueryInterface( IID_IDispatchEx, (void**)&pDispScript));
  805. if ( hr )
  806. goto Cleanup;
  807. // inorder to derefernce the script variables we need the browse
  808. // documents window.
  809. hr = (pBrowseDoc->get_parentWindow(&pWin));
  810. if(hr)
  811. goto Cleanup;
  812. hr = (pWin->QueryInterface(IID_IDispatchEx, (void**)&pBrowseWin));
  813. if (hr)
  814. goto Cleanup;
  815. // now run through all the objects in this script engine and
  816. // find their values
  817. hr = (pDispScript->GetNextDispID(0, DISPID_STARTENUM, &dispidName));
  818. if (hr)
  819. goto Cleanup;
  820. while (dispidName)
  821. {
  822. hr = (pDispScript->GetMemberName(dispidName, &bstrName));
  823. if (!hr && bstrName)
  824. {
  825. DISPID dispidBrowser;
  826. if (!FAILED(pBrowseWin->GetDispID(bstrName, fdexNameCaseSensitive, &dispidBrowser)))
  827. {
  828. DISPPARAMS dp;
  829. CVariant cvarRes;
  830. BOOL fNeedsQuotesInScript = FALSE;
  831. dp.rgvarg = NULL;
  832. dp.rgdispidNamedArgs = NULL;
  833. dp.cArgs = 0;
  834. dp.cNamedArgs = 0;
  835. hr = (pBrowseWin->Invoke(dispidBrowser,
  836. IID_NULL,
  837. LCID_SCRIPTING,
  838. DISPATCH_PROPERTYGET,
  839. &dp,
  840. &cvarRes,
  841. NULL,
  842. NULL));
  843. fNeedsQuotesInScript = (V_VT(&cvarRes) == VT_BSTR);
  844. // Filter out VT_IDISPATCH & VT_UNKNOWN
  845. if (!hr &&
  846. V_VT(&cvarRes) != VT_DISPATCH &&
  847. V_VT(&cvarRes) != VT_UNKNOWN &&
  848. !FAILED(cvarRes.CoerceVariantArg(VT_BSTR)))
  849. {
  850. // assume JSCript if not VBScript
  851. LPTSTR pstrAssign = (*puFlags & SCRIPT_ENGINE_VBSCRIPT) ?
  852. _T("\n\r dim ") :
  853. _T("\n\r var ");
  854. // concatenate on the end of the buffer.
  855. // "\n\r var <name> = " <value>"; "
  856. hr = pstrBuffer->QuickAppend(pstrAssign);
  857. if (hr)
  858. goto Cleanup;
  859. hr = pstrBuffer->QuickAppend(bstrName);
  860. if (hr)
  861. goto Cleanup;
  862. // if vbscript, put assignment on its own line.
  863. if (*puFlags & SCRIPT_ENGINE_VBSCRIPT)
  864. {
  865. hr = pstrBuffer->QuickAppend(_T("\n\r "));
  866. if (hr)
  867. goto Cleanup;
  868. hr = pstrBuffer->QuickAppend(bstrName);
  869. if (hr)
  870. goto Cleanup;
  871. }
  872. // now handle outputing the assignment itself, careful
  873. // to only put quotes around things that were strings.
  874. hr = pstrBuffer->QuickAppend(_T(" = "));
  875. if (hr)
  876. goto Cleanup;
  877. if (fNeedsQuotesInScript )
  878. {
  879. hr = pstrBuffer->QuickAppend(_T("\""));
  880. if (hr)
  881. goto Cleanup;
  882. }
  883. if (V_VT(&cvarRes) != VT_EMPTY)
  884. {
  885. hr = pstrBuffer->QuickAppend(V_BSTR(&cvarRes));
  886. }
  887. else
  888. {
  889. // someone had dim x = Empty in their script
  890. // or var x = document.expando
  891. // and this doesn't coerc to a bstr (returned S_FALSE)
  892. // don't reset the use quotes flag,
  893. // for jscript convert to null, for vb
  894. if (!(*puFlags & SCRIPT_ENGINE_VBSCRIPT))
  895. {
  896. hr = pstrBuffer->QuickAppend(_T("null"));
  897. }
  898. else
  899. hr = pstrBuffer->QuickAppend(_T("Empty"));
  900. }
  901. if (hr)
  902. goto Cleanup;
  903. if (fNeedsQuotesInScript )
  904. {
  905. hr = pstrBuffer->QuickAppend(_T("\""));
  906. if (hr)
  907. goto Cleanup;
  908. }
  909. // VBSCRIPT doesn need ';' at the eol
  910. if (!(*puFlags & SCRIPT_ENGINE_VBSCRIPT))
  911. {
  912. hr = pstrBuffer->QuickAppend(_T(";"));
  913. }
  914. if (hr)
  915. goto Cleanup;
  916. }
  917. }
  918. SysFreeString(bstrName);
  919. bstrName = NULL;
  920. }
  921. hr = (pDispScript->GetNextDispID(0, dispidName, &dispidName));
  922. if (hr==S_FALSE)
  923. {
  924. hr = S_OK;
  925. break;
  926. }
  927. else if (hr)
  928. goto Cleanup;
  929. }
  930. Cleanup:
  931. if (pScriptEngine)
  932. pScriptEngine->Close();
  933. if (bstrName)
  934. SysFreeString(bstrName);
  935. ReleaseInterface(pPeerElement);
  936. ReleaseInterface(pBrowseDoc);
  937. ReleaseInterface(pScriptEngine);
  938. ReleaseInterface(pdispBrowseDoc);
  939. ReleaseInterface(pWin);
  940. ReleaseInterface(pBrowseWin);
  941. ReleaseInterface(pDispScript);
  942. ReleaseInterface(pScriptNameSpace);
  943. // no partial string in an error case...
  944. if (hr)
  945. pstrBuffer->Set();
  946. return( hr );
  947. }
  948. //+-----------------------------------------------------------
  949. //
  950. // member : SaveHandler_GenericTag
  951. //
  952. // synopsis : this does the generic handling when a tag has the
  953. // persistence peer. It simply gets the outerHTML and saves that
  954. // in the XML object. on the load the element will be hit
  955. // with a put_outerHTML of this string.
  956. //
  957. //+-----------------------------------------------------------
  958. HRESULT
  959. CPersistDataPeer::SaveHandler_GenericTag()
  960. {
  961. HRESULT hr = S_OK;
  962. CVariant cvarOuter;
  963. IHTMLElement * pPeerParent = NULL;
  964. IHTMLElement * pPeerElement = NULL;
  965. _pPeerSite->GetElement(&pPeerElement);
  966. if (!pPeerElement)
  967. {
  968. hr = E_FAIL;
  969. goto Cleanup;
  970. }
  971. // for elements in the head, we don't want to do this since
  972. // there is a tree limitation that doesn't allow the outerHTML
  973. // to be set.
  974. hr = pPeerElement->get_parentElement(&pPeerParent);
  975. if (hr)
  976. goto Cleanup;
  977. // the right way to do this is to ask the question:
  978. // are we in the body/frameset, NOT is our parent the
  979. // head, so...
  980. while (pPeerParent)
  981. {
  982. CVariant cvarTag;
  983. IHTMLElement * pTemp = NULL;
  984. V_VT(&cvarTag) = VT_BSTR;
  985. hr = pPeerParent->get_tagName(&V_BSTR(&cvarTag));
  986. if (hr)
  987. goto Cleanup;
  988. if (0==_wcsicmp(V_BSTR(&cvarTag), L"body") ||
  989. 0==_wcsicmp(V_BSTR(&cvarTag), L"frameset"))
  990. break;
  991. hr = pPeerParent->get_parentElement(&pTemp);
  992. if (hr)
  993. goto Cleanup;
  994. ClearInterface(&pPeerParent);
  995. pPeerParent = pTemp;
  996. }
  997. if (!pPeerParent)
  998. goto Cleanup;
  999. hr = pPeerElement->get_outerHTML(&V_BSTR(&cvarOuter));
  1000. if (hr)
  1001. goto Cleanup;
  1002. V_VT(&cvarOuter) = VT_BSTR;
  1003. // now save the outerHTML string
  1004. hr = _pRoot->setAttribute(_T("__NEW_TAG_OUTER"), cvarOuter);
  1005. if (hr)
  1006. goto Cleanup;
  1007. Cleanup:
  1008. ReleaseInterface(pPeerElement);
  1009. ReleaseInterface(pPeerParent);
  1010. return( hr );
  1011. }
  1012. //+----------------------------------------------------------------------------
  1013. //
  1014. // Member : LoadHandler_GenericTag()
  1015. //
  1016. // Synopsis : this restores the outerHTML of the tag as it is loaded
  1017. //
  1018. //+----------------------------------------------------------------------------
  1019. HRESULT
  1020. CPersistDataPeer::LoadHandler_GenericTag()
  1021. {
  1022. HRESULT hr = S_OK;
  1023. CVariant cvarOuter;
  1024. IHTMLElement * pPeerElement = NULL;
  1025. if (!_pRoot)
  1026. goto Cleanup;
  1027. _pPeerSite->GetElement(&pPeerElement);
  1028. if (!pPeerElement)
  1029. {
  1030. hr = E_FAIL;
  1031. goto Cleanup;
  1032. }
  1033. // now get the outerHTML string
  1034. hr = _pRoot->getAttribute(_T("__NEW_TAG_OUTER"), &cvarOuter);
  1035. if (hr)
  1036. goto Cleanup;
  1037. hr = cvarOuter.CoerceVariantArg(VT_BSTR);
  1038. if (hr)
  1039. goto Cleanup;
  1040. hr = pPeerElement->put_outerHTML(V_BSTR(&cvarOuter));
  1041. if (hr)
  1042. goto Cleanup;
  1043. Cleanup:
  1044. ReleaseInterface(pPeerElement);
  1045. return( hr );
  1046. }
  1047. //+------------------------------------------------------------------------------
  1048. //
  1049. // Member : SaveHandler_ScriptTag()
  1050. //
  1051. // Synopsis : saves the script into the xml store. We do this in the standard way:
  1052. // 1- create a private script engin,
  1053. // 2- load it with the text of the peer's script block
  1054. // 3 - walk through its namespace pulling out the variables
  1055. // 4 - create a list of name value pairs as the new script block
  1056. // 5 - save this block in the appropriate place
  1057. //
  1058. //+------------------------------------------------------------------------------
  1059. HRESULT
  1060. CPersistDataPeer::SaveHandler_ScriptTag()
  1061. {
  1062. HRESULT hr = S_OK;
  1063. CVariant cvarText;
  1064. BSTR bstrLanguage = NULL;
  1065. CLSID clsIDTarget;
  1066. CLSID clsIDLang;
  1067. CBufferedStr cbsNewScript;
  1068. IDispatch * pdispBrowseDoc = NULL;
  1069. IHTMLDocument2 * pBrowseDoc = NULL;
  1070. IHTMLElement * pPeerElement = NULL;
  1071. ULONG uFlags = 0;
  1072. if (!_pRoot)
  1073. goto Cleanup;
  1074. _pPeerSite->GetElement(&pPeerElement);
  1075. if (!pPeerElement)
  1076. {
  1077. hr = E_FAIL;
  1078. goto Cleanup;
  1079. }
  1080. hr = pPeerElement->get_document(&pdispBrowseDoc);
  1081. if (hr)
  1082. goto Cleanup;
  1083. hr = pdispBrowseDoc->QueryInterface(IID_IHTMLDocument2,
  1084. (void**)&pBrowseDoc);
  1085. if (hr)
  1086. goto Cleanup;
  1087. // get the new script block, and the language that it
  1088. // originated from
  1089. hr = BuildNewScriptBlock(&cbsNewScript, &uFlags);
  1090. if (hr)
  1091. goto Cleanup;
  1092. // Language can only be set in design mode, as a result
  1093. // we can't change the script block since we don't know the
  1094. // syntax for an arbitrary script engine. so, only save
  1095. // script if this is javascript or vbscript.
  1096. if (uFlags & SCRIPT_ENGINE_OTHER)
  1097. goto Cleanup;
  1098. V_VT(&cvarText) = VT_BSTR;
  1099. V_BSTR(&cvarText) = SysAllocString((LPTSTR)cbsNewScript);
  1100. if (!V_BSTR(&cvarText))
  1101. {
  1102. hr = E_OUTOFMEMORY;
  1103. goto Cleanup;
  1104. }
  1105. hr = _pRoot->setAttribute(_T("__NEW_SCRIPT_TEXT"), cvarText);
  1106. if (hr)
  1107. goto Cleanup;
  1108. Cleanup:
  1109. SysFreeString(bstrLanguage);
  1110. ReleaseInterface(pPeerElement);
  1111. ReleaseInterface(pBrowseDoc);
  1112. ReleaseInterface(pdispBrowseDoc);
  1113. return( hr );
  1114. }
  1115. //+----------------------------------------------------------------------------
  1116. //
  1117. // Member : LoadHandler_ScriptTag()
  1118. //
  1119. // Synopsis : does the load - but wait...
  1120. // NYI - put_text is design time only, so the peer needs a different way
  1121. // of sliding this in.
  1122. //
  1123. //+----------------------------------------------------------------------------
  1124. HRESULT
  1125. CPersistDataPeer::LoadHandler_ScriptTag()
  1126. {
  1127. HRESULT hr = S_OK;
  1128. CVariant cvarText;
  1129. if (!_pRoot)
  1130. goto Cleanup;
  1131. // try to get the new text from the saved XML
  1132. hr = _pRoot->getAttribute(_T("__NEW_SCRIPT_TEXT"), &cvarText);
  1133. if (hr)
  1134. goto Cleanup;
  1135. // if there is script text, then we need to set this into the
  1136. // browse window's namespace
  1137. Cleanup:
  1138. if (hr == S_FALSE) hr = S_OK;
  1139. return( hr );
  1140. }
  1141. //===============================================================
  1142. //
  1143. // Class : CPersistShortcut
  1144. //
  1145. // synopsis : derives from CPersistDataPeer and does the special handling
  1146. // related to the shortcut functionality
  1147. //
  1148. //===============================================================
  1149. //+----------------------------------------------------------------
  1150. //
  1151. // Member : save
  1152. //
  1153. // synopsis: over ride of parent functionality to deal with shortcut
  1154. // specific stuff...
  1155. //
  1156. //-----------------------------------------------------------------
  1157. HRESULT
  1158. CPersistShortcut::save(IUnknown * pUnk,
  1159. long lType,
  1160. VARIANT_BOOL *pfContinue)
  1161. {
  1162. HRESULT hr;
  1163. if (lType != (long)htmlPersistStateFavorite)
  1164. {
  1165. hr = S_OK;
  1166. goto Cleanup;
  1167. }
  1168. hr = super::save(pUnk, lType, pfContinue);
  1169. if (hr || (*pfContinue == VB_FALSE))
  1170. goto Cleanup;
  1171. // do the particular tag handling related to shortcuts
  1172. switch (GetSaveCategory())
  1173. {
  1174. case ESC_UNKNOWN:
  1175. case ESC_INTRINSIC:
  1176. case ESC_CONTROL:
  1177. hr = SaveHandler_GenericTag();
  1178. break;
  1179. // case ESC_SCRIPT:
  1180. // hr = SaveHandler_ScriptTag();
  1181. // break;
  1182. default:
  1183. break;
  1184. }
  1185. Cleanup:
  1186. return hr;
  1187. }
  1188. //+----------------------------------------------------------------------------
  1189. //
  1190. // Member : Load
  1191. //
  1192. // Synopsis : Implementation of the IHTMLPersistenceCache method
  1193. //
  1194. //-----------------------------------------------------------------------------
  1195. HRESULT
  1196. CPersistShortcut::load(IUnknown * pUnk,
  1197. long lType,
  1198. VARIANT_BOOL *pfDoDefault)
  1199. {
  1200. HRESULT hr;
  1201. if (lType != (long)htmlPersistStateFavorite)
  1202. {
  1203. hr = S_OK;
  1204. goto Cleanup;
  1205. }
  1206. // fire the event first, since the pPeerElement is about to be
  1207. // removed from the tree entirely. Of course, if the user sets
  1208. // expandoes on this, they will get lost, but the alternative is to
  1209. // loose access to the xmlcache. So, if you think about it, they get
  1210. // outerHTML behaviour, and using XMLCache behavior means
  1211. // that you better cancel default behavior.
  1212. hr = super::load(pUnk, lType, pfDoDefault);
  1213. // is default load behavior canceled ?
  1214. if (hr || (*pfDoDefault == VB_FALSE))
  1215. goto Cleanup;
  1216. switch (GetSaveCategory())
  1217. {
  1218. case ESC_UNKNOWN:
  1219. case ESC_INTRINSIC:
  1220. case ESC_CONTROL:
  1221. hr = LoadHandler_GenericTag();
  1222. break;
  1223. // case ESC_SCRIPT:
  1224. // hr = LoadHandler_ScriptTag();
  1225. // break;
  1226. default:
  1227. break;
  1228. }
  1229. Cleanup:
  1230. return hr;
  1231. }
  1232. //===============================================================
  1233. //
  1234. // Class : CPersistHistory
  1235. //
  1236. // synopsis : derives from CPersistDataPeer and does the special handling
  1237. // related to the History functionality
  1238. //
  1239. //===============================================================
  1240. //+----------------------------------------------------------------
  1241. //
  1242. // Member : save
  1243. //
  1244. // synopsis: over ride of parent functionality to deal with shortcut
  1245. // specific stuff...
  1246. //
  1247. //-----------------------------------------------------------------
  1248. HRESULT
  1249. CPersistHistory::save(IUnknown * pUnk,
  1250. long lType,
  1251. VARIANT_BOOL *pfContinue)
  1252. {
  1253. HRESULT hr;
  1254. if (lType != (long)htmlPersistStateHistory)
  1255. {
  1256. hr = S_OK;
  1257. goto Cleanup;
  1258. }
  1259. hr = super::save(pUnk, lType, pfContinue);
  1260. if (hr || (*pfContinue == VB_FALSE))
  1261. goto Cleanup;
  1262. // do any tag specific handling that history needs
  1263. switch (GetSaveCategory())
  1264. {
  1265. // if we don't know anything special to do with this tag, just
  1266. // transfer over its outerHTML on the assumption that the author
  1267. // knows what they are doing
  1268. case ESC_UNKNOWN:
  1269. case ESC_INTRINSIC:
  1270. hr = SaveHandler_GenericTag();
  1271. break;
  1272. // case ESC_SCRIPT:
  1273. // hr = SaveHandler_ScriptTag();
  1274. // break;
  1275. default :
  1276. break;
  1277. }
  1278. Cleanup:
  1279. return hr;
  1280. }
  1281. //+----------------------------------------------------------------------------
  1282. //
  1283. // Member : Load
  1284. //
  1285. // Synopsis : Implementation of the IHTMLPersistenceCache method
  1286. //
  1287. //-----------------------------------------------------------------------------
  1288. HRESULT
  1289. CPersistHistory::load(IUnknown * pUnk,
  1290. long lType,
  1291. VARIANT_BOOL *pfDoDefault)
  1292. {
  1293. HRESULT hr;
  1294. if (lType != (long)htmlPersistStateHistory)
  1295. {
  1296. hr = S_OK;
  1297. goto Cleanup;
  1298. }
  1299. // cache the OM pointers
  1300. hr = InitOM(pUnk, lType);
  1301. if (hr)
  1302. goto Cleanup;
  1303. hr = super::load(pUnk, lType, pfDoDefault);
  1304. // is default load behavior canceled ?
  1305. if (hr || (*pfDoDefault == VB_FALSE))
  1306. goto Cleanup;
  1307. // ISSUE (carled) NYI - script retoration presents a unique challenge
  1308. // because this needs to execute inline instead of the original
  1309. // script block text.
  1310. switch(GetSaveCategory())
  1311. {
  1312. // case ESC_SCRIPT:
  1313. // hr = LoadHandler_ScriptTag();
  1314. // break;
  1315. case ESC_UNKNOWN:
  1316. case ESC_INTRINSIC:
  1317. hr = LoadHandler_GenericTag();
  1318. break;
  1319. default:
  1320. break;
  1321. }
  1322. Cleanup:
  1323. return hr;
  1324. }
  1325. //===============================================================
  1326. //
  1327. // Class : CPersistSnapshot
  1328. //
  1329. // synopsis : derives from CPersistDataPeer and does the special handling
  1330. // related to the run-time same (snapshot) functionality
  1331. //
  1332. //===============================================================
  1333. //+----------------------------------------------------------------
  1334. //
  1335. // Member : CPersistSnapshot::save
  1336. //
  1337. // synopsis: over ride of parent functionality to deal with shortcut
  1338. // specific stuff...
  1339. // Since it doesn't make much sense to allow theXMLOM to be acccessed
  1340. // in this case, calling super:: pretty much only fires the event.
  1341. //
  1342. //-----------------------------------------------------------------
  1343. HRESULT
  1344. CPersistSnapshot::save(IUnknown * pUnk,
  1345. long lType,
  1346. VARIANT_BOOL *pfContinue)
  1347. {
  1348. HRESULT hr;
  1349. CVariant cvarTag;
  1350. IHTMLDocument2 * pDesignDoc = NULL;
  1351. if (lType != (long)htmlPersistStateSnapshot)
  1352. {
  1353. hr = S_OK;
  1354. goto Cleanup;
  1355. }
  1356. if (!pUnk)
  1357. {
  1358. hr = E_POINTER;
  1359. goto Cleanup;
  1360. }
  1361. hr = pUnk->QueryInterface(IID_IHTMLDocument2, (void**)&pDesignDoc);
  1362. if (hr)
  1363. goto Cleanup;
  1364. // allow the onsave event to be fired.
  1365. hr = super::save(NULL, lType, pfContinue);
  1366. if (hr || (*pfContinue == VB_FALSE))
  1367. goto Cleanup;
  1368. // now that the author has had the oppurtunity to set expandoes,
  1369. // and value properties, we start the real work of snapshot saveing.
  1370. switch (GetSaveCategory())
  1371. {
  1372. case ESC_CONTROL:
  1373. hr = TransferControlValues(pDesignDoc);
  1374. break;
  1375. case ESC_SCRIPT:
  1376. hr = TransferScriptValues(pDesignDoc);
  1377. break;
  1378. case ESC_UNKNOWN:
  1379. // if we don't know anything special to do with this tag, just
  1380. // transfer over its outerHTML on the assumption that the author
  1381. // knows what they are doing
  1382. case ESC_INTRINSIC:
  1383. hr = TransferIntrinsicValues(pDesignDoc);
  1384. break;
  1385. default:
  1386. break;
  1387. }
  1388. Cleanup:
  1389. ReleaseInterface(pDesignDoc);
  1390. return hr;
  1391. }
  1392. //+----------------------------------------------------------------------------
  1393. //
  1394. // Member : CPersistSnapshot::Load
  1395. //
  1396. // Synopsis : Implementation of the IHTMLPersistenceCache method
  1397. // Since it doesn't make much sense to allow theXMLOM to be acccessed
  1398. // in this case, calling super:: pretty much only fires the event.
  1399. //
  1400. //-----------------------------------------------------------------------------
  1401. HRESULT
  1402. CPersistSnapshot::load(IUnknown * pUnk,
  1403. long lType,
  1404. VARIANT_BOOL *pfDoDefault)
  1405. {
  1406. HRESULT hr;
  1407. if (lType != (long)htmlPersistStateSnapshot)
  1408. {
  1409. hr = S_OK;
  1410. goto Cleanup;
  1411. }
  1412. hr = super::load(pUnk, lType, pfDoDefault);
  1413. Cleanup:
  1414. return hr;
  1415. }
  1416. //+-----------------------------------------------------------
  1417. //
  1418. // member : GetDesignElem
  1419. //
  1420. // Synposis : this helper function is responsible for finding the
  1421. // design document's element counterpart to our pPeerElement.
  1422. //
  1423. //+-----------------------------------------------------------
  1424. IHTMLElement *
  1425. CPersistSnapshot::GetDesignElem(IHTMLDocument2 * pDesignDoc)
  1426. {
  1427. HRESULT hr = S_OK;
  1428. CVariant cvarID;
  1429. CVariant cvarEmpty;
  1430. IDispatch * pDisp = NULL;
  1431. IHTMLElement * pRetElem = NULL;
  1432. IHTMLElement * pPeerElement = NULL;
  1433. IHTMLElementCollection * pCollection = NULL;
  1434. _pPeerSite->GetElement(&pPeerElement);
  1435. if (!pPeerElement)
  1436. {
  1437. hr = E_FAIL;
  1438. goto Cleanup;
  1439. }
  1440. V_VT(&cvarID) = VT_BSTR;
  1441. hr = pPeerElement->get_id(&V_BSTR(&cvarID));
  1442. if (hr)
  1443. goto Cleanup;
  1444. // first get the documents all collection.
  1445. hr = pDesignDoc->get_all(&pCollection);
  1446. if (hr)
  1447. goto Cleanup;
  1448. // now find out elements coutner part inthe design document,
  1449. // if it exits. Elements that were created by inline scripts,
  1450. // or other script driven means, may not be found.
  1451. hr = pCollection->item(cvarID, cvarEmpty, &pDisp);
  1452. if (hr || !pDisp)
  1453. goto Cleanup;
  1454. // make sure that we do not have a collection...
  1455. hr = pDisp->QueryInterface(IID_IHTMLElement, (void**)&pRetElem);
  1456. Cleanup:
  1457. ReleaseInterface(pPeerElement);
  1458. ReleaseInterface(pDisp);
  1459. ReleaseInterface(pCollection);
  1460. return pRetElem;
  1461. }
  1462. //+-----------------------------------------------------------
  1463. //
  1464. // member : TransferIntrinsicValues
  1465. //
  1466. // synopsis : this transfers the value of the browse-control to the
  1467. // design control by setting the outerHTML of the design control
  1468. // to that of the browseControl
  1469. //
  1470. //+-----------------------------------------------------------
  1471. HRESULT
  1472. CPersistSnapshot::TransferIntrinsicValues (IHTMLDocument2 *pDesignDoc)
  1473. {
  1474. HRESULT hr = S_OK;
  1475. BSTR bstrOuter = NULL;
  1476. IHTMLElement * pDesignElem = NULL;
  1477. IHTMLElement * pPeerElement = NULL;
  1478. _pPeerSite->GetElement(&pPeerElement);
  1479. if (!pPeerElement)
  1480. {
  1481. hr = E_FAIL;
  1482. goto Cleanup;
  1483. }
  1484. hr = pPeerElement->get_outerHTML(&bstrOuter);
  1485. if (hr)
  1486. goto Cleanup;
  1487. pDesignElem = GetDesignElem(pDesignDoc);
  1488. if (!pDesignElem)
  1489. goto Cleanup;
  1490. hr = pDesignElem->put_outerHTML(bstrOuter);
  1491. Cleanup:
  1492. SysFreeString(bstrOuter);
  1493. ReleaseInterface(pPeerElement);
  1494. ReleaseInterface(pDesignElem);
  1495. return( hr );
  1496. }
  1497. //+-----------------------------------------------------------
  1498. //
  1499. // Member : TransferControlValues
  1500. //
  1501. // Synopsis : Transfers the values of the control by cooking up a
  1502. // stream and calling IPersitHistory::Save on the browse-control
  1503. // and then feeding that stream into IPersistHistory::Load on
  1504. // the design-control, Then when the design document is saved,
  1505. // IPersistPropertyBag::save will get called (it is this that finally
  1506. // allows the controls state to be written out as the altHTML attribute.
  1507. //
  1508. //+-----------------------------------------------------------
  1509. HRESULT
  1510. CPersistSnapshot::TransferControlValues (IHTMLDocument2 *pDesignDoc)
  1511. {
  1512. HRESULT hr = S_OK;
  1513. IHTMLElement * pDesignElem = NULL;
  1514. IHTMLElement * pPeerElement = NULL;
  1515. IPersistHistory * pSrcPH = NULL;
  1516. IPersistHistory * pDesignPH = NULL;
  1517. IStream * pStream = NULL;
  1518. HGLOBAL hg = NULL;
  1519. static LARGE_INTEGER i64Zero = {0, 0};
  1520. _pPeerSite->GetElement(&pPeerElement);
  1521. if (!pPeerElement)
  1522. {
  1523. hr = E_FAIL;
  1524. goto Cleanup;
  1525. }
  1526. pDesignElem = GetDesignElem(pDesignDoc);
  1527. if (!pDesignElem)
  1528. goto Cleanup;
  1529. // step 1. get the appropriate IPersistHistory interfaces.
  1530. hr = pPeerElement->QueryInterface(IID_IPersistHistory, (void**)&pSrcPH);
  1531. if (hr)
  1532. goto Cleanup;
  1533. hr = (pDesignElem->QueryInterface(IID_IPersistHistory, (void**)&pDesignPH));
  1534. if (hr)
  1535. goto Cleanup;
  1536. // step 2. create a temporary stream
  1537. hg = GlobalAlloc(GMEM_MOVEABLE, 0);
  1538. if (!hg)
  1539. {
  1540. hr = E_OUTOFMEMORY;
  1541. goto Cleanup;
  1542. }
  1543. hr = (CreateStreamOnHGlobal(hg, TRUE, &pStream));
  1544. if (hr)
  1545. goto Cleanup;
  1546. // Step 3. save the browse object
  1547. hr = (pSrcPH->SaveHistory(pStream));
  1548. if (hr)
  1549. goto Cleanup;
  1550. // Step 4. reset the stream
  1551. hr = (pStream->Seek(i64Zero, STREAM_SEEK_SET, NULL));
  1552. if (hr)
  1553. goto Cleanup;
  1554. // Stpe 5. Load the design object.
  1555. hr = (pDesignPH->LoadHistory(pStream, NULL));
  1556. if (hr)
  1557. goto Cleanup;
  1558. Cleanup:
  1559. // ISSUE (carled) is there a memory leak here? the hGlobal was allocated,
  1560. // a stream was created, and freed but where does the memory get freed?
  1561. ReleaseInterface(pPeerElement);
  1562. ReleaseInterface(pStream);
  1563. ReleaseInterface(pSrcPH);
  1564. ReleaseInterface(pDesignPH);
  1565. ReleaseInterface(pDesignElem);
  1566. return( (hr == E_NOINTERFACE)? S_OK : hr );
  1567. }
  1568. //+-----------------------------------------------------------
  1569. //
  1570. // Member : TransferScriptValues
  1571. //
  1572. // Synopsis: transfers the values of script blocks, by loading this
  1573. // script into a private script engine, and then iterating over all
  1574. // the values and getting the current values from the browse document.
  1575. // These (Variable, Value) pairs are then spit out as the new innerHTML
  1576. // of the design-script element.
  1577. //
  1578. //+-----------------------------------------------------------
  1579. HRESULT
  1580. CPersistSnapshot::TransferScriptValues (IHTMLDocument2 *pDesignDoc)
  1581. {
  1582. HRESULT hr = S_OK;
  1583. IHTMLElement * pDesignElem = NULL;
  1584. BSTR bstrNewBlock = NULL;
  1585. CBufferedStr cbsNewScript;
  1586. ULONG uFlags = 0;
  1587. IHTMLScriptElement * pScriptElem = NULL;
  1588. pDesignElem = GetDesignElem(pDesignDoc);
  1589. if (!pDesignElem)
  1590. goto Cleanup;
  1591. // get the text of the new script block
  1592. hr = (BuildNewScriptBlock(&cbsNewScript, &uFlags));
  1593. if (hr)
  1594. goto Cleanup;
  1595. bstrNewBlock = SysAllocString((LPTSTR)cbsNewScript);
  1596. if (!bstrNewBlock)
  1597. {
  1598. hr = E_OUTOFMEMORY;
  1599. goto Cleanup;
  1600. }
  1601. // Set the new script assignments
  1602. hr = (pDesignElem->QueryInterface(IID_IHTMLScriptElement, (void**)&pScriptElem));
  1603. if ( hr )
  1604. goto Cleanup;
  1605. hr = (pScriptElem->put_text(bstrNewBlock));
  1606. if ( hr )
  1607. goto Cleanup;
  1608. // since all the namespaces exist on the "window"
  1609. // we need to make sure that the script block we
  1610. // write out is in a language whose syntax we know. As
  1611. // a result a script block from any ActiveScript language
  1612. // will get persisted, but unless it is VBScript or JScript,
  1613. // in the saveing, it is translated into JScript.
  1614. if (uFlags & SCRIPT_ENGINE_OTHER)
  1615. {
  1616. BSTR bstrForceLanguage = SysAllocString(L"JScript");
  1617. if (!bstrForceLanguage)
  1618. {
  1619. hr = E_OUTOFMEMORY;
  1620. goto Cleanup;
  1621. }
  1622. hr = (pDesignElem->put_language(bstrForceLanguage));
  1623. SysFreeString(bstrForceLanguage);
  1624. if (hr)
  1625. goto Cleanup;
  1626. }
  1627. Cleanup:
  1628. SysFreeString(bstrNewBlock);
  1629. ReleaseInterface(pScriptElem);
  1630. ReleaseInterface(pDesignElem);
  1631. return( hr );
  1632. }