From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/msxml3/domdoc.c | 11 +- dlls/msxml3/msxml_private.h | 30 +++ dlls/msxml3/node.c | 417 +++++++++++++++++++++++++++++- dlls/msxml3/selection.c | 25 +- dlls/msxml3/xmldoc.c | 27 -- libs/xslt/libxslt/xsltInternals.h | 3 + 6 files changed, 478 insertions(+), 35 deletions(-)
diff --git a/dlls/msxml3/domdoc.c b/dlls/msxml3/domdoc.c index 8407f914c4f..b53203b9bb2 100644 --- a/dlls/msxml3/domdoc.c +++ b/dlls/msxml3/domdoc.c @@ -3792,14 +3792,19 @@ HRESULT dom_document_create(MSXML_VERSION version, void **ppObj) return hr; }
-IUnknown* create_domdoc( xmlNodePtr document ) +IUnknown* create_domdoc( xmlNodePtr node ) { + xmlDocPtr doc = (xmlDocPtr)node; IUnknown *obj = NULL; HRESULT hr;
- TRACE("(%p)\n", document); + TRACE("(%p)\n", node);
- hr = get_domdoc_from_xmldoc((xmlDocPtr)document, (IXMLDOMDocument3**)&obj); + if (!doc->_private) + xmldoc_init(doc, MSXML6); + xmldoc_add_ref(doc); + + hr = get_domdoc_from_xmldoc(doc, (IXMLDOMDocument3**)&obj); if (FAILED(hr)) return NULL;
diff --git a/dlls/msxml3/msxml_private.h b/dlls/msxml3/msxml_private.h index 63d85d3590e..9709f8247ab 100644 --- a/dlls/msxml3/msxml_private.h +++ b/dlls/msxml3/msxml_private.h @@ -21,14 +21,43 @@ #ifndef __MSXML_PRIVATE__ #define __MSXML_PRIVATE__
+#include <stdbool.h> + #include "dispex.h"
#include "wine/list.h"
+#include <libxml/xpath.h> + #include "msxml_dispex.h"
extern const CLSID * DOMDocument_version(MSXML_VERSION v);
+static inline bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) +{ + size_t new_capacity, max_capacity; + void *new_elements; + + if (count <= *capacity) + return true; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return false; + + new_capacity = max(4, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = max_capacity; + + if (!(new_elements = realloc(*elements, new_capacity * size))) + return false; + + *elements = new_elements; + *capacity = new_capacity; + return true; +}
/* The XDR datatypes (urn:schemas-microsoft-com:datatypes) * These are actually valid for XSD schemas as well @@ -139,6 +168,7 @@ extern IUnknown *create_doc_fragment( xmlNodePtr ); extern IUnknown *create_doc_entity_ref( xmlNodePtr ); extern IUnknown *create_doc_type( xmlNodePtr ); extern HRESULT create_selection( xmlNodePtr, xmlChar*, IXMLDOMNodeList** ); +extern HRESULT create_selection_from_nodeset( xmlXPathObjectPtr, IXMLDOMNodeList ** ); extern HRESULT create_enumvariant( IUnknown*, BOOL, const struct enumvariant_funcs*, IEnumVARIANT**); extern HRESULT create_dom_implementation(IXMLDOMImplementation **obj);
diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c index 5a3238edca8..23d496b64c9 100644 --- a/dlls/msxml3/node.c +++ b/dlls/msxml3/node.c @@ -33,6 +33,8 @@ #include <libxslt/xsltutils.h> #include <libxslt/xsltInternals.h> #include <libxslt/documents.h> +#include <libxslt/extensions.h> +#include <libxslt/xsltInternals.h>
#include "windef.h" #include "winbase.h" @@ -40,6 +42,7 @@ #include "winnls.h" #include "ole2.h" #include "msxml6.h" +#include <activscp.h>
#include "msxml_private.h"
@@ -1477,6 +1480,396 @@ failed: return doc; }
+#ifdef _WIN64 + +#define IActiveScriptParse_Release IActiveScriptParse64_Release +#define IActiveScriptParse_InitNew IActiveScriptParse64_InitNew +#define IActiveScriptParse_ParseScriptText IActiveScriptParse64_ParseScriptText + +#else + +#define IActiveScriptParse_Release IActiveScriptParse32_Release +#define IActiveScriptParse_InitNew IActiveScriptParse32_InitNew +#define IActiveScriptParse_ParseScriptText IActiveScriptParse32_ParseScriptText + +#endif + +struct xsl_scripts +{ + struct + { + IActiveScript *script; + const xmlChar *uri; + } *entries; + size_t count; + size_t capacity; +}; + +static IActiveScript *xpath_msxsl_script_get_script(xmlXPathParserContextPtr ctxt, const xmlChar *uri) +{ + xsltTransformContextPtr xslt_ctxt = xsltXPathGetTransformContext(ctxt); + struct xsl_scripts *scripts = xslt_ctxt->userData; + + for (size_t i = 0; i < scripts->count; ++i) + { + if (xmlStrEqual(scripts->entries[i].uri, uri)) + return scripts->entries[i].script; + } + + return NULL; +} + +static HRESULT xpath_create_nodelist_from_set(xmlNodeSetPtr set, bool needs_copy, IXMLDOMNodeList **ret) +{ + xmlNodeSet _copy = { 0 }; + xmlXPathObjectPtr obj; + HRESULT hr; + + if (needs_copy) + { + _copy.nodeTab = xmlMalloc(set->nodeNr * sizeof(*set->nodeTab)); + _copy.nodeNr = set->nodeNr; + _copy.nodeMax = set->nodeNr; + for (int i = 0; i < set->nodeNr; ++i) + _copy.nodeTab[i] = xmlCopyNode(set->nodeTab[i], 1); + set = &_copy; + } + + obj = xmlXPathNewNodeSetList(set); + hr = create_selection_from_nodeset(obj, ret); + xmlFree(_copy.nodeTab); + + return hr; +} + +static void xpath_msxsl_script_function(xmlXPathParserContextPtr ctxt, int nargs) +{ + xsltTransformContextPtr xslt_ctxt = xsltXPathGetTransformContext(ctxt); + xmlXPathContextPtr xpath = ((struct _xsltTransformContext *)xslt_ctxt)->xpathCtxt; + DISPPARAMS params = { 0 }; + IActiveScript *script; + VARIANT *args = NULL; + IDispatch *disp; + VARIANT result; + DISPID dispid; + HRESULT hr; + BSTR name; + + TRACE("%s:%s\n", xpath->functionURI, xpath->function); + + script = xpath_msxsl_script_get_script(ctxt, xpath->functionURI); + if (!script) + { + WARN("Couldn't find a script for %s.\n", xpath->functionURI); + return; + } + + if (FAILED(IActiveScript_GetScriptDispatch(script, NULL, &disp))) + return; + + name = bstr_from_xmlChar(xpath->function); + hr = IDispatch_GetIDsOfNames(disp, &IID_NULL, &name, 1, 0, &dispid); + SysFreeString(name); + if (FAILED(hr)) + { + WARN("Couldn't find %s function.\n", xpath->function); + IDispatch_Release(disp); + return; + } + + if (nargs) + { + args = calloc(nargs, sizeof(*args)); + + for (int i = 0; i < nargs; ++i) + { + xmlXPathObjectType obj_type = XPATH_UNDEFINED; + xmlXPathObjectPtr obj = NULL; + xmlChar *s; + + /* Since we don't want to impose expectations on argument types, + first inspect actual type on stack, and then pop with corresponding function. */ + if (ctxt->valueNr > 0) + { + obj = ctxt->valueTab[ctxt->valueNr - 1]; + obj_type = obj->type; + } + + switch (obj_type) + { + case XPATH_XSLT_TREE: + case XPATH_NODESET: + { + xmlNodeSetPtr nodeset = xmlXPathPopNodeSet(ctxt); + IXMLDOMNodeList *nodelist; + + hr = xpath_create_nodelist_from_set(nodeset, obj_type == XPATH_XSLT_TREE, &nodelist); + + V_VT(&args[i]) = VT_DISPATCH; + V_DISPATCH(&args[i]) = (IDispatch *)nodelist; + break; + } + case XPATH_STRING: + s = xmlXPathPopString(ctxt); + V_VT(&args[i]) = VT_BSTR; + V_BSTR(&args[i]) = bstr_from_xmlChar(s); + xmlFree(s); + break; + case XPATH_NUMBER: + V_VT(&args[i]) = VT_R8; + V_R8(&args[i]) = xmlXPathPopNumber(ctxt); + break; + case XPATH_BOOLEAN: + V_VT(&args[i]) = VT_BOOL; + V_BOOL(&args[i]) = xmlXPathPopBoolean(ctxt) ? VARIANT_TRUE : VARIANT_FALSE; + break; + default: + FIXME("Unexpected XPath (%s) value type %d.\n", xpath->function, obj->type); + return; + } + } + + params.rgvarg = args; + params.cArgs = nargs; + } + + VariantInit(&result); + hr = IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_METHOD, ¶ms, &result, NULL, NULL); + if (FAILED(hr)) + WARN("User script Invoke() failed %#lx, function %s.\n", hr, xpath->function); + + if (args) + { + for (int i = 0; i < nargs; ++i) + VariantClear(&args[i]); + free(args); + } + + switch (V_VT(&result)) + { + case VT_BSTR: + { + xmlChar *s = xmlchar_from_wchar(V_BSTR(&result)); + xmlXPathReturnString(ctxt, s); + break; + } + case VT_BOOL: + xmlXPathReturnBoolean(ctxt, !!V_BOOL(&result)); + break; + case VT_INT: + case VT_I1: + case VT_UI1: + case VT_I2: + case VT_UI2: + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_R8: + VariantChangeType(&result, &result, 0, VT_R8); + xmlXPathReturnNumber(ctxt, V_R8(&result)); + break; + case VT_EMPTY: + xmlXPathReturnEmptyString(ctxt); + break; + default: + FIXME("Unexpected return value %s.\n", debugstr_variant(&result)); + xmlXPathReturnEmptyString(ctxt); + } + + IDispatch_Release(disp); + VariantClear(&result); +} + +static HRESULT WINAPI msxsl_script_site_QueryInterface(IActiveScriptSite *iface, REFIID riid, void **obj) +{ + if (IsEqualGUID(&IID_IUnknown, riid) + || IsEqualGUID(&IID_IActiveScriptSite, riid)) + { + *obj = iface; + IActiveScriptSite_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI msxsl_script_site_AddRef(IActiveScriptSite *iface) +{ + return 2; +} + +static ULONG WINAPI msxsl_script_site_Release(IActiveScriptSite *iface) +{ + return 1; +} + +static HRESULT WINAPI msxsl_script_site_GetLCID(IActiveScriptSite *iface, LCID *lcid) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI msxsl_script_site_GetItemInfo(IActiveScriptSite *iface, LPCOLESTR name, + DWORD mask, IUnknown **item, ITypeInfo **typeinfo) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI msxsl_script_site_GetDocVersionString(IActiveScriptSite *iface, BSTR *version) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI msxsl_script_site_OnScriptTerminate(IActiveScriptSite *iface, + const VARIANT *result, const EXCEPINFO *ei) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI msxsl_script_site_OnStateChange(IActiveScriptSite *iface, SCRIPTSTATE state) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI msxsl_script_site_OnScriptError(IActiveScriptSite *iface, IActiveScriptError *script_error) +{ + return S_OK; +} + +static HRESULT WINAPI msxsl_script_site_OnEnterScript(IActiveScriptSite *iface) +{ + return S_OK; +} + +static HRESULT WINAPI msxsl_script_site_OnLeaveScript(IActiveScriptSite *iface) +{ + return S_OK; +} + +static const IActiveScriptSiteVtbl msxsl_script_site_vtbl = +{ + msxsl_script_site_QueryInterface, + msxsl_script_site_AddRef, + msxsl_script_site_Release, + msxsl_script_site_GetLCID, + msxsl_script_site_GetItemInfo, + msxsl_script_site_GetDocVersionString, + msxsl_script_site_OnScriptTerminate, + msxsl_script_site_OnStateChange, + msxsl_script_site_OnScriptError, + msxsl_script_site_OnEnterScript, + msxsl_script_site_OnLeaveScript +}; + +static IActiveScriptSite msxsl_script_site = { &msxsl_script_site_vtbl }; + +static void node_transform_bind_scripts(xsltTransformContextPtr ctxt, + xmlDocPtr sheet, struct xsl_scripts *scripts) +{ + xmlNodePtr root, child, node; + xmlNsPtr ns; + + root = xmlDocGetRootElement(sheet); + for (node = root->children; node; node = node->next) + { + if (xmlStrEqual(node->name, BAD_CAST "script") + && node->ns + && xmlStrEqual(node->ns->prefix, BAD_CAST "msxsl") + && xmlStrEqual(node->ns->href, BAD_CAST "urn:schemas-microsoft-com:xslt")) + { + child = node->children; + + if (child && child->type == XML_CDATA_SECTION_NODE) + { + IActiveScript *active_script; + xmlChar *language, *prefix; + IActiveScriptParse *parser; + TYPEATTR *typeattr; + BSTR text, progid; + IDispatch *disp; + ITypeInfo *ti; + CLSID clsid; + HRESULT hr; + + if (!(prefix = xmlGetProp(node, BAD_CAST "implements-prefix"))) + { + WARN("msxsl:script without 'implements-prefix'.\n"); + continue; + } + + if (!(ns = xmlSearchNs(sheet, node, prefix))) + { + WARN("Couldn't locate script element namespace for "%s".\n", debugstr_a((char *)prefix)); + continue; + } + + if (!(language = xmlGetProp(node, BAD_CAST "language"))) + language = BAD_CAST "javascript"; + + progid = bstr_from_xmlChar(language); + if (FAILED(hr = CLSIDFromProgID(progid, &clsid))) + WARN("Unknown engine progid %s.\n", debugstr_w(progid)); + SysFreeString(progid); + if (FAILED(hr)) + return; + + if (FAILED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, + &IID_IActiveScript, (void **)&active_script))) + { + WARN("Failed to create a script engine instance, hr %#lx.\n", hr); + continue; + } + + IActiveScript_QueryInterface(active_script, &IID_IActiveScriptParse, (void **)&parser); + IActiveScript_SetScriptSite(active_script, &msxsl_script_site); + IActiveScriptParse_InitNew(parser); + IActiveScript_SetScriptState(active_script, SCRIPTSTATE_STARTED); + + text = bstr_from_xmlChar(child->content); + hr = IActiveScriptParse_ParseScriptText(parser, text, NULL, NULL, NULL, 0, 0, 0, NULL, NULL); + IActiveScriptParse_Release(parser); + SysFreeString(text); + if (FAILED(hr)) + { + WARN("Failed to parse script for the namespace %s, hr %#lx.\n", prefix, hr); + IActiveScript_Release(active_script); + continue; + } + + IActiveScript_GetScriptDispatch(active_script, NULL, &disp); + IDispatch_GetTypeInfo(disp, 0, LOCALE_USER_DEFAULT, &ti); + + ITypeInfo_GetTypeAttr(ti, &typeattr); + + for (int i = 0; i < typeattr->cFuncs; ++i) + { + FUNCDESC *funcdesc; + xmlChar *func_name; + BSTR name; + + ITypeInfo_GetFuncDesc(ti, i, &funcdesc); + ITypeInfo_GetDocumentation(ti, funcdesc->memid, &name, NULL, NULL, NULL); + func_name = xmlchar_from_wchar(name); + + xsltRegisterExtFunction(ctxt, func_name, ns->href, xpath_msxsl_script_function); + + SysFreeString(name); + free(func_name); + } + + IDispatch_Release(disp); + ITypeInfo_Release(ti); + + array_reserve((void **)&scripts->entries, &scripts->capacity, scripts->count + 1, sizeof(*scripts->entries)); + + scripts->entries[scripts->count].script = active_script; + scripts->entries[scripts->count].uri = ns->href; + scripts->count++; + } + } + } +} + HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, BSTR *p, ISequentialStream *stream, const struct xslprocessor_params *params) { @@ -1496,7 +1889,9 @@ HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, xsltSS = xsltParseStylesheetDoc(sheet_doc); if (xsltSS) { + struct xsl_scripts scripts = { 0 }; const char **xslparams = NULL; + xsltTransformContextPtr ctxt; xmlDocPtr result; unsigned int i;
@@ -1515,21 +1910,35 @@ HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, xslparams[i] = NULL; }
+ ctxt = xsltNewTransformContext(xsltSS, This->node->doc); + + node_transform_bind_scripts(ctxt, sheet_doc, &scripts); + ctxt->userData = &scripts; + if (xslparams) { - xsltTransformContextPtr ctxt = xsltNewTransformContext(xsltSS, This->node->doc); - /* push parameters to user context */ xsltQuoteUserParams(ctxt, xslparams); result = xsltApplyStylesheetUser(xsltSS, This->node->doc, NULL, NULL, NULL, ctxt); - xsltFreeTransformContext(ctxt);
for (i = 0; i < params->count*2; i++) free((char*)xslparams[i]); free(xslparams); } else - result = xsltApplyStylesheet(xsltSS, This->node->doc, NULL); + { + result = xsltApplyStylesheetUser(xsltSS, This->node->doc, NULL, NULL, NULL, ctxt); + } + + for (size_t i = 0; i < scripts.count; ++i) + { + if (scripts.entries[i].script) + IActiveScript_Release(scripts.entries[i].script); + } + free(scripts.entries); + + ctxt->userData = NULL; + xsltFreeTransformContext(ctxt);
if (result) { diff --git a/dlls/msxml3/selection.c b/dlls/msxml3/selection.c index 21441f314ce..e752f59b347 100644 --- a/dlls/msxml3/selection.c +++ b/dlls/msxml3/selection.c @@ -169,7 +169,8 @@ static ULONG WINAPI domselection_Release( if ( ref == 0 ) { xmlXPathFreeObject(This->result); - xmldoc_release(This->node->doc); + if (This->node) + xmldoc_release(This->node->doc); if (This->enumvariant) IEnumVARIANT_Release(This->enumvariant); free(This); } @@ -833,3 +834,25 @@ cleanup: xmlXPathFreeContext(ctxt); return hr; } + +HRESULT create_selection_from_nodeset(xmlXPathObjectPtr nodeset, IXMLDOMNodeList **out) +{ + domselection *obj; + + TRACE("%p, %p.\n", nodeset, out); + + *out = NULL; + + if (!(obj = calloc(1, sizeof(*obj)))) + return E_OUTOFMEMORY; + + obj->IXMLDOMSelection_iface.lpVtbl = &domselection_vtbl; + obj->ref = 1; + init_dispex(&obj->dispex, (IUnknown *)&obj->IXMLDOMSelection_iface, &domselection_dispex); + + obj->result = nodeset; + + *out = (IXMLDOMNodeList *)&obj->IXMLDOMSelection_iface; + + return S_OK; +} diff --git a/dlls/msxml3/xmldoc.c b/dlls/msxml3/xmldoc.c index 93674bf06e8..125d041c8f8 100644 --- a/dlls/msxml3/xmldoc.c +++ b/dlls/msxml3/xmldoc.c @@ -22,7 +22,6 @@ #define COBJMACROS
#include <stdarg.h> -#include <stdbool.h> #include <libxml/parser.h>
#include "windef.h" @@ -300,32 +299,6 @@ struct parse_context } dtd; };
-static bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) -{ - size_t new_capacity, max_capacity; - void *new_elements; - - if (count <= *capacity) - return true; - - max_capacity = ~(SIZE_T)0 / size; - if (count > max_capacity) - return false; - - new_capacity = max(4, *capacity); - while (new_capacity < count && new_capacity <= max_capacity / 2) - new_capacity *= 2; - if (new_capacity < count) - new_capacity = max_capacity; - - if (!(new_elements = realloc(*elements, new_capacity * size))) - return false; - - *elements = new_elements; - *capacity = new_capacity; - return true; -} - static HRESULT text_buffer_append(struct buffer *buffer, const WCHAR *chars, int count) { if (!count) diff --git a/libs/xslt/libxslt/xsltInternals.h b/libs/xslt/libxslt/xsltInternals.h index be50678c4b6..a187802558f 100644 --- a/libs/xslt/libxslt/xsltInternals.h +++ b/libs/xslt/libxslt/xsltInternals.h @@ -1799,6 +1799,9 @@ struct _xsltTransformContext { xsltNewLocaleFunc newLocale; xsltFreeLocaleFunc freeLocale; xsltGenSortKeyFunc genSortKey; + + /* Wine extension */ + void *userData; };
/**