mshtml: Use custom get_readyState implementations for object and frame elements.
[wine] / dlls / msxml3 / schema.c
1 /*
2  * Schema cache implementation
3  *
4  * Copyright 2007 Huw Davies
5  * Copyright 2010 Adam Martinson for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <stdarg.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "ole2.h"
32 #include "msxml6.h"
33
34 #include "wine/debug.h"
35
36 #include "msxml_private.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
39
40 /* We use a chained hashtable, which can hold any number of schemas
41  * TODO: versioned constructor
42  * TODO: grow/shrink hashtable depending on load factor
43  * TODO: implement read-only where appropriate
44  */
45
46 /* This is just the number of buckets, should be prime */
47 #define DEFAULT_HASHTABLE_SIZE 17
48
49 #ifdef HAVE_LIBXML2
50
51 #include <libxml/tree.h>
52 #include <libxml/xmlschemas.h>
53 #include <libxml/schemasInternals.h>
54 #include <libxml/hash.h>
55 #include <libxml/parser.h>
56 #include <libxml/parserInternals.h>
57 #include <libxml/xmlIO.h>
58
59 xmlDocPtr XDR_to_XSD_doc(xmlDocPtr xdr_doc, xmlChar const* nsURI);
60
61 static const xmlChar XSD_schema[] = "schema";
62 static const xmlChar XSD_nsURI[] = "http://www.w3.org/2001/XMLSchema";
63 static const xmlChar XDR_schema[] = "Schema";
64 static const xmlChar XDR_nsURI[] = "urn:schemas-microsoft-com:xml-data";
65 static const xmlChar DT_nsURI[] = "urn:schemas-microsoft-com:datatypes";
66
67 static xmlChar const* datatypes_schema = NULL;
68 static HGLOBAL datatypes_handle = NULL;
69 static HRSRC datatypes_rsrc = NULL;
70
71 /* Supported Types:
72  * msxml3 - XDR only
73  * msxml4 - XDR & XSD
74  * msxml5 - XDR & XSD
75  * mxsml6 - XSD only
76  */
77 typedef enum _SCHEMA_TYPE {
78     SCHEMA_TYPE_INVALID,
79     SCHEMA_TYPE_XDR,
80     SCHEMA_TYPE_XSD
81 } SCHEMA_TYPE;
82
83 typedef struct _schema_cache
84 {
85     const struct IXMLDOMSchemaCollection2Vtbl* lpVtbl;
86     xmlHashTablePtr cache;
87     LONG ref;
88 } schema_cache;
89
90 typedef struct _cache_entry
91 {
92     SCHEMA_TYPE type;
93     xmlSchemaPtr schema;
94     xmlDocPtr doc;
95     LONG ref;
96 } cache_entry;
97
98 typedef struct _cache_index_data
99 {
100     LONG index;
101     BSTR* out;
102 } cache_index_data;
103
104 xmlExternalEntityLoader _external_entity_loader = NULL;
105
106 static xmlParserInputPtr external_entity_loader(const char *URL, const char *ID,
107                                                 xmlParserCtxtPtr ctxt)
108 {
109     xmlParserInputPtr input;
110
111     TRACE("(%s, %s, %p)\n", wine_dbgstr_a(URL), wine_dbgstr_a(ID), ctxt);
112
113     assert(MSXML_hInstance != NULL);
114     assert(datatypes_rsrc != NULL);
115     assert(datatypes_handle != NULL);
116     assert(datatypes_schema != NULL);
117
118     /* TODO: if the desired schema is in the cache, load it from there */
119     if (lstrcmpA(URL, "urn:schemas-microsoft-com:datatypes") == 0)
120     {
121         TRACE("loading built-in schema for %s\n", URL);
122         input = xmlNewStringInputStream(ctxt, datatypes_schema);
123     }
124     else
125     {
126         input = _external_entity_loader(URL, ID, ctxt);
127     }
128
129     return input;
130 }
131
132 void schemasInit(void)
133 {
134     int len;
135     char* buf;
136     if (!(datatypes_rsrc = FindResourceA(MSXML_hInstance, "DATATYPES", "XML")))
137     {
138         FIXME("failed to find resource for %s\n", DT_nsURI);
139         return;
140     }
141
142     if (!(datatypes_handle = LoadResource(MSXML_hInstance, datatypes_rsrc)))
143     {
144         FIXME("failed to load resource for %s\n", DT_nsURI);
145         return;
146     }
147     buf = LockResource(datatypes_handle);
148     len = SizeofResource(MSXML_hInstance, datatypes_rsrc) - 1;
149
150     /* Resource is loaded as raw data,
151      * need a null-terminated string */
152     while (buf[len] != '>')
153         buf[len--] = 0;
154     datatypes_schema = BAD_CAST buf;
155
156     if ((void*)xmlGetExternalEntityLoader() != (void*)external_entity_loader)
157     {
158         _external_entity_loader = xmlGetExternalEntityLoader();
159         xmlSetExternalEntityLoader(external_entity_loader);
160     }
161 }
162
163 void schemasCleanup(void)
164 {
165     if (datatypes_handle)
166         FreeResource(datatypes_handle);
167     xmlSetExternalEntityLoader(_external_entity_loader);
168 }
169
170 static LONG cache_entry_add_ref(cache_entry* entry)
171 {
172     LONG ref = InterlockedIncrement(&entry->ref);
173     TRACE("%p new ref %d\n", entry, ref);
174     return ref;
175 }
176
177 static LONG cache_entry_release(cache_entry* entry)
178 {
179     LONG ref = InterlockedDecrement(&entry->ref);
180     TRACE("%p new ref %d\n", entry, ref);
181
182     if (ref == 0)
183     {
184         if (entry->type == SCHEMA_TYPE_XSD)
185         {
186             xmldoc_release(entry->doc);
187             entry->schema->doc = NULL;
188             xmlSchemaFree(entry->schema);
189             heap_free(entry);
190         }
191         else /* SCHEMA_TYPE_XDR */
192         {
193             xmldoc_release(entry->doc);
194             xmldoc_release(entry->schema->doc);
195             entry->schema->doc = NULL;
196             xmlSchemaFree(entry->schema);
197             heap_free(entry);
198         }
199     }
200     return ref;
201 }
202
203 static inline schema_cache* impl_from_IXMLDOMSchemaCollection2(IXMLDOMSchemaCollection2* iface)
204 {
205     return (schema_cache*)((char*)iface - FIELD_OFFSET(schema_cache, lpVtbl));
206 }
207
208 static inline SCHEMA_TYPE schema_type_from_xmlDocPtr(xmlDocPtr schema)
209 {
210     xmlNodePtr root;
211     if (schema)
212         root = xmlDocGetRootElement(schema);
213     if (root && root->ns)
214     {
215
216         if (xmlStrEqual(root->name, XDR_schema) &&
217             xmlStrEqual(root->ns->href, XDR_nsURI))
218         {
219             return SCHEMA_TYPE_XDR;
220         }
221         else if (xmlStrEqual(root->name, XSD_schema) &&
222                  xmlStrEqual(root->ns->href, XSD_nsURI))
223         {
224             return SCHEMA_TYPE_XSD;
225         }
226     }
227     return SCHEMA_TYPE_INVALID;
228 }
229
230 static BOOL link_datatypes(xmlDocPtr schema)
231 {
232     xmlNodePtr root, next, child;
233     xmlNsPtr ns;
234
235     assert((void*)xmlGetExternalEntityLoader() == (void*)external_entity_loader);
236     root = xmlDocGetRootElement(schema);
237     if (!root)
238         return FALSE;
239
240     for (ns = root->nsDef; ns != NULL; ns = ns->next)
241     {
242         if (xmlStrEqual(ns->href, DT_nsURI))
243             break;
244     }
245
246     if (!ns)
247         return FALSE;
248
249     next = xmlFirstElementChild(root);
250     child = xmlNewChild(root, NULL, BAD_CAST "import", NULL);
251     if (next) child = xmlAddPrevSibling(next, child);
252     xmlSetProp(child, BAD_CAST "namespace", DT_nsURI);
253     xmlSetProp(child, BAD_CAST "schemaLocation", DT_nsURI);
254
255     return TRUE;
256 }
257
258 static cache_entry* cache_entry_from_url(char const* url, xmlChar const* nsURI)
259 {
260     cache_entry* entry = heap_alloc(sizeof(cache_entry));
261     xmlSchemaParserCtxtPtr spctx = xmlSchemaNewParserCtxt(url);
262     entry->type = SCHEMA_TYPE_XSD;
263     entry->ref = 0;
264     if (spctx)
265     {
266         if((entry->schema = xmlSchemaParse(spctx)))
267         {
268             /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
269              *       do we need to do something special here? */
270             xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
271             entry->doc = entry->schema->doc;
272             xmldoc_add_ref(entry->doc);
273         }
274         else
275         {
276             heap_free(entry);
277             entry = NULL;
278         }
279         xmlSchemaFreeParserCtxt(spctx);
280     }
281     else
282     {
283         FIXME("schema for nsURI %s not found\n", wine_dbgstr_a(url));
284         heap_free(entry);
285         entry = NULL;
286     }
287     return entry;
288 }
289
290 static cache_entry* cache_entry_from_xsd_doc(xmlDocPtr doc, xmlChar const* nsURI)
291 {
292     cache_entry* entry = heap_alloc(sizeof(cache_entry));
293     xmlSchemaParserCtxtPtr spctx;
294     xmlDocPtr new_doc = xmlCopyDoc(doc, 1);
295
296     link_datatypes(new_doc);
297
298     /* TODO: if the nsURI is different from the default xmlns or targetNamespace,
299      *       do we need to do something special here? */
300     entry->type = SCHEMA_TYPE_XSD;
301     entry->ref = 0;
302     spctx = xmlSchemaNewDocParserCtxt(new_doc);
303
304     if ((entry->schema = xmlSchemaParse(spctx)))
305     {
306         xmldoc_init(entry->schema->doc, &CLSID_DOMDocument40);
307         entry->doc = entry->schema->doc;
308         xmldoc_add_ref(entry->doc);
309     }
310     else
311     {
312         FIXME("failed to parse doc\n");
313         xmlFreeDoc(new_doc);
314         heap_free(entry);
315         entry = NULL;
316     }
317     xmlSchemaFreeParserCtxt(spctx);
318     return entry;
319 }
320
321 static cache_entry* cache_entry_from_xdr_doc(xmlDocPtr doc, xmlChar const* nsURI)
322 {
323     cache_entry* entry = heap_alloc(sizeof(cache_entry));
324     xmlSchemaParserCtxtPtr spctx;
325     xmlDocPtr new_doc = xmlCopyDoc(doc, 1), xsd_doc = XDR_to_XSD_doc(doc, nsURI);
326
327     link_datatypes(xsd_doc);
328
329     entry->type = SCHEMA_TYPE_XDR;
330     entry->ref = 0;
331     spctx = xmlSchemaNewDocParserCtxt(xsd_doc);
332
333     if ((entry->schema = xmlSchemaParse(spctx)))
334     {
335         entry->doc = new_doc;
336         xmldoc_init(entry->schema->doc, &CLSID_DOMDocument30);
337         xmldoc_init(entry->doc, &CLSID_DOMDocument30);
338         xmldoc_add_ref(entry->doc);
339         xmldoc_add_ref(entry->schema->doc);
340     }
341     else
342     {
343         FIXME("failed to parse doc\n");
344         xmlFreeDoc(new_doc);
345         xmlFreeDoc(xsd_doc);
346         heap_free(entry);
347         entry = NULL;
348     }
349     xmlSchemaFreeParserCtxt(spctx);
350
351     return entry;
352 }
353
354 static HRESULT WINAPI schema_cache_QueryInterface(IXMLDOMSchemaCollection2* iface,
355                                                   REFIID riid, void** ppvObject)
356 {
357     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
358
359     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
360
361     if ( IsEqualIID(riid, &IID_IUnknown) ||
362          IsEqualIID(riid, &IID_IDispatch) ||
363          IsEqualIID(riid, &IID_IXMLDOMSchemaCollection) ||
364          IsEqualIID(riid, &IID_IXMLDOMSchemaCollection2) )
365     {
366         *ppvObject = iface;
367     }
368     else
369     {
370         FIXME("interface %s not implemented\n", debugstr_guid(riid));
371         return E_NOINTERFACE;
372     }
373
374     IXMLDOMSchemaCollection2_AddRef(iface);
375
376     return S_OK;
377 }
378
379 static ULONG WINAPI schema_cache_AddRef(IXMLDOMSchemaCollection2* iface)
380 {
381     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
382     LONG ref = InterlockedIncrement(&This->ref);
383     TRACE("%p new ref %d\n", This, ref);
384     return ref;
385 }
386
387 static void cache_free(void* data, xmlChar* name /* ignored */)
388 {
389     cache_entry_release((cache_entry*)data);
390 }
391
392 static ULONG WINAPI schema_cache_Release(IXMLDOMSchemaCollection2* iface)
393 {
394     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
395     LONG ref = InterlockedDecrement(&This->ref);
396     TRACE("%p new ref %d\n", This, ref);
397
398     if (ref == 0)
399     {
400         xmlHashFree(This->cache, cache_free);
401         heap_free(This);
402     }
403
404     return ref;
405 }
406
407 static HRESULT WINAPI schema_cache_GetTypeInfoCount(IXMLDOMSchemaCollection2* iface,
408                                                     UINT* pctinfo)
409 {
410     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
411
412     TRACE("(%p)->(%p)\n", This, pctinfo);
413
414     *pctinfo = 1;
415
416     return S_OK;
417 }
418
419 static HRESULT WINAPI schema_cache_GetTypeInfo(IXMLDOMSchemaCollection2* iface,
420                                                UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
421 {
422     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
423     HRESULT hr;
424
425     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
426
427     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, ppTInfo);
428
429     return hr;
430 }
431
432 static HRESULT WINAPI schema_cache_GetIDsOfNames(IXMLDOMSchemaCollection2* iface,
433                                                  REFIID riid, LPOLESTR* rgszNames,
434                                                  UINT cNames, LCID lcid, DISPID* rgDispId)
435 {
436     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
437     ITypeInfo* typeinfo;
438     HRESULT hr;
439
440     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
441           lcid, rgDispId);
442
443     if(!rgszNames || cNames == 0 || !rgDispId)
444         return E_INVALIDARG;
445
446     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
447     if(SUCCEEDED(hr))
448     {
449         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
450         ITypeInfo_Release(typeinfo);
451     }
452
453     return hr;
454 }
455
456 static HRESULT WINAPI schema_cache_Invoke(IXMLDOMSchemaCollection2* iface,
457                                           DISPID dispIdMember, REFIID riid, LCID lcid,
458                                           WORD wFlags, DISPPARAMS* pDispParams,
459                                           VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
460                                           UINT* puArgErr)
461 {
462     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
463     ITypeInfo* typeinfo;
464     HRESULT hr;
465
466     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
467           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
468
469     hr = get_typeinfo(IXMLDOMSchemaCollection_tid, &typeinfo);
470     if(SUCCEEDED(hr))
471     {
472         hr = ITypeInfo_Invoke(typeinfo, &(This->lpVtbl), dispIdMember, wFlags, pDispParams,
473                 pVarResult, pExcepInfo, puArgErr);
474         ITypeInfo_Release(typeinfo);
475     }
476
477     return hr;
478 }
479
480 static HRESULT WINAPI schema_cache_add(IXMLDOMSchemaCollection2* iface, BSTR uri, VARIANT var)
481 {
482     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
483     xmlChar* name = xmlChar_from_wchar(uri);
484     TRACE("(%p)->(%s, var(vt %x))\n", This, debugstr_w(uri), V_VT(&var));
485
486     switch (V_VT(&var))
487     {
488         case VT_NULL:
489             {
490                 xmlHashRemoveEntry(This->cache, name, cache_free);
491             }
492             break;
493
494         case VT_BSTR:
495             {
496                 xmlChar* url = xmlChar_from_wchar(V_BSTR(&var));
497                 cache_entry* entry = cache_entry_from_url((char const*)url, name);
498                 heap_free(url);
499
500                 if (entry)
501                 {
502                     cache_entry_add_ref(entry);
503                 }
504                 else
505                 {
506                     heap_free(name);
507                     return E_FAIL;
508                 }
509
510                 xmlHashRemoveEntry(This->cache, name, cache_free);
511                 xmlHashAddEntry(This->cache, name, entry);
512             }
513             break;
514
515         case VT_DISPATCH:
516             {
517                 xmlDocPtr doc = NULL;
518                 cache_entry* entry;
519                 SCHEMA_TYPE type;
520                 IXMLDOMNode* domnode = NULL;
521                 IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IXMLDOMNode, (void**)&domnode);
522
523                 if (domnode)
524                     doc = xmlNodePtr_from_domnode(domnode, XML_DOCUMENT_NODE)->doc;
525
526                 if (!doc)
527                 {
528                     IXMLDOMNode_Release(domnode);
529                     heap_free(name);
530                     return E_INVALIDARG;
531                 }
532                 type = schema_type_from_xmlDocPtr(doc);
533
534                 if (type == SCHEMA_TYPE_XSD)
535                 {
536                     entry = cache_entry_from_xsd_doc(doc, name);
537                 }
538                 else if (type == SCHEMA_TYPE_XDR)
539                 {
540                     entry = cache_entry_from_xdr_doc(doc, name);
541                 }
542                 else
543                 {
544                     WARN("invalid schema!\n");
545                     entry = NULL;
546                 }
547
548                 IXMLDOMNode_Release(domnode);
549
550                 if (entry)
551                 {
552                     cache_entry_add_ref(entry);
553                 }
554                 else
555                 {
556                     heap_free(name);
557                     return E_FAIL;
558                 }
559
560                 xmlHashRemoveEntry(This->cache, name, cache_free);
561                 xmlHashAddEntry(This->cache, name, entry);
562             }
563             break;
564
565         default:
566             {
567                 heap_free(name);
568                 return E_INVALIDARG;
569             }
570     }
571     heap_free(name);
572     return S_OK;
573 }
574
575 static HRESULT WINAPI schema_cache_get(IXMLDOMSchemaCollection2* iface, BSTR uri,
576                                        IXMLDOMNode** node)
577 {
578     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
579     xmlChar* name;
580     cache_entry* entry;
581     TRACE("(%p)->(%s, %p)\n", This, wine_dbgstr_w(uri), node);
582
583     if (!node)
584         return E_POINTER;
585
586     name = xmlChar_from_wchar(uri);
587     entry = (cache_entry*) xmlHashLookup(This->cache, name);
588     heap_free(name);
589
590     /* TODO: this should be read-only */
591     if (entry)
592         return DOMDocument_create_from_xmldoc(entry->doc, (IXMLDOMDocument3**)node);
593
594     *node = NULL;
595     return S_OK;
596 }
597
598 static HRESULT WINAPI schema_cache_remove(IXMLDOMSchemaCollection2* iface, BSTR uri)
599 {
600     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
601     xmlChar* name = xmlChar_from_wchar(uri);
602     TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(uri));
603
604     xmlHashRemoveEntry(This->cache, name, cache_free);
605     heap_free(name);
606     return S_OK;
607 }
608
609 static HRESULT WINAPI schema_cache_get_length(IXMLDOMSchemaCollection2* iface, LONG* length)
610 {
611     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
612     TRACE("(%p)->(%p)\n", This, length);
613
614     if (!length)
615         return E_POINTER;
616     *length = xmlHashSize(This->cache);
617     return S_OK;
618 }
619
620 static void cache_index(void* data /* ignored */, void* index, xmlChar* name)
621 {
622     cache_index_data* index_data = (cache_index_data*)index;
623
624     if (index_data->index-- == 0)
625         *index_data->out = bstr_from_xmlChar(name);
626 }
627
628 static HRESULT WINAPI schema_cache_get_namespaceURI(IXMLDOMSchemaCollection2* iface,
629                                                     LONG index, BSTR* len)
630 {
631     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
632     cache_index_data data = {index,len};
633     TRACE("(%p)->(%i, %p)\n", This, index, len);
634
635     if (!len)
636         return E_POINTER;
637     *len = NULL;
638
639     if (index >= xmlHashSize(This->cache))
640         return E_FAIL;
641
642     xmlHashScan(This->cache, cache_index, &data);
643     return S_OK;
644 }
645
646 static void cache_copy(void* data, void* dest, xmlChar* name)
647 {
648     schema_cache* This = (schema_cache*) dest;
649     cache_entry* entry = (cache_entry*) data;
650
651     if (xmlHashLookup(This->cache, name) == NULL)
652     {
653         cache_entry_add_ref(entry);
654         xmlHashAddEntry(This->cache, name, entry);
655     }
656 }
657
658 static HRESULT WINAPI schema_cache_addCollection(IXMLDOMSchemaCollection2* iface,
659                                                  IXMLDOMSchemaCollection* otherCollection)
660 {
661     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
662     schema_cache* That = impl_from_IXMLDOMSchemaCollection2((IXMLDOMSchemaCollection2*)otherCollection);
663     TRACE("(%p)->(%p)\n", This, That);
664
665     if (!otherCollection)
666         return E_POINTER;
667
668     /* TODO: detect errors while copying & return E_FAIL */
669     xmlHashScan(That->cache, cache_copy, This);
670
671     return S_OK;
672 }
673
674 static HRESULT WINAPI schema_cache_get__newEnum(IXMLDOMSchemaCollection2* iface,
675                                                 IUnknown** ppUnk)
676 {
677     FIXME("stub\n");
678     if (ppUnk)
679         *ppUnk = NULL;
680     return E_NOTIMPL;
681 }
682
683 static HRESULT WINAPI schema_cache_validate(IXMLDOMSchemaCollection2* iface)
684 {
685     FIXME("stub\n");
686     return E_NOTIMPL;
687 }
688
689 static HRESULT WINAPI schema_cache_put_validateOnLoad(IXMLDOMSchemaCollection2* iface,
690                                                       VARIANT_BOOL validateOnLoad)
691 {
692     FIXME("stub\n");
693     return E_NOTIMPL;
694 }
695
696 static HRESULT WINAPI schema_cache_get_validateOnLoad(IXMLDOMSchemaCollection2* iface,
697                                                       VARIANT_BOOL* validateOnLoad)
698 {
699     FIXME("stub\n");
700     return E_NOTIMPL;
701 }
702
703 static HRESULT WINAPI schema_cache_getSchema(IXMLDOMSchemaCollection2* iface,
704                                              BSTR namespaceURI, ISchema** schema)
705 {
706     FIXME("stub\n");
707     if (schema)
708         *schema = NULL;
709     return E_NOTIMPL;
710 }
711
712 static HRESULT WINAPI schema_cache_getDeclaration(IXMLDOMSchemaCollection2* iface,
713                                                   IXMLDOMNode* node, ISchemaItem** item)
714 {
715     FIXME("stub\n");
716     if (item)
717         *item = NULL;
718     return E_NOTIMPL;
719 }
720
721 static const struct IXMLDOMSchemaCollection2Vtbl schema_cache_vtbl =
722 {
723     schema_cache_QueryInterface,
724     schema_cache_AddRef,
725     schema_cache_Release,
726     schema_cache_GetTypeInfoCount,
727     schema_cache_GetTypeInfo,
728     schema_cache_GetIDsOfNames,
729     schema_cache_Invoke,
730     schema_cache_add,
731     schema_cache_get,
732     schema_cache_remove,
733     schema_cache_get_length,
734     schema_cache_get_namespaceURI,
735     schema_cache_addCollection,
736     schema_cache_get__newEnum,
737     schema_cache_validate,
738     schema_cache_put_validateOnLoad,
739     schema_cache_get_validateOnLoad,
740     schema_cache_getSchema,
741     schema_cache_getDeclaration
742 };
743
744 static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
745 {
746     va_list ap;
747     va_start(ap, msg);
748     LIBXML2_CALLBACK_ERR(SchemaCache_validate_tree, msg, ap);
749     va_end(ap);
750 }
751
752 static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
753 {
754     va_list ap;
755     va_start(ap, msg);
756     LIBXML2_CALLBACK_WARN(SchemaCache_validate_tree, msg, ap);
757     va_end(ap);
758 }
759
760 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
761 static void validate_serror(void* ctx, xmlErrorPtr err)
762 {
763     LIBXML2_CALLBACK_SERROR(SchemaCache_validate_tree, err);
764 }
765 #endif
766
767 HRESULT SchemaCache_validate_tree(IXMLDOMSchemaCollection2* iface, xmlNodePtr tree)
768 {
769     schema_cache* This = impl_from_IXMLDOMSchemaCollection2(iface);
770     cache_entry* entry;
771     xmlChar const* ns = NULL;
772     TRACE("(%p, %p)\n", This, tree);
773
774     if (!tree)
775         return E_POINTER;
776
777     if ((xmlNodePtr)tree->doc == tree)
778     {
779         xmlNodePtr root = xmlDocGetRootElement(tree->doc);
780         if (root && root->ns)
781             ns = root->ns->href;
782     }
783     else if (tree->ns)
784     {
785         ns = tree->ns->href;
786     }
787
788     entry = (ns != NULL)? xmlHashLookup(This->cache, ns) :
789                           xmlHashLookup(This->cache, BAD_CAST "");
790     /* TODO: if the ns is not in the cache, and it's a URL,
791      *       do we try to load from that? */
792     if (entry)
793     {
794         xmlSchemaValidCtxtPtr svctx;
795         int err;
796         /* TODO: if validateOnLoad property is false,
797          *       we probably need to validate the schema here. */
798         svctx = xmlSchemaNewValidCtxt(entry->schema);
799         xmlSchemaSetValidErrors(svctx, validate_error, validate_warning, NULL);
800 #ifdef HAVE_XMLSCHEMASSETVALIDSTRUCTUREDERRORS
801             xmlSchemaSetValidStructuredErrors(svctx, validate_serror, NULL);
802 #endif
803
804         if ((xmlNodePtr)tree->doc == tree)
805             err = xmlSchemaValidateDoc(svctx, (xmlDocPtr)tree);
806         else
807             err = xmlSchemaValidateOneElement(svctx, tree);
808
809         xmlSchemaFreeValidCtxt(svctx);
810         return err? S_FALSE : S_OK;
811     }
812
813     return E_FAIL;
814 }
815
816 HRESULT SchemaCache_create(IUnknown* pUnkOuter, void** ppObj)
817 {
818     schema_cache* This = heap_alloc(sizeof(schema_cache));
819     if (!This)
820         return E_OUTOFMEMORY;
821
822     This->lpVtbl = &schema_cache_vtbl;
823     This->cache = xmlHashCreate(DEFAULT_HASHTABLE_SIZE);
824     This->ref = 1;
825
826     *ppObj = &This->lpVtbl;
827     return S_OK;
828 }
829
830 #else
831
832 HRESULT SchemaCache_create(IUnknown* pUnkOuter, void** ppObj)
833 {
834     MESSAGE("This program tried to use a SchemaCache object, but\n"
835             "libxml2 support was not present at compile time.\n");
836     return E_NOTIMPL;
837 }
838
839 #endif