1 /*
2  * SAXReader/MXWriter tests
3  *
4  * Copyright 2008 Piotr Caban
5  * Copyright 2011 Thomas Mullaly
6  * Copyright 2012 Nikolay Sivov
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #define COBJMACROS
24 #define CONST_VTABLE
25 
26 #include <stdio.h>
27 #include <assert.h>
28 
29 #include "windows.h"
30 #include "ole2.h"
31 #include "msxml2.h"
32 #include "msxml2did.h"
33 #include "ocidl.h"
34 #include "dispex.h"
35 
36 #include "wine/heap.h"
37 #include "wine/test.h"
38 
39 static const WCHAR emptyW[] = {0};
40 
41 #define EXPECT_HR(hr,hr_exp) \
42     ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
43 
44 #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__)
45 static void _expect_ref(IUnknown* obj, ULONG ref, int line)
46 {
47     ULONG rc;
48     IUnknown_AddRef(obj);
49     rc = IUnknown_Release(obj);
50     ok_(__FILE__, line)(rc == ref, "expected refcount %d, got %d\n", ref, rc);
51 }
52 
53 static LONG get_refcount(void *iface)
54 {
55     IUnknown *unk = iface;
56     LONG ref;
57 
58     ref = IUnknown_AddRef(unk);
59     IUnknown_Release(unk);
60     return ref-1;
61 }
62 
63 struct msxmlsupported_data_t
64 {
65     const GUID *clsid;
66     const char *name;
67     BOOL supported;
68 };
69 
70 static BOOL is_clsid_supported(const GUID *clsid, const struct msxmlsupported_data_t *table)
71 {
72     while (table->clsid)
73     {
74         if (table->clsid == clsid) return table->supported;
75         table++;
76     }
77     return FALSE;
78 }
79 
80 static BSTR alloc_str_from_narrow(const char *str)
81 {
82     int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
83     BSTR ret = SysAllocStringLen(NULL, len - 1);  /* NUL character added automatically */
84     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
85     return ret;
86 }
87 
88 static BSTR alloced_bstrs[512];
89 static int alloced_bstrs_count;
90 
91 static BSTR _bstr_(const char *str)
92 {
93     assert(alloced_bstrs_count < ARRAY_SIZE(alloced_bstrs));
94     alloced_bstrs[alloced_bstrs_count] = alloc_str_from_narrow(str);
95     return alloced_bstrs[alloced_bstrs_count++];
96 }
97 
98 static void free_bstrs(void)
99 {
100     int i;
101     for (i = 0; i < alloced_bstrs_count; i++)
102         SysFreeString(alloced_bstrs[i]);
103     alloced_bstrs_count = 0;
104 }
105 
106 static void test_saxstr(const char *file, unsigned line, BSTR str, const char *expected, BOOL todo, int *failcount)
107 {
108     int len, lenexp, cmp;
109     WCHAR buf[1024];
110 
111     len = SysStringLen(str);
112 
113     if (!expected) {
114         if (str && todo)
115         {
116             (*failcount)++;
117             todo_wine
118             ok_(file, line) (!str, "got %p, expected null str\n", str);
119         }
120         else
121             ok_(file, line) (!str, "got %p, expected null str\n", str);
122 
123         if (len && todo)
124         {
125             (*failcount)++;
126             todo_wine
127             ok_(file, line) (len == 0, "got len %d, expected 0\n", len);
128         }
129         else
130             ok_(file, line) (len == 0, "got len %d, expected 0\n", len);
131         return;
132     }
133 
134     lenexp = strlen(expected);
135     if (lenexp != len && todo)
136     {
137         (*failcount)++;
138         todo_wine
139         ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected);
140     }
141     else
142         ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected);
143 
144     /* exit earlier on length mismatch */
145     if (lenexp != len) return;
146 
147     MultiByteToWideChar(CP_ACP, 0, expected, -1, buf, ARRAY_SIZE(buf));
148 
149     cmp = memcmp(str, buf, lenexp*sizeof(WCHAR));
150     if (cmp && todo)
151     {
152         (*failcount)++;
153         todo_wine
154         ok_(file, line) (!cmp, "unexpected str %s, expected %s\n",
155                          wine_dbgstr_wn(str, len), expected);
156     }
157     else
158         ok_(file, line) (!cmp, "unexpected str %s, expected %s\n",
159                              wine_dbgstr_wn(str, len), expected);
160 }
161 
162 typedef enum _CH {
163     CH_ENDTEST,
164     CH_PUTDOCUMENTLOCATOR,
165     CH_STARTDOCUMENT,
166     CH_ENDDOCUMENT,
167     CH_STARTPREFIXMAPPING,
168     CH_ENDPREFIXMAPPING,
169     CH_STARTELEMENT,
170     CH_ENDELEMENT,
171     CH_CHARACTERS,
172     CH_IGNORABLEWHITESPACE,
173     CH_PROCESSINGINSTRUCTION,
174     CH_SKIPPEDENTITY,
175     LH_STARTCDATA,
176     LH_ENDCDATA,
177     EH_ERROR,
178     EH_FATALERROR,
179     EH_IGNORABLEWARNING,
180     EVENT_LAST
181 } CH;
182 
183 static const char *event_names[EVENT_LAST] = {
184     "endtest",
185     "putDocumentLocator",
186     "startDocument",
187     "endDocument",
188     "startPrefixMapping",
189     "endPrefixMapping",
190     "startElement",
191     "endElement",
192     "characters",
193     "ignorableWhitespace",
194     "processingInstruction",
195     "skippedEntity",
196     "startCDATA",
197     "endCDATA",
198     "error",
199     "fatalError",
200     "ignorableWarning"
201 };
202 
203 struct attribute_entry {
204     const char *uri;
205     const char *local;
206     const char *qname;
207     const char *value;
208 
209     /* used for actual call data only, null for expected call data */
210     BSTR uriW;
211     BSTR localW;
212     BSTR qnameW;
213     BSTR valueW;
214 };
215 
216 struct call_entry {
217     CH id;
218     int line;
219     int column;
220     HRESULT ret;
221     const char *arg1;
222     const char *arg2;
223     const char *arg3;
224 
225     /* allocated once at startElement callback */
226     struct attribute_entry *attributes;
227     int attr_count;
228 
229     /* used for actual call data only, null for expected call data */
230     BSTR arg1W;
231     BSTR arg2W;
232     BSTR arg3W;
233 };
234 
235 struct call_sequence
236 {
237     int count;
238     int size;
239     struct call_entry *sequence;
240 };
241 
242 #define CONTENT_HANDLER_INDEX 0
243 #define NUM_CALL_SEQUENCES    1
244 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
245 
246 static void init_call_entry(ISAXLocator *locator, struct call_entry *call)
247 {
248     memset(call, 0, sizeof(*call));
249     ISAXLocator_getLineNumber(locator, &call->line);
250     ISAXLocator_getColumnNumber(locator, &call->column);
251 }
252 
253 static void add_call(struct call_sequence **seq, int sequence_index,
254     const struct call_entry *call)
255 {
256     struct call_sequence *call_seq = seq[sequence_index];
257 
258     if (!call_seq->sequence)
259     {
260         call_seq->size = 10;
261         call_seq->sequence = heap_alloc(call_seq->size * sizeof (struct call_entry));
262     }
263 
264     if (call_seq->count == call_seq->size)
265     {
266         call_seq->size *= 2;
267         call_seq->sequence = heap_realloc(call_seq->sequence, call_seq->size * sizeof (struct call_entry));
268     }
269 
270     assert(call_seq->sequence);
271 
272     call_seq->sequence[call_seq->count].id     = call->id;
273     call_seq->sequence[call_seq->count].line   = call->line;
274     call_seq->sequence[call_seq->count].column = call->column;
275     call_seq->sequence[call_seq->count].arg1W  = call->arg1W;
276     call_seq->sequence[call_seq->count].arg2W  = call->arg2W;
277     call_seq->sequence[call_seq->count].arg3W  = call->arg3W;
278     call_seq->sequence[call_seq->count].ret    = call->ret;
279     call_seq->sequence[call_seq->count].attr_count = call->attr_count;
280     call_seq->sequence[call_seq->count].attributes = call->attributes;
281 
282     call_seq->count++;
283 }
284 
285 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
286 {
287     int i;
288 
289     struct call_sequence *call_seq = seg[sequence_index];
290 
291     for (i = 0; i < call_seq->count; i++)
292     {
293         int j;
294 
295         for (j = 0; j < call_seq->sequence[i].attr_count; j++)
296         {
297             SysFreeString(call_seq->sequence[i].attributes[j].uriW);
298             SysFreeString(call_seq->sequence[i].attributes[j].localW);
299             SysFreeString(call_seq->sequence[i].attributes[j].qnameW);
300             SysFreeString(call_seq->sequence[i].attributes[j].valueW);
301         }
302         heap_free(call_seq->sequence[i].attributes);
303         call_seq->sequence[i].attr_count = 0;
304 
305         SysFreeString(call_seq->sequence[i].arg1W);
306         SysFreeString(call_seq->sequence[i].arg2W);
307         SysFreeString(call_seq->sequence[i].arg3W);
308     }
309 
310     heap_free(call_seq->sequence);
311     call_seq->sequence = NULL;
312     call_seq->count = call_seq->size = 0;
313 }
314 
315 static const char *get_event_name(CH event)
316 {
317     return event_names[event];
318 }
319 
320 static void compare_attributes(const struct call_entry *actual, const struct call_entry *expected, const char *context,
321     BOOL todo, const char *file, int line, int *failcount)
322 {
323     int i, lenexp = 0;
324 
325     /* attribute count is not stored for expected data */
326     if (expected->attributes)
327     {
328         struct attribute_entry *ptr = expected->attributes;
329         while (ptr->uri) { lenexp++; ptr++; };
330     }
331 
332     /* check count first and exit earlier */
333     if (actual->attr_count != lenexp && todo)
334     {
335         (*failcount)++;
336         todo_wine
337             ok_(file, line) (FALSE, "%s: in event %s expecting attr count %d got %d\n",
338                 context, get_event_name(actual->id), lenexp, actual->attr_count);
339     }
340     else
341         ok_(file, line) (actual->attr_count == lenexp, "%s: in event %s expecting attr count %d got %d\n",
342             context, get_event_name(actual->id), lenexp, actual->attr_count);
343 
344     if (actual->attr_count != lenexp) return;
345 
346     /* now compare all attributes strings */
347     for (i = 0; i < actual->attr_count; i++)
348     {
349         test_saxstr(file, line, actual->attributes[i].uriW,   expected->attributes[i].uri, todo, failcount);
350         test_saxstr(file, line, actual->attributes[i].localW, expected->attributes[i].local, todo, failcount);
351         test_saxstr(file, line, actual->attributes[i].qnameW, expected->attributes[i].qname, todo, failcount);
352         test_saxstr(file, line, actual->attributes[i].valueW, expected->attributes[i].value, todo, failcount);
353     }
354 }
355 
356 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
357     const struct call_entry *expected, const char *context, BOOL todo,
358     const char *file, int line)
359 {
360     struct call_sequence *call_seq = seq[sequence_index];
361     static const struct call_entry end_of_sequence = { CH_ENDTEST };
362     const struct call_entry *actual, *sequence;
363     int failcount = 0;
364 
365     add_call(seq, sequence_index, &end_of_sequence);
366 
367     sequence = call_seq->sequence;
368     actual = sequence;
369 
370     while (expected->id != CH_ENDTEST && actual->id != CH_ENDTEST)
371     {
372         if (expected->id == actual->id)
373         {
374             if (expected->line != -1)
375             {
376                 /* always test position data */
377                 if (expected->line != actual->line && todo)
378                 {
379                     todo_wine
380                     {
381                         failcount++;
382                         ok_(file, line) (FALSE,
383                             "%s: in event %s expecting line %d got %d\n",
384                             context, get_event_name(actual->id), expected->line, actual->line);
385                     }
386                 }
387                 else
388                 {
389                     ok_(file, line) (expected->line == actual->line,
390                         "%s: in event %s expecting line %d got %d\n",
391                         context, get_event_name(actual->id), expected->line, actual->line);
392                 }
393             }
394 
395 
396             if (expected->column != -1)
397             {
398                 if (expected->column != actual->column && todo)
399                 {
400                     todo_wine
401                     {
402                         failcount++;
403                         ok_(file, line) (FALSE,
404                             "%s: in event %s expecting column %d got %d\n",
405                             context, get_event_name(actual->id), expected->column, actual->column);
406                     }
407                 }
408                 else
409                 {
410                     ok_(file, line) (expected->column == actual->column,
411                         "%s: in event %s expecting column %d got %d\n",
412                         context, get_event_name(actual->id), expected->column, actual->column);
413                 }
414             }
415 
416             switch (actual->id)
417             {
418             case CH_PUTDOCUMENTLOCATOR:
419             case CH_STARTDOCUMENT:
420             case CH_ENDDOCUMENT:
421             case LH_STARTCDATA:
422             case LH_ENDCDATA:
423                 break;
424             case CH_STARTPREFIXMAPPING:
425                 /* prefix, uri */
426                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
427                 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
428                 break;
429             case CH_ENDPREFIXMAPPING:
430                 /* prefix */
431                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
432                 break;
433             case CH_STARTELEMENT:
434                 /* compare attributes */
435                 compare_attributes(actual, expected, context, todo, file, line, &failcount);
436                 /* fallthrough */
437             case CH_ENDELEMENT:
438                 /* uri, localname, qname */
439                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
440                 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
441                 test_saxstr(file, line, actual->arg3W, expected->arg3, todo, &failcount);
442                 break;
443             case CH_CHARACTERS:
444             case CH_IGNORABLEWHITESPACE:
445                 /* char data */
446                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
447                 break;
448             case CH_PROCESSINGINSTRUCTION:
449                 /* target, data */
450                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
451                 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
452                 break;
453             case CH_SKIPPEDENTITY:
454                 /* name */
455                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
456                 break;
457             case EH_FATALERROR:
458                 /* test return value only */
459                 if (expected->ret != actual->ret && todo)
460                 {
461                      failcount++;
462                      ok_(file, line) (FALSE,
463                          "%s: in event %s expecting ret 0x%08x got 0x%08x\n",
464                          context, get_event_name(actual->id), expected->ret, actual->ret);
465                 }
466                 else
467                      ok_(file, line) (expected->ret == actual->ret,
468                          "%s: in event %s expecting ret 0x%08x got 0x%08x\n",
469                          context, get_event_name(actual->id), expected->ret, actual->ret);
470                 break;
471             case EH_ERROR:
472             case EH_IGNORABLEWARNING:
473             default:
474                 ok(0, "%s: callback not handled, %s\n", context, get_event_name(actual->id));
475             }
476             expected++;
477             actual++;
478         }
479         else if (todo)
480         {
481             failcount++;
482             todo_wine
483             {
484                 ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n",
485                     context, get_event_name(expected->id), get_event_name(actual->id));
486             }
487 
488             flush_sequence(seq, sequence_index);
489             return;
490         }
491         else
492         {
493             ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n",
494                 context, get_event_name(expected->id), get_event_name(actual->id));
495             expected++;
496             actual++;
497         }
498     }
499 
500     if (todo)
501     {
502         todo_wine
503         {
504             if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST)
505             {
506                 failcount++;
507                 ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n",
508                     context, get_event_name(expected->id), get_event_name(actual->id));
509             }
510         }
511     }
512     else if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST)
513     {
514         ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n",
515             context, get_event_name(expected->id), get_event_name(actual->id));
516     }
517 
518     if (todo && !failcount) /* succeeded yet marked todo */
519     {
520         todo_wine
521         {
522             ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context);
523         }
524     }
525 
526     flush_sequence(seq, sequence_index);
527 }
528 
529 #define ok_sequence(seq, index, exp, contx, todo) \
530         ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
531 
532 static void init_call_sequences(struct call_sequence **seq, int n)
533 {
534     int i;
535 
536     for (i = 0; i < n; i++)
537         seq[i] = heap_alloc_zero(sizeof(struct call_sequence));
538 }
539 
540 static const WCHAR szSimpleXML[] = {
541 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"','1','.','0','\"',' ','?','>','\n',
542 '<','B','a','n','k','A','c','c','o','u','n','t','>','\n',
543 ' ',' ',' ','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\n',
544 ' ',' ',' ','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\n',
545 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\n','\0'
546 };
547 
548 static const WCHAR carriage_ret_test[] = {
549 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"','?','>','\r','\n',
550 '<','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n',
551 '\t','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\r','\n',
552 '\t','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\r','\n',
553 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n','\0'
554 };
555 
556 static const WCHAR szUtf16XML[] = {
557 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"',' ',
558 'e','n','c','o','d','i','n','g','=','"','U','T','F','-','1','6','"',' ',
559 's','t','a','n','d','a','l','o','n','e','=','"','n','o','"','?','>','\r','\n'
560 };
561 
562 static const CHAR szUtf16BOM[] = {0xff, 0xfe};
563 
564 static const CHAR szUtf8XML[] =
565 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n";
566 
567 static const char utf8xml2[] =
568 "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\r\n";
569 
570 static const char testXML[] =
571 "<?xml version=\"1.0\" ?>\n"
572 "<BankAccount>\n"
573 "   <Number>1234</Number>\n"
574 "   <Name>Captain Ahab</Name>\n"
575 "</BankAccount>\n";
576 
577 static const char test_attributes[] =
578 "<?xml version=\"1.0\" ?>\n"
579 "<document xmlns:test=\"prefix_test\" xmlns=\"prefix\" test:arg1=\"arg1\" arg2=\"arg2\" test:ar3=\"arg3\">\n"
580 "<node1 xmlns:p=\"test\" />"
581 "</document>\n";
582 
583 static const char test_cdata_xml[] =
584 "<?xml version=\"1.0\" ?>"
585 "<a><![CDATA[Some \r\ntext\n\r\ndata\n\n]]></a>";
586 
587 static const char test2_cdata_xml[] =
588 "<?xml version=\"1.0\" ?>"
589 "<a><![CDATA[\n\r\nSome \r\ntext\n\r\ndata\n\n]]></a>";
590 
591 static const char test3_cdata_xml[] =
592 "<?xml version=\"1.0\" ?><a><![CDATA[Some text data]]></a>";
593 
594 static struct call_entry content_handler_test1[] = {
595     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
596     { CH_STARTDOCUMENT, 0, 0, S_OK },
597     { CH_STARTELEMENT, 2, 14, S_OK, "", "BankAccount", "BankAccount" },
598     { CH_CHARACTERS, 2, 14, S_OK, "\n   " },
599     { CH_STARTELEMENT, 3, 12, S_OK, "", "Number", "Number" },
600     { CH_CHARACTERS, 3, 12, S_OK, "1234" },
601     { CH_ENDELEMENT, 3, 18, S_OK, "", "Number", "Number" },
602     { CH_CHARACTERS, 3, 25, S_OK, "\n   " },
603     { CH_STARTELEMENT, 4, 10, S_OK, "", "Name", "Name" },
604     { CH_CHARACTERS, 4, 10, S_OK, "Captain Ahab" },
605     { CH_ENDELEMENT, 4, 24, S_OK, "", "Name", "Name" },
606     { CH_CHARACTERS, 4, 29, S_OK, "\n" },
607     { CH_ENDELEMENT, 5, 3, S_OK, "", "BankAccount", "BankAccount" },
608     { CH_ENDDOCUMENT, 0, 0, S_OK},
609     { CH_ENDTEST }
610 };
611 
612 /* applies to versions 4 and 6 */
613 static struct call_entry content_handler_test1_alternate[] = {
614     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
615     { CH_STARTDOCUMENT, 1, 22, S_OK },
616     { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" },
617     { CH_CHARACTERS, 3, 4, S_OK, "\n   " },
618     { CH_STARTELEMENT, 3, 11, S_OK, "", "Number", "Number" },
619     { CH_CHARACTERS, 3, 16, S_OK, "1234" },
620     { CH_ENDELEMENT, 3, 24, S_OK, "", "Number", "Number" },
621     { CH_CHARACTERS, 4, 4, S_OK, "\n   " },
622     { CH_STARTELEMENT, 4, 9, S_OK, "", "Name", "Name" },
623     { CH_CHARACTERS, 4, 22, S_OK, "Captain Ahab" },
624     { CH_ENDELEMENT, 4, 28, S_OK, "", "Name", "Name" },
625     { CH_CHARACTERS, 5, 1, S_OK, "\n" },
626     { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" },
627     { CH_ENDDOCUMENT, 6, 0, S_OK },
628     { CH_ENDTEST }
629 };
630 
631 static struct call_entry content_handler_test2[] = {
632     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
633     { CH_STARTDOCUMENT, 0, 0, S_OK },
634     { CH_STARTELEMENT, 2, 14, S_OK, "", "BankAccount", "BankAccount" },
635     { CH_CHARACTERS, 2, 14, S_OK, "\n" },
636     { CH_CHARACTERS, 2, 16, S_OK, "\t" },
637     { CH_STARTELEMENT, 3, 10, S_OK, "", "Number", "Number" },
638     { CH_CHARACTERS, 3, 10, S_OK, "1234" },
639     { CH_ENDELEMENT, 3, 16, S_OK, "", "Number", "Number" },
640     { CH_CHARACTERS, 3, 23, S_OK, "\n" },
641     { CH_CHARACTERS, 3, 25, S_OK, "\t" },
642     { CH_STARTELEMENT, 4, 8, S_OK, "", "Name", "Name" },
643     { CH_CHARACTERS, 4, 8, S_OK, "Captain Ahab" },
644     { CH_ENDELEMENT, 4, 22, S_OK, "", "Name", "Name" },
645     { CH_CHARACTERS, 4, 27, S_OK, "\n" },
646     { CH_ENDELEMENT, 5, 3, S_OK, "", "BankAccount", "BankAccount" },
647     { CH_ENDDOCUMENT, 0, 0, S_OK },
648     { CH_ENDTEST }
649 };
650 
651 static struct call_entry content_handler_test2_alternate[] = {
652     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
653     { CH_STARTDOCUMENT, 1, 21, S_OK },
654     { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" },
655     { CH_CHARACTERS, 3, 0, S_OK, "\n" },
656     { CH_CHARACTERS, 3, 2, S_OK, "\t" },
657     { CH_STARTELEMENT, 3, 9, S_OK, "", "Number", "Number" },
658     { CH_CHARACTERS, 3, 14, S_OK, "1234" },
659     { CH_ENDELEMENT, 3, 22, S_OK, "", "Number", "Number" },
660     { CH_CHARACTERS, 4, 0, S_OK, "\n" },
661     { CH_CHARACTERS, 4, 2, S_OK, "\t" },
662     { CH_STARTELEMENT, 4, 7, S_OK, "", "Name", "Name" },
663     { CH_CHARACTERS, 4, 20, S_OK, "Captain Ahab" },
664     { CH_ENDELEMENT, 4, 26, S_OK, "", "Name", "Name" },
665     { CH_CHARACTERS, 5, 0, S_OK, "\n" },
666     { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" },
667     { CH_ENDDOCUMENT, 6, 0, S_OK },
668     { CH_ENDTEST }
669 };
670 
671 static struct call_entry content_handler_testerror[] = {
672     { CH_PUTDOCUMENTLOCATOR, 0, 0, E_FAIL },
673     { EH_FATALERROR, 0, 0, E_FAIL },
674     { CH_ENDTEST }
675 };
676 
677 static struct call_entry content_handler_testerror_alternate[] = {
678     { CH_PUTDOCUMENTLOCATOR, 1, 0, E_FAIL },
679     { EH_FATALERROR, 1, 0, E_FAIL },
680     { CH_ENDTEST }
681 };
682 
683 static struct call_entry content_handler_test_callback_rets[] = {
684     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_FALSE },
685     { CH_STARTDOCUMENT, 0, 0, S_FALSE },
686     { EH_FATALERROR, 0, 0, S_FALSE },
687     { CH_ENDTEST }
688 };
689 
690 static struct call_entry content_handler_test_callback_rets_alt[] = {
691     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_FALSE },
692     { CH_STARTDOCUMENT, 1, 22, S_FALSE },
693     { CH_STARTELEMENT, 2, 13, S_FALSE, "", "BankAccount", "BankAccount" },
694     { CH_CHARACTERS, 3, 4, S_FALSE, "\n   " },
695     { CH_STARTELEMENT, 3, 11, S_FALSE, "", "Number", "Number" },
696     { CH_CHARACTERS, 3, 16, S_FALSE, "1234" },
697     { CH_ENDELEMENT, 3, 24, S_FALSE, "", "Number", "Number" },
698     { CH_CHARACTERS, 4, 4, S_FALSE, "\n   " },
699     { CH_STARTELEMENT, 4, 9, S_FALSE, "", "Name", "Name" },
700     { CH_CHARACTERS, 4, 22, S_FALSE, "Captain Ahab" },
701     { CH_ENDELEMENT, 4, 28, S_FALSE, "", "Name", "Name" },
702     { CH_CHARACTERS, 5, 1, S_FALSE, "\n" },
703     { CH_ENDELEMENT, 5, 14, S_FALSE, "", "BankAccount", "BankAccount" },
704     { CH_ENDDOCUMENT, 6, 0, S_FALSE },
705     { CH_ENDTEST }
706 };
707 
708 static struct attribute_entry ch_attributes1[] = {
709     { "", "", "xmlns:test", "prefix_test" },
710     { "", "", "xmlns", "prefix" },
711     { "prefix_test", "arg1", "test:arg1", "arg1" },
712     { "", "arg2", "arg2", "arg2" },
713     { "prefix_test", "ar3", "test:ar3", "arg3" },
714     { NULL }
715 };
716 
717 static struct attribute_entry ch_attributes2[] = {
718     { "", "", "xmlns:p", "test" },
719     { NULL }
720 };
721 
722 static struct call_entry content_handler_test_attributes[] = {
723     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
724     { CH_STARTDOCUMENT, 0, 0, S_OK },
725     { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "test", "prefix_test" },
726     { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "", "prefix" },
727     { CH_STARTELEMENT, 2, 96, S_OK, "prefix", "document", "document", ch_attributes1 },
728     { CH_CHARACTERS, 2, 96, S_OK, "\n" },
729     { CH_STARTPREFIXMAPPING, 3, 25, S_OK, "p", "test" },
730     { CH_STARTELEMENT, 3, 25, S_OK, "prefix", "node1", "node1", ch_attributes2 },
731     { CH_ENDELEMENT, 3, 25, S_OK, "prefix", "node1", "node1" },
732     { CH_ENDPREFIXMAPPING, 3, 25, S_OK, "p" },
733     { CH_ENDELEMENT, 3, 27, S_OK, "prefix", "document", "document" },
734     { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "" },
735     { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "test" },
736     { CH_ENDDOCUMENT, 0, 0 },
737     { CH_ENDTEST }
738 };
739 
740 static struct attribute_entry ch_attributes_alt_4[] = {
741     { "prefix_test", "arg1", "test:arg1", "arg1" },
742     { "", "arg2", "arg2", "arg2" },
743     { "prefix_test", "ar3", "test:ar3", "arg3" },
744     { "", "", "xmlns:test", "prefix_test" },
745     { "", "", "xmlns", "prefix" },
746     { NULL }
747 };
748 
749 static struct call_entry content_handler_test_attributes_alternate_4[] = {
750     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
751     { CH_STARTDOCUMENT, 1, 22, S_OK },
752     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
753     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
754     { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_4 },
755     { CH_CHARACTERS, 3, 1, S_OK, "\n" },
756     { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
757     { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2 },
758     { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
759     { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
760     { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
761     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
762     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
763     { CH_ENDDOCUMENT, 4, 0, S_OK },
764     { CH_ENDTEST }
765 };
766 
767 /* 'namespace' feature switched off */
768 static struct attribute_entry ch_attributes_alt_no_ns[] = {
769     { "", "", "xmlns:test", "prefix_test" },
770     { "", "", "xmlns", "prefix" },
771     { "", "", "test:arg1", "arg1" },
772     { "", "", "arg2", "arg2" },
773     { "", "", "test:ar3", "arg3" },
774     { NULL }
775 };
776 
777 static struct call_entry content_handler_test_attributes_alt_no_ns[] = {
778     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
779     { CH_STARTDOCUMENT, 1, 22, S_OK },
780     { CH_STARTELEMENT, 2, 95, S_OK, "", "", "document", ch_attributes_alt_no_ns },
781     { CH_CHARACTERS, 3, 1, S_OK, "\n" },
782     { CH_STARTELEMENT, 3, 24, S_OK, "", "", "node1", ch_attributes2 },
783     { CH_ENDELEMENT, 3, 24, S_OK, "", "", "node1" },
784     { CH_ENDELEMENT, 3, 35, S_OK, "", "", "document" },
785     { CH_ENDDOCUMENT, 4, 0, S_OK },
786     { CH_ENDTEST }
787 };
788 
789 static struct attribute_entry ch_attributes_alt_6[] = {
790     { "prefix_test", "arg1", "test:arg1", "arg1" },
791     { "", "arg2", "arg2", "arg2" },
792     { "prefix_test", "ar3", "test:ar3", "arg3" },
793     { "http://www.w3.org/2000/xmlns/", "", "xmlns:test", "prefix_test" },
794     { "http://www.w3.org/2000/xmlns/", "", "xmlns", "prefix" },
795     { NULL }
796 };
797 
798 static struct attribute_entry ch_attributes2_6[] = {
799     { "http://www.w3.org/2000/xmlns/", "", "xmlns:p", "test" },
800     { NULL }
801 };
802 
803 static struct call_entry content_handler_test_attributes_alternate_6[] = {
804     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
805     { CH_STARTDOCUMENT, 1, 22, S_OK },
806     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
807     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
808     { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_6 },
809     { CH_CHARACTERS, 3, 1, S_OK, "\n" },
810     { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
811     { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2_6 },
812     { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
813     { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
814     { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
815     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
816     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
817     { CH_ENDDOCUMENT, 4, 0, S_OK },
818     { CH_ENDTEST }
819 };
820 
821 /* 'namespaces' is on, 'namespace-prefixes' if off */
822 static struct attribute_entry ch_attributes_no_prefix[] = {
823     { "prefix_test", "arg1", "test:arg1", "arg1" },
824     { "", "arg2", "arg2", "arg2" },
825     { "prefix_test", "ar3", "test:ar3", "arg3" },
826     { NULL }
827 };
828 
829 static struct call_entry content_handler_test_attributes_alt_no_prefix[] = {
830     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
831     { CH_STARTDOCUMENT, 1, 22, S_OK },
832     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
833     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
834     { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_no_prefix },
835     { CH_CHARACTERS, 3, 1, S_OK, "\n" },
836     { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
837     { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", NULL },
838     { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
839     { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
840     { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
841     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
842     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
843     { CH_ENDDOCUMENT, 4, 0, S_OK },
844     { CH_ENDTEST }
845 };
846 
847 static struct call_entry content_handler_test_attributes_no_prefix[] = {
848     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
849     { CH_STARTDOCUMENT, 0, 0, S_OK },
850     { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "test", "prefix_test" },
851     { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "", "prefix" },
852     { CH_STARTELEMENT, 2, 96, S_OK, "prefix", "document", "document", ch_attributes_no_prefix },
853     { CH_CHARACTERS, 2, 96, S_OK, "\n" },
854     { CH_STARTPREFIXMAPPING, 3, 25, S_OK, "p", "test" },
855     { CH_STARTELEMENT, 3, 25, S_OK, "prefix", "node1", "node1", NULL },
856     { CH_ENDELEMENT, 3, 25, S_OK, "prefix", "node1", "node1" },
857     { CH_ENDPREFIXMAPPING, 3, 25, S_OK, "p" },
858     { CH_ENDELEMENT, 3, 27, S_OK, "prefix", "document", "document" },
859     { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "" },
860     { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "test" },
861     { CH_ENDDOCUMENT, 0, 0 },
862     { CH_ENDTEST }
863 };
864 
865 static struct attribute_entry xmlspace_attrs[] = {
866     { "http://www.w3.org/XML/1998/namespace", "space", "xml:space", "preserve" },
867     { NULL }
868 };
869 
870 static struct call_entry xmlspaceattr_test[] = {
871     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
872     { CH_STARTDOCUMENT, 0, 0, S_OK },
873     { CH_STARTELEMENT, 1, 64, S_OK, "", "a", "a", xmlspace_attrs },
874     { CH_CHARACTERS, 1, 64, S_OK, " Some text data " },
875     { CH_ENDELEMENT, 1, 82, S_OK, "", "a", "a" },
876     { CH_ENDDOCUMENT, 0, 0, S_OK },
877     { CH_ENDTEST }
878 };
879 
880 static struct call_entry xmlspaceattr_test_alternate[] = {
881     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
882     { CH_STARTDOCUMENT, 1, 39, S_OK },
883     { CH_STARTELEMENT, 1, 63, S_OK, "", "a", "a", xmlspace_attrs },
884     { CH_CHARACTERS, 1, 80, S_OK, " Some text data " },
885     { CH_ENDELEMENT, 1, 83, S_OK, "", "a", "a" },
886     { CH_ENDDOCUMENT, 1, 83, S_OK },
887     { CH_ENDTEST }
888 };
889 
890 /* attribute value normalization test */
891 static const char attribute_normalize[] =
892     "<?xml version=\"1.0\" ?>\n"
893     "<a attr1=\" \r \n \tattr_value &#65; &#38; &amp;\t \r \n\r\n \n\"/>\n";
894 
895 static struct attribute_entry attribute_norm_attrs[] = {
896     { "", "attr1", "attr1", "      attr_value A & &        " },
897     { NULL }
898 };
899 
900 static struct call_entry attribute_norm[] = {
901     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
902     { CH_STARTDOCUMENT, 0, 0, S_OK },
903     { CH_STARTELEMENT, 6, 4, S_OK, "", "a", "a", attribute_norm_attrs },
904     { CH_ENDELEMENT, 6, 4, S_OK, "", "a", "a" },
905     { CH_ENDDOCUMENT, 0, 0, S_OK },
906     { CH_ENDTEST }
907 };
908 
909 static struct call_entry attribute_norm_alt[] = {
910     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
911     { CH_STARTDOCUMENT, 1, 22, S_OK },
912     { CH_STARTELEMENT, 8, 3, S_OK, "", "a", "a", attribute_norm_attrs },
913     { CH_ENDELEMENT, 8, 3, S_OK, "", "a", "a" },
914     { CH_ENDDOCUMENT, 9, 0, S_OK },
915     { CH_ENDTEST }
916 };
917 
918 static struct call_entry cdata_test[] = {
919     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
920     { CH_STARTDOCUMENT, 0, 0, S_OK },
921     { CH_STARTELEMENT, 1, 26, S_OK, "", "a", "a" },
922     { LH_STARTCDATA, 1, 35, S_OK },
923     { CH_CHARACTERS, 1, 35, S_OK, "Some \n" },
924     { CH_CHARACTERS, 1, 42, S_OK, "text\n\n" },
925     { CH_CHARACTERS, 1, 49, S_OK,  "data\n\n" },
926     { LH_ENDCDATA, 1, 49, S_OK },
927     { CH_ENDELEMENT, 6, 6, S_OK, "", "a", "a" },
928     { CH_ENDDOCUMENT, 0, 0, S_OK },
929     { CH_ENDTEST }
930 };
931 
932 static struct call_entry cdata_test2[] = {
933     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
934     { CH_STARTDOCUMENT, 0, 0, S_OK },
935     { CH_STARTELEMENT, 1, 26, S_OK, "", "a", "a" },
936     { LH_STARTCDATA, 1, 35, S_OK },
937     { CH_CHARACTERS, 1, 35, S_OK, "\n\n" },
938     { CH_CHARACTERS, 1, 38, S_OK, "Some \n" },
939     { CH_CHARACTERS, 1, 45, S_OK, "text\n\n" },
940     { CH_CHARACTERS, 1, 52, S_OK,  "data\n\n" },
941     { LH_ENDCDATA, 1, 52, S_OK },
942     { CH_ENDELEMENT, 8, 6, S_OK, "", "a", "a" },
943     { CH_ENDDOCUMENT, 0, 0, S_OK },
944     { CH_ENDTEST }
945 };
946 
947 static struct call_entry cdata_test3[] = {
948     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
949     { CH_STARTDOCUMENT, 0, 0, S_OK },
950     { CH_STARTELEMENT, 1, 26, S_OK, "", "a", "a" },
951     { LH_STARTCDATA, 1, 35, S_OK },
952     { CH_CHARACTERS, 1, 35, S_OK, "Some text data" },
953     { LH_ENDCDATA, 1, 35, S_OK },
954     { CH_ENDELEMENT, 1, 54, S_OK, "", "a", "a" },
955     { CH_ENDDOCUMENT, 0, 0, S_OK },
956     { CH_ENDTEST }
957 };
958 
959 /* this is what MSXML6 does */
960 static struct call_entry cdata_test_alt[] = {
961     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
962     { CH_STARTDOCUMENT, 1, 22, S_OK },
963     { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" },
964     { LH_STARTCDATA, 1, 34, S_OK },
965     { CH_CHARACTERS, 1, 40, S_OK, "Some " },
966     { CH_CHARACTERS, 2, 0, S_OK, "\n" },
967     { CH_CHARACTERS, 3, 1, S_OK, "text\n" },
968     { CH_CHARACTERS, 4, 0, S_OK, "\n" },
969     { CH_CHARACTERS, 6, 3, S_OK, "data\n\n" },
970     { LH_ENDCDATA, 6, 3, S_OK },
971     { CH_ENDELEMENT, 6, 7, S_OK, "", "a", "a" },
972     { CH_ENDDOCUMENT, 6, 7, S_OK },
973     { CH_ENDTEST }
974 };
975 
976 static struct call_entry cdata_test2_alt[] = {
977     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
978     { CH_STARTDOCUMENT, 1, 22, S_OK },
979     { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" },
980     { LH_STARTCDATA, 1, 34, S_OK },
981     { CH_CHARACTERS, 2, 1, S_OK, "\n" },
982     { CH_CHARACTERS, 3, 0, S_OK, "\n" },
983     { CH_CHARACTERS, 3, 6, S_OK, "Some " },
984     { CH_CHARACTERS, 4, 0, S_OK, "\n" },
985     { CH_CHARACTERS, 5, 1, S_OK, "text\n" },
986     { CH_CHARACTERS, 6, 0, S_OK, "\n" },
987     { CH_CHARACTERS, 8, 3, S_OK, "data\n\n" },
988     { LH_ENDCDATA, 8, 3, S_OK },
989     { CH_ENDELEMENT, 8, 7, S_OK, "", "a", "a" },
990     { CH_ENDDOCUMENT, 8, 7, S_OK },
991     { CH_ENDTEST }
992 };
993 
994 static struct call_entry cdata_test3_alt[] = {
995     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
996     { CH_STARTDOCUMENT, 1, 22, S_OK },
997     { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" },
998     { LH_STARTCDATA, 1, 34, S_OK },
999     { CH_CHARACTERS, 1, 51, S_OK, "Some text data" },
1000     { LH_ENDCDATA, 1, 51, S_OK },
1001     { CH_ENDELEMENT, 1, 55, S_OK, "", "a", "a" },
1002     { CH_ENDDOCUMENT, 1, 55, S_OK },
1003     { CH_ENDTEST }
1004 };
1005 
1006 static struct attribute_entry read_test_attrs[] = {
1007     { "", "attr", "attr", "val" },
1008     { NULL }
1009 };
1010 
1011 static struct call_entry read_test_seq[] = {
1012     { CH_PUTDOCUMENTLOCATOR, -1, 0, S_OK },
1013     { CH_STARTDOCUMENT, -1, -1, S_OK },
1014     { CH_STARTELEMENT, -1, -1, S_OK, "", "rootelem", "rootelem" },
1015     { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs },
1016     { CH_CHARACTERS, -1, -1, S_OK, "text" },
1017     { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" },
1018     { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs },
1019     { CH_CHARACTERS, -1, -1, S_OK, "text" },
1020     { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" },
1021     { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs },
1022     { CH_CHARACTERS, -1, -1, S_OK, "text" },
1023     { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" },
1024     { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs },
1025     { CH_CHARACTERS, -1, -1, S_OK, "text" },
1026     { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" },
1027     { CH_ENDELEMENT, -1, -1, S_OK, "", "rootelem", "rootelem" },
1028     { CH_ENDDOCUMENT, -1, -1, S_OK},
1029     { CH_ENDTEST }
1030 };
1031 
1032 static const char xmlspace_attr[] =
1033     "<?xml version=\"1.0\" encoding=\"UTF-16\"?>"
1034     "<a xml:space=\"preserve\"> Some text data </a>";
1035 
1036 static struct call_entry *expectCall;
1037 static ISAXLocator *locator;
1038 static ISAXXMLReader *g_reader;
1039 int msxml_version;
1040 
1041 static void set_expected_seq(struct call_entry *expected)
1042 {
1043     expectCall = expected;
1044 }
1045 
1046 /* to be called once on each tested callback return */
1047 static HRESULT get_expected_ret(void)
1048 {
1049     HRESULT hr = expectCall->ret;
1050     if (expectCall->id != CH_ENDTEST) expectCall++;
1051     return hr;
1052 }
1053 
1054 static HRESULT WINAPI contentHandler_QueryInterface(
1055         ISAXContentHandler* iface,
1056         REFIID riid,
1057         void **ppvObject)
1058 {
1059     *ppvObject = NULL;
1060 
1061     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler))
1062     {
1063         *ppvObject = iface;
1064     }
1065     else
1066     {
1067         return E_NOINTERFACE;
1068     }
1069 
1070     return S_OK;
1071 }
1072 
1073 static ULONG WINAPI contentHandler_AddRef(
1074         ISAXContentHandler* iface)
1075 {
1076     return 2;
1077 }
1078 
1079 static ULONG WINAPI contentHandler_Release(
1080         ISAXContentHandler* iface)
1081 {
1082     return 1;
1083 }
1084 
1085 static HRESULT WINAPI contentHandler_putDocumentLocator(
1086         ISAXContentHandler* iface,
1087         ISAXLocator *pLocator)
1088 {
1089     struct call_entry call;
1090     IUnknown *unk;
1091     HRESULT hr;
1092 
1093     locator = pLocator;
1094 
1095     init_call_entry(locator, &call);
1096     call.id = CH_PUTDOCUMENTLOCATOR;
1097     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1098 
1099     hr = ISAXLocator_QueryInterface(pLocator, &IID_IVBSAXLocator, (void**)&unk);
1100     EXPECT_HR(hr, E_NOINTERFACE);
1101 
1102     if (msxml_version >= 6) {
1103         ISAXAttributes *attr, *attr1;
1104         IMXAttributes *mxattr;
1105 
1106         EXPECT_REF(pLocator, 1);
1107         hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr);
1108         EXPECT_HR(hr, S_OK);
1109         EXPECT_REF(pLocator, 2);
1110         hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr1);
1111         EXPECT_HR(hr, S_OK);
1112         EXPECT_REF(pLocator, 3);
1113         ok(attr == attr1, "got %p, %p\n", attr, attr1);
1114 
1115         hr = ISAXAttributes_QueryInterface(attr, &IID_IVBSAXAttributes, (void**)&unk);
1116         EXPECT_HR(hr, E_NOINTERFACE);
1117 
1118         hr = ISAXLocator_QueryInterface(pLocator, &IID_IVBSAXAttributes, (void**)&unk);
1119         EXPECT_HR(hr, E_NOINTERFACE);
1120 
1121         hr = ISAXAttributes_QueryInterface(attr, &IID_IMXAttributes, (void**)&mxattr);
1122         EXPECT_HR(hr, E_NOINTERFACE);
1123 
1124         ISAXAttributes_Release(attr);
1125         ISAXAttributes_Release(attr1);
1126     }
1127 
1128     return get_expected_ret();
1129 }
1130 
1131 static ISAXAttributes *test_attr_ptr;
1132 static HRESULT WINAPI contentHandler_startDocument(
1133         ISAXContentHandler* iface)
1134 {
1135     struct call_entry call;
1136 
1137     init_call_entry(locator, &call);
1138     call.id = CH_STARTDOCUMENT;
1139     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1140 
1141     test_attr_ptr = NULL;
1142 
1143     return get_expected_ret();
1144 }
1145 
1146 static HRESULT WINAPI contentHandler_endDocument(
1147         ISAXContentHandler* iface)
1148 {
1149     struct call_entry call;
1150 
1151     init_call_entry(locator, &call);
1152     call.id = CH_ENDDOCUMENT;
1153     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1154 
1155     return get_expected_ret();
1156 }
1157 
1158 static HRESULT WINAPI contentHandler_startPrefixMapping(
1159         ISAXContentHandler* iface,
1160         const WCHAR *prefix, int prefix_len,
1161         const WCHAR *uri, int uri_len)
1162 {
1163     struct call_entry call;
1164 
1165     ok(prefix != NULL, "prefix == NULL\n");
1166     ok(uri != NULL, "uri == NULL\n");
1167 
1168     init_call_entry(locator, &call);
1169     call.id = CH_STARTPREFIXMAPPING;
1170     call.arg1W = SysAllocStringLen(prefix, prefix_len);
1171     call.arg2W = SysAllocStringLen(uri, uri_len);
1172     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1173 
1174     return get_expected_ret();
1175 }
1176 
1177 static HRESULT WINAPI contentHandler_endPrefixMapping(
1178         ISAXContentHandler* iface,
1179         const WCHAR *prefix, int len)
1180 {
1181     struct call_entry call;
1182 
1183     ok(prefix != NULL, "prefix == NULL\n");
1184 
1185     init_call_entry(locator, &call);
1186     call.id = CH_ENDPREFIXMAPPING;
1187     call.arg1W = SysAllocStringLen(prefix, len);
1188     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1189 
1190     return get_expected_ret();
1191 }
1192 
1193 static HRESULT WINAPI contentHandler_startElement(
1194         ISAXContentHandler* iface,
1195         const WCHAR *uri, int uri_len,
1196         const WCHAR *localname, int local_len,
1197         const WCHAR *qname, int qname_len,
1198         ISAXAttributes *saxattr)
1199 {
1200     struct call_entry call;
1201     IMXAttributes *mxattr;
1202     HRESULT hr;
1203     int len;
1204 
1205     ok(uri != NULL, "uri == NULL\n");
1206     ok(localname != NULL, "localname == NULL\n");
1207     ok(qname != NULL, "qname == NULL\n");
1208 
1209     hr = ISAXAttributes_QueryInterface(saxattr, &IID_IMXAttributes, (void**)&mxattr);
1210     EXPECT_HR(hr, E_NOINTERFACE);
1211 
1212     init_call_entry(locator, &call);
1213     call.id = CH_STARTELEMENT;
1214     call.arg1W = SysAllocStringLen(uri, uri_len);
1215     call.arg2W = SysAllocStringLen(localname, local_len);
1216     call.arg3W = SysAllocStringLen(qname, qname_len);
1217 
1218     if(!test_attr_ptr)
1219         test_attr_ptr = saxattr;
1220     ok(test_attr_ptr == saxattr, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr);
1221 
1222     /* store actual attributes */
1223     len = 0;
1224     hr = ISAXAttributes_getLength(saxattr, &len);
1225     EXPECT_HR(hr, S_OK);
1226 
1227     if (len)
1228     {
1229         VARIANT_BOOL v;
1230         int i;
1231 
1232         struct attribute_entry *attr;
1233         attr = heap_alloc_zero(len * sizeof(*attr));
1234 
1235         v = VARIANT_TRUE;
1236         hr = ISAXXMLReader_getFeature(g_reader, _bstr_("http://xml.org/sax/features/namespaces"), &v);
1237         EXPECT_HR(hr, S_OK);
1238 
1239         for (i = 0; i < len; i++)
1240         {
1241             const WCHAR *value;
1242             int value_len;
1243 
1244             hr = ISAXAttributes_getName(saxattr, i, &uri, &uri_len,
1245                 &localname, &local_len, &qname, &qname_len);
1246             EXPECT_HR(hr, S_OK);
1247 
1248             hr = ISAXAttributes_getValue(saxattr, i, &value, &value_len);
1249             EXPECT_HR(hr, S_OK);
1250 
1251             /* if 'namespaces' switched off uri and local name contains garbage */
1252             if (v == VARIANT_FALSE && msxml_version > 0)
1253             {
1254                 attr[i].uriW   = SysAllocStringLen(NULL, 0);
1255                 attr[i].localW = SysAllocStringLen(NULL, 0);
1256             }
1257             else
1258             {
1259                 attr[i].uriW   = SysAllocStringLen(uri, uri_len);
1260                 attr[i].localW = SysAllocStringLen(localname, local_len);
1261             }
1262 
1263             attr[i].qnameW = SysAllocStringLen(qname, qname_len);
1264             attr[i].valueW = SysAllocStringLen(value, value_len);
1265         }
1266 
1267         call.attributes = attr;
1268         call.attr_count = len;
1269     }
1270 
1271     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1272 
1273     return get_expected_ret();
1274 }
1275 
1276 static HRESULT WINAPI contentHandler_endElement(
1277         ISAXContentHandler* iface,
1278         const WCHAR *uri, int uri_len,
1279         const WCHAR *localname, int local_len,
1280         const WCHAR *qname, int qname_len)
1281 {
1282     struct call_entry call;
1283 
1284     ok(uri != NULL, "uri == NULL\n");
1285     ok(localname != NULL, "localname == NULL\n");
1286     ok(qname != NULL, "qname == NULL\n");
1287 
1288     init_call_entry(locator, &call);
1289     call.id = CH_ENDELEMENT;
1290     call.arg1W = SysAllocStringLen(uri, uri_len);
1291     call.arg2W = SysAllocStringLen(localname, local_len);
1292     call.arg3W = SysAllocStringLen(qname, qname_len);
1293     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1294 
1295     return get_expected_ret();
1296 }
1297 
1298 static HRESULT WINAPI contentHandler_characters(
1299         ISAXContentHandler* iface,
1300         const WCHAR *chars,
1301         int len)
1302 {
1303     struct call_entry call;
1304 
1305     ok(chars != NULL, "chars == NULL\n");
1306 
1307     init_call_entry(locator, &call);
1308     call.id = CH_CHARACTERS;
1309     call.arg1W = SysAllocStringLen(chars, len);
1310     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1311 
1312     return get_expected_ret();
1313 }
1314 
1315 static HRESULT WINAPI contentHandler_ignorableWhitespace(
1316         ISAXContentHandler* iface,
1317         const WCHAR *chars, int len)
1318 {
1319     struct call_entry call;
1320 
1321     ok(chars != NULL, "chars == NULL\n");
1322 
1323     init_call_entry(locator, &call);
1324     call.id = CH_IGNORABLEWHITESPACE;
1325     call.arg1W = SysAllocStringLen(chars, len);
1326     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1327 
1328     return get_expected_ret();
1329 }
1330 
1331 static HRESULT WINAPI contentHandler_processingInstruction(
1332         ISAXContentHandler* iface,
1333         const WCHAR *target, int target_len,
1334         const WCHAR *data, int data_len)
1335 {
1336     struct call_entry call;
1337 
1338     ok(target != NULL, "target == NULL\n");
1339     ok(data != NULL, "data == NULL\n");
1340 
1341     init_call_entry(locator, &call);
1342     call.id = CH_PROCESSINGINSTRUCTION;
1343     call.arg1W = SysAllocStringLen(target, target_len);
1344     call.arg2W = SysAllocStringLen(data, data_len);
1345     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1346 
1347     return get_expected_ret();
1348 }
1349 
1350 static HRESULT WINAPI contentHandler_skippedEntity(
1351         ISAXContentHandler* iface,
1352         const WCHAR *name, int len)
1353 {
1354     struct call_entry call;
1355 
1356     ok(name != NULL, "name == NULL\n");
1357 
1358     init_call_entry(locator, &call);
1359     call.id = CH_SKIPPEDENTITY;
1360     call.arg1W = SysAllocStringLen(name, len);
1361     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1362 
1363     return get_expected_ret();
1364 }
1365 
1366 static const ISAXContentHandlerVtbl contentHandlerVtbl =
1367 {
1368     contentHandler_QueryInterface,
1369     contentHandler_AddRef,
1370     contentHandler_Release,
1371     contentHandler_putDocumentLocator,
1372     contentHandler_startDocument,
1373     contentHandler_endDocument,
1374     contentHandler_startPrefixMapping,
1375     contentHandler_endPrefixMapping,
1376     contentHandler_startElement,
1377     contentHandler_endElement,
1378     contentHandler_characters,
1379     contentHandler_ignorableWhitespace,
1380     contentHandler_processingInstruction,
1381     contentHandler_skippedEntity
1382 };
1383 
1384 static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
1385 
1386 static HRESULT WINAPI isaxerrorHandler_QueryInterface(
1387         ISAXErrorHandler* iface,
1388         REFIID riid,
1389         void **ppvObject)
1390 {
1391     *ppvObject = NULL;
1392 
1393     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
1394     {
1395         *ppvObject = iface;
1396     }
1397     else
1398     {
1399         return E_NOINTERFACE;
1400     }
1401 
1402     return S_OK;
1403 }
1404 
1405 static ULONG WINAPI isaxerrorHandler_AddRef(
1406         ISAXErrorHandler* iface)
1407 {
1408     return 2;
1409 }
1410 
1411 static ULONG WINAPI isaxerrorHandler_Release(
1412         ISAXErrorHandler* iface)
1413 {
1414     return 1;
1415 }
1416 
1417 static HRESULT WINAPI isaxerrorHandler_error(
1418         ISAXErrorHandler* iface,
1419         ISAXLocator *pLocator,
1420         const WCHAR *pErrorMessage,
1421         HRESULT hrErrorCode)
1422 {
1423     ok(0, "unexpected call\n");
1424     return S_OK;
1425 }
1426 
1427 static HRESULT WINAPI isaxerrorHandler_fatalError(
1428         ISAXErrorHandler* iface,
1429         ISAXLocator *pLocator,
1430         const WCHAR *message,
1431         HRESULT hr)
1432 {
1433     struct call_entry call;
1434 
1435     init_call_entry(locator, &call);
1436     call.id  = EH_FATALERROR;
1437     call.ret = hr;
1438 
1439     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1440 
1441     get_expected_ret();
1442     return S_OK;
1443 }
1444 
1445 static HRESULT WINAPI isaxerrorHandler_ignorableWarning(
1446         ISAXErrorHandler* iface,
1447         ISAXLocator *pLocator,
1448         const WCHAR *pErrorMessage,
1449         HRESULT hrErrorCode)
1450 {
1451     ok(0, "unexpected call\n");
1452     return S_OK;
1453 }
1454 
1455 static const ISAXErrorHandlerVtbl errorHandlerVtbl =
1456 {
1457     isaxerrorHandler_QueryInterface,
1458     isaxerrorHandler_AddRef,
1459     isaxerrorHandler_Release,
1460     isaxerrorHandler_error,
1461     isaxerrorHandler_fatalError,
1462     isaxerrorHandler_ignorableWarning
1463 };
1464 
1465 static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
1466 
1467 static HRESULT WINAPI isaxattributes_QueryInterface(
1468         ISAXAttributes* iface,
1469         REFIID riid,
1470         void **ppvObject)
1471 {
1472     *ppvObject = NULL;
1473 
1474     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
1475     {
1476         *ppvObject = iface;
1477     }
1478     else
1479     {
1480         return E_NOINTERFACE;
1481     }
1482 
1483     return S_OK;
1484 }
1485 
1486 static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
1487 {
1488     return 2;
1489 }
1490 
1491 static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
1492 {
1493     return 1;
1494 }
1495 
1496 static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
1497 {
1498     *length = 3;
1499     return S_OK;
1500 }
1501 
1502 static HRESULT WINAPI isaxattributes_getURI(
1503     ISAXAttributes* iface,
1504     int nIndex,
1505     const WCHAR **pUrl,
1506     int *pUriSize)
1507 {
1508     ok(0, "unexpected call\n");
1509     return E_NOTIMPL;
1510 }
1511 
1512 static HRESULT WINAPI isaxattributes_getLocalName(
1513     ISAXAttributes* iface,
1514     int nIndex,
1515     const WCHAR **pLocalName,
1516     int *pLocalNameLength)
1517 {
1518     ok(0, "unexpected call\n");
1519     return E_NOTIMPL;
1520 }
1521 
1522 static HRESULT WINAPI isaxattributes_getQName(
1523     ISAXAttributes* iface,
1524     int index,
1525     const WCHAR **QName,
1526     int *QNameLength)
1527 {
1528     static const WCHAR attrqnamesW[][15] = {{'a',':','a','t','t','r','1','j','u','n','k',0},
1529                                             {'a','t','t','r','2','j','u','n','k',0},
1530                                             {'a','t','t','r','3',0}};
1531     static const int attrqnamelen[] = {7, 5, 5};
1532 
1533     ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1534 
1535     if (index >= 0 && index <= 2) {
1536         *QName = attrqnamesW[index];
1537         *QNameLength = attrqnamelen[index];
1538     } else {
1539         *QName = NULL;
1540         *QNameLength = 0;
1541     }
1542 
1543     return S_OK;
1544 }
1545 
1546 static HRESULT WINAPI isaxattributes_getName(
1547     ISAXAttributes* iface,
1548     int nIndex,
1549     const WCHAR **pUri,
1550     int * pUriLength,
1551     const WCHAR ** pLocalName,
1552     int * pLocalNameSize,
1553     const WCHAR ** pQName,
1554     int * pQNameLength)
1555 {
1556     ok(0, "unexpected call\n");
1557     return E_NOTIMPL;
1558 }
1559 
1560 static HRESULT WINAPI isaxattributes_getIndexFromName(
1561     ISAXAttributes* iface,
1562     const WCHAR * pUri,
1563     int cUriLength,
1564     const WCHAR * pLocalName,
1565     int cocalNameLength,
1566     int * index)
1567 {
1568     ok(0, "unexpected call\n");
1569     return E_NOTIMPL;
1570 }
1571 
1572 static HRESULT WINAPI isaxattributes_getIndexFromQName(
1573     ISAXAttributes* iface,
1574     const WCHAR * pQName,
1575     int nQNameLength,
1576     int * index)
1577 {
1578     ok(0, "unexpected call\n");
1579     return E_NOTIMPL;
1580 }
1581 
1582 static HRESULT WINAPI isaxattributes_getType(
1583     ISAXAttributes* iface,
1584     int nIndex,
1585     const WCHAR ** pType,
1586     int * pTypeLength)
1587 {
1588     ok(0, "unexpected call\n");
1589     return E_NOTIMPL;
1590 }
1591 
1592 static HRESULT WINAPI isaxattributes_getTypeFromName(
1593     ISAXAttributes* iface,
1594     const WCHAR * pUri,
1595     int nUri,
1596     const WCHAR * pLocalName,
1597     int nLocalName,
1598     const WCHAR ** pType,
1599     int * nType)
1600 {
1601     ok(0, "unexpected call\n");
1602     return E_NOTIMPL;
1603 }
1604 
1605 static HRESULT WINAPI isaxattributes_getTypeFromQName(
1606     ISAXAttributes* iface,
1607     const WCHAR * pQName,
1608     int nQName,
1609     const WCHAR ** pType,
1610     int * nType)
1611 {
1612     ok(0, "unexpected call\n");
1613     return E_NOTIMPL;
1614 }
1615 
1616 static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes* iface, int index,
1617     const WCHAR **value, int *nValue)
1618 {
1619     static const WCHAR attrvaluesW[][10] = {{'a','1','j','u','n','k',0},
1620                                             {'a','2','j','u','n','k',0},
1621                                             {'<','&','"','>','\'',0}};
1622     static const int attrvalueslen[] = {2, 2, 5};
1623 
1624     ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1625 
1626     if (index >= 0 && index <= 2) {
1627         *value = attrvaluesW[index];
1628         *nValue = attrvalueslen[index];
1629     } else {
1630         *value = NULL;
1631         *nValue = 0;
1632     }
1633 
1634     return S_OK;
1635 }
1636 
1637 static HRESULT WINAPI isaxattributes_getValueFromName(
1638     ISAXAttributes* iface,
1639     const WCHAR * pUri,
1640     int nUri,
1641     const WCHAR * pLocalName,
1642     int nLocalName,
1643     const WCHAR ** pValue,
1644     int * nValue)
1645 {
1646     ok(0, "unexpected call\n");
1647     return E_NOTIMPL;
1648 }
1649 
1650 static HRESULT WINAPI isaxattributes_getValueFromQName(
1651     ISAXAttributes* iface,
1652     const WCHAR * pQName,
1653     int nQName,
1654     const WCHAR ** pValue,
1655     int * nValue)
1656 {
1657     ok(0, "unexpected call\n");
1658     return E_NOTIMPL;
1659 }
1660 
1661 static const ISAXAttributesVtbl SAXAttributesVtbl =
1662 {
1663     isaxattributes_QueryInterface,
1664     isaxattributes_AddRef,
1665     isaxattributes_Release,
1666     isaxattributes_getLength,
1667     isaxattributes_getURI,
1668     isaxattributes_getLocalName,
1669     isaxattributes_getQName,
1670     isaxattributes_getName,
1671     isaxattributes_getIndexFromName,
1672     isaxattributes_getIndexFromQName,
1673     isaxattributes_getType,
1674     isaxattributes_getTypeFromName,
1675     isaxattributes_getTypeFromQName,
1676     isaxattributes_getValue,
1677     isaxattributes_getValueFromName,
1678     isaxattributes_getValueFromQName
1679 };
1680 
1681 static ISAXAttributes saxattributes = { &SAXAttributesVtbl };
1682 
1683 struct saxlexicalhandler
1684 {
1685     ISAXLexicalHandler ISAXLexicalHandler_iface;
1686     LONG ref;
1687 
1688     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1689 };
1690 
1691 static inline struct saxlexicalhandler *impl_from_ISAXLexicalHandler( ISAXLexicalHandler *iface )
1692 {
1693     return CONTAINING_RECORD(iface, struct saxlexicalhandler, ISAXLexicalHandler_iface);
1694 }
1695 
1696 static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out)
1697 {
1698     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1699 
1700     *out = NULL;
1701 
1702     if (IsEqualGUID(riid, &IID_IUnknown))
1703     {
1704         *out = iface;
1705         ok(0, "got unexpected IID_IUnknown query\n");
1706     }
1707     else if (IsEqualGUID(riid, &IID_ISAXLexicalHandler))
1708     {
1709         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1710         *out = iface;
1711     }
1712 
1713     if (*out)
1714         ISAXLexicalHandler_AddRef(iface);
1715     else
1716         return E_NOINTERFACE;
1717 
1718     return S_OK;
1719 }
1720 
1721 static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface)
1722 {
1723     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1724     return InterlockedIncrement(&handler->ref);
1725 }
1726 
1727 static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface)
1728 {
1729     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1730     return InterlockedDecrement(&handler->ref);
1731 }
1732 
1733 static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
1734     const WCHAR * pName, int nName, const WCHAR * pPublicId,
1735     int nPublicId, const WCHAR * pSystemId, int nSystemId)
1736 {
1737     ok(0, "call not expected\n");
1738     return E_NOTIMPL;
1739 }
1740 
1741 static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface)
1742 {
1743     ok(0, "call not expected\n");
1744     return E_NOTIMPL;
1745 }
1746 
1747 static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface,
1748     const WCHAR * pName, int nName)
1749 {
1750     ok(0, "call not expected\n");
1751     return E_NOTIMPL;
1752 }
1753 
1754 static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface,
1755     const WCHAR * pName, int nName)
1756 {
1757     ok(0, "call not expected\n");
1758     return E_NOTIMPL;
1759 }
1760 
1761 static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
1762 {
1763     struct call_entry call;
1764 
1765     init_call_entry(locator, &call);
1766     call.id = LH_STARTCDATA;
1767     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1768 
1769     return get_expected_ret();
1770 }
1771 
1772 static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
1773 {
1774     struct call_entry call;
1775 
1776     init_call_entry(locator, &call);
1777     call.id = LH_ENDCDATA;
1778     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1779 
1780     return get_expected_ret();
1781 }
1782 
1783 static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface,
1784     const WCHAR * pChars, int nChars)
1785 {
1786     ok(0, "call not expected\n");
1787     return E_NOTIMPL;
1788 }
1789 
1790 static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1791 {
1792    isaxlexical_QueryInterface,
1793    isaxlexical_AddRef,
1794    isaxlexical_Release,
1795    isaxlexical_startDTD,
1796    isaxlexical_endDTD,
1797    isaxlexical_startEntity,
1798    isaxlexical_endEntity,
1799    isaxlexical_startCDATA,
1800    isaxlexical_endCDATA,
1801    isaxlexical_comment
1802 };
1803 
1804 static void init_saxlexicalhandler(struct saxlexicalhandler *handler, HRESULT hr)
1805 {
1806     handler->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1807     handler->ref = 1;
1808     handler->qi_hr = hr;
1809 }
1810 
1811 struct saxdeclhandler
1812 {
1813     ISAXDeclHandler ISAXDeclHandler_iface;
1814     LONG ref;
1815 
1816     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1817 };
1818 
1819 static inline struct saxdeclhandler *impl_from_ISAXDeclHandler( ISAXDeclHandler *iface )
1820 {
1821     return CONTAINING_RECORD(iface, struct saxdeclhandler, ISAXDeclHandler_iface);
1822 }
1823 
1824 static HRESULT WINAPI isaxdecl_QueryInterface(ISAXDeclHandler* iface, REFIID riid, void **out)
1825 {
1826     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1827 
1828     *out = NULL;
1829 
1830     if (IsEqualGUID(riid, &IID_IUnknown))
1831     {
1832         *out = iface;
1833         ok(0, "got unexpected IID_IUnknown query\n");
1834     }
1835     else if (IsEqualGUID(riid, &IID_ISAXDeclHandler))
1836     {
1837         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1838         *out = iface;
1839     }
1840 
1841     if (*out)
1842         ISAXDeclHandler_AddRef(iface);
1843     else
1844         return E_NOINTERFACE;
1845 
1846     return S_OK;
1847 }
1848 
1849 static ULONG WINAPI isaxdecl_AddRef(ISAXDeclHandler* iface)
1850 {
1851     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1852     return InterlockedIncrement(&handler->ref);
1853 }
1854 
1855 static ULONG WINAPI isaxdecl_Release(ISAXDeclHandler* iface)
1856 {
1857     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1858     return InterlockedDecrement(&handler->ref);
1859 }
1860 
1861 static HRESULT WINAPI isaxdecl_elementDecl(ISAXDeclHandler* iface,
1862     const WCHAR * pName, int nName, const WCHAR * pModel, int nModel)
1863 {
1864     ok(0, "call not expected\n");
1865     return E_NOTIMPL;
1866 }
1867 
1868 static HRESULT WINAPI isaxdecl_attributeDecl(ISAXDeclHandler* iface,
1869     const WCHAR * pElementName, int nElementName, const WCHAR * pAttributeName,
1870     int nAttributeName, const WCHAR * pType, int nType, const WCHAR * pValueDefault,
1871     int nValueDefault, const WCHAR * pValue, int nValue)
1872 {
1873     ok(0, "call not expected\n");
1874     return E_NOTIMPL;
1875 }
1876 
1877 static HRESULT WINAPI isaxdecl_internalEntityDecl(ISAXDeclHandler* iface,
1878     const WCHAR * pName, int nName, const WCHAR * pValue, int nValue)
1879 {
1880     ok(0, "call not expected\n");
1881     return E_NOTIMPL;
1882 }
1883 
1884 static HRESULT WINAPI isaxdecl_externalEntityDecl(ISAXDeclHandler* iface,
1885     const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId,
1886     const WCHAR * pSystemId, int nSystemId)
1887 {
1888     ok(0, "call not expected\n");
1889     return E_NOTIMPL;
1890 }
1891 
1892 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl =
1893 {
1894    isaxdecl_QueryInterface,
1895    isaxdecl_AddRef,
1896    isaxdecl_Release,
1897    isaxdecl_elementDecl,
1898    isaxdecl_attributeDecl,
1899    isaxdecl_internalEntityDecl,
1900    isaxdecl_externalEntityDecl
1901 };
1902 
1903 static void init_saxdeclhandler(struct saxdeclhandler *handler, HRESULT hr)
1904 {
1905     handler->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1906     handler->ref = 1;
1907     handler->qi_hr = hr;
1908 }
1909 
1910 typedef struct mxwriter_write_test_t {
1911     BOOL        last;
1912     const BYTE  *data;
1913     DWORD       cb;
1914     BOOL        null_written;
1915     BOOL        fail_write;
1916 } mxwriter_write_test;
1917 
1918 typedef struct mxwriter_stream_test_t {
1919     VARIANT_BOOL        bom;
1920     const char          *encoding;
1921     mxwriter_write_test expected_writes[4];
1922 } mxwriter_stream_test;
1923 
1924 static const mxwriter_write_test *current_write_test;
1925 static DWORD current_stream_test_index;
1926 
1927 static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject)
1928 {
1929     *ppvObject = NULL;
1930 
1931     ok(!IsEqualGUID(riid, &IID_IPersistStream), "Did not expect QI for IPersistStream\n");
1932 
1933     if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown))
1934         *ppvObject = iface;
1935     else
1936         return E_NOINTERFACE;
1937 
1938     return S_OK;
1939 }
1940 
1941 static ULONG WINAPI istream_AddRef(IStream *iface)
1942 {
1943     return 2;
1944 }
1945 
1946 static ULONG WINAPI istream_Release(IStream *iface)
1947 {
1948     return 1;
1949 }
1950 
1951 static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
1952 {
1953     ok(0, "unexpected call\n");
1954     return E_NOTIMPL;
1955 }
1956 
1957 static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
1958 {
1959     ok(0, "unexpected call\n");
1960     return E_NOTIMPL;
1961 }
1962 
1963 static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
1964         ULARGE_INTEGER *plibNewPosition)
1965 {
1966     ok(0, "unexpected call\n");
1967     return E_NOTIMPL;
1968 }
1969 
1970 static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
1971 {
1972     ok(0, "unexpected call\n");
1973     return E_NOTIMPL;
1974 }
1975 
1976 static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
1977         ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten)
1978 {
1979     ok(0, "unexpected call\n");
1980     return E_NOTIMPL;
1981 }
1982 
1983 static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags)
1984 {
1985     ok(0, "unexpected call\n");
1986     return E_NOTIMPL;
1987 }
1988 
1989 static HRESULT WINAPI istream_Revert(IStream *iface)
1990 {
1991     ok(0, "unexpected call\n");
1992     return E_NOTIMPL;
1993 }
1994 
1995 static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1996         ULARGE_INTEGER cb, DWORD dwLockType)
1997 {
1998     ok(0, "unexpected call\n");
1999     return E_NOTIMPL;
2000 }
2001 
2002 static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
2003         ULARGE_INTEGER cb, DWORD dwLockType)
2004 {
2005     ok(0, "unexpected call\n");
2006     return E_NOTIMPL;
2007 }
2008 
2009 static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag)
2010 {
2011     return E_NOTIMPL;
2012 }
2013 
2014 static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm)
2015 {
2016     ok(0, "unexpected call\n");
2017     return E_NOTIMPL;
2018 }
2019 
2020 static HRESULT WINAPI mxstream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
2021 {
2022     BOOL fail = FALSE;
2023 
2024     ok(pv != NULL, "pv == NULL\n");
2025 
2026     if(current_write_test->last) {
2027         ok(0, "Too many Write calls made on test %d\n", current_stream_test_index);
2028         return E_FAIL;
2029     }
2030 
2031     fail = current_write_test->fail_write;
2032 
2033     ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n",
2034         current_write_test->cb, cb, current_stream_test_index);
2035 
2036     if(!pcbWritten)
2037         ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index);
2038     else
2039         ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index);
2040 
2041     ++current_write_test;
2042 
2043     if(pcbWritten)
2044         *pcbWritten = cb;
2045 
2046     return fail ? E_FAIL : S_OK;
2047 }
2048 
2049 static const IStreamVtbl mxstreamVtbl = {
2050     istream_QueryInterface,
2051     istream_AddRef,
2052     istream_Release,
2053     istream_Read,
2054     mxstream_Write,
2055     istream_Seek,
2056     istream_SetSize,
2057     istream_CopyTo,
2058     istream_Commit,
2059     istream_Revert,
2060     istream_LockRegion,
2061     istream_UnlockRegion,
2062     istream_Stat,
2063     istream_Clone
2064 };
2065 
2066 static IStream mxstream = { &mxstreamVtbl };
2067 
2068 static int read_cnt;
2069 
2070 static HRESULT WINAPI instream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
2071 {
2072     static const char *ret_str;
2073 
2074     if(!read_cnt)
2075         ret_str = "<?xml version=\"1.0\" ?>\n<rootelem>";
2076     else if(read_cnt < 5)
2077         ret_str = "<elem attr=\"val\">text</elem>";
2078     else if(read_cnt == 5)
2079         ret_str = "</rootelem>\n";
2080     else
2081         ret_str = "";
2082 
2083     read_cnt++;
2084     strcpy(pv, ret_str);
2085     *pcbRead = strlen(ret_str);
2086     return S_OK;
2087 }
2088 
2089 static const IStreamVtbl instreamVtbl = {
2090     istream_QueryInterface,
2091     istream_AddRef,
2092     istream_Release,
2093     instream_Read,
2094     istream_Write,
2095     istream_Seek,
2096     istream_SetSize,
2097     istream_CopyTo,
2098     istream_Commit,
2099     istream_Revert,
2100     istream_LockRegion,
2101     istream_UnlockRegion,
2102     istream_Stat,
2103     istream_Clone
2104 };
2105 
2106 static IStream instream = { &instreamVtbl };
2107 
2108 static struct msxmlsupported_data_t reader_support_data[] =
2109 {
2110     { &CLSID_SAXXMLReader,   "SAXReader"   },
2111     { &CLSID_SAXXMLReader30, "SAXReader30" },
2112     { &CLSID_SAXXMLReader40, "SAXReader40" },
2113     { &CLSID_SAXXMLReader60, "SAXReader60" },
2114     { NULL }
2115 };
2116 
2117 static struct saxlexicalhandler lexicalhandler;
2118 static struct saxdeclhandler declhandler;
2119 
2120 static IStream *create_test_stream(const char *data, int len)
2121 {
2122      ULARGE_INTEGER size;
2123      LARGE_INTEGER pos;
2124      IStream *stream;
2125      ULONG written;
2126 
2127      if (len == -1) len = strlen(data);
2128      CreateStreamOnHGlobal(NULL, TRUE, &stream);
2129      size.QuadPart = len;
2130      IStream_SetSize(stream, size);
2131      IStream_Write(stream, data, len, &written);
2132      pos.QuadPart = 0;
2133      IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2134 
2135      return stream;
2136 }
2137 
2138 static void test_saxreader(void)
2139 {
2140     const struct msxmlsupported_data_t *table = reader_support_data;
2141     HRESULT hr;
2142     ISAXXMLReader *reader = NULL;
2143     VARIANT var;
2144     ISAXContentHandler *content;
2145     ISAXErrorHandler *lpErrorHandler;
2146     SAFEARRAY *sa;
2147     SAFEARRAYBOUND SADim[1];
2148     char *ptr = NULL;
2149     IStream *stream;
2150     ULONG written;
2151     HANDLE file;
2152     static const CHAR testXmlA[] = "test.xml";
2153     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
2154     IXMLDOMDocument *doc;
2155     char seqname[50];
2156     VARIANT_BOOL v;
2157 
2158     while (table->clsid)
2159     {
2160         struct call_entry *test_seq;
2161         ISAXEntityResolver *resolver;
2162         BSTR str;
2163 
2164         if (!is_clsid_supported(table->clsid, reader_support_data))
2165         {
2166             table++;
2167             continue;
2168         }
2169 
2170         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2171         EXPECT_HR(hr, S_OK);
2172         g_reader = reader;
2173 
2174         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2175             msxml_version = 4;
2176         else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2177             msxml_version = 6;
2178         else
2179             msxml_version = 0;
2180 
2181         /* crashes on old versions */
2182         if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) &&
2183             !IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2184         {
2185             hr = ISAXXMLReader_getContentHandler(reader, NULL);
2186             EXPECT_HR(hr, E_POINTER);
2187 
2188             hr = ISAXXMLReader_getErrorHandler(reader, NULL);
2189             EXPECT_HR(hr, E_POINTER);
2190         }
2191 
2192         hr = ISAXXMLReader_getContentHandler(reader, &content);
2193         EXPECT_HR(hr, S_OK);
2194         ok(content == NULL, "Expected %p, got %p\n", NULL, content);
2195 
2196         hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
2197         EXPECT_HR(hr, S_OK);
2198         ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
2199 
2200         hr = ISAXXMLReader_putContentHandler(reader, NULL);
2201         EXPECT_HR(hr, S_OK);
2202 
2203         hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
2204         EXPECT_HR(hr, S_OK);
2205 
2206         hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
2207         EXPECT_HR(hr, S_OK);
2208 
2209         hr = ISAXXMLReader_getContentHandler(reader, &content);
2210         EXPECT_HR(hr, S_OK);
2211         ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content);
2212 
2213         V_VT(&var) = VT_BSTR;
2214         V_BSTR(&var) = SysAllocString(szSimpleXML);
2215 
2216         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2217             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2218             test_seq = content_handler_test1_alternate;
2219         else
2220             test_seq = content_handler_test1;
2221         set_expected_seq(test_seq);
2222         hr = ISAXXMLReader_parse(reader, var);
2223         EXPECT_HR(hr, S_OK);
2224         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE);
2225 
2226         VariantClear(&var);
2227 
2228         SADim[0].lLbound = 0;
2229         SADim[0].cElements = sizeof(testXML)-1;
2230         sa = SafeArrayCreate(VT_UI1, 1, SADim);
2231         SafeArrayAccessData(sa, (void**)&ptr);
2232         memcpy(ptr, testXML, sizeof(testXML)-1);
2233         SafeArrayUnaccessData(sa);
2234         V_VT(&var) = VT_ARRAY|VT_UI1;
2235         V_ARRAY(&var) = sa;
2236 
2237         set_expected_seq(test_seq);
2238         hr = ISAXXMLReader_parse(reader, var);
2239         EXPECT_HR(hr, S_OK);
2240         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE);
2241 
2242         SafeArrayDestroy(sa);
2243 
2244         V_VT(&var) = VT_UNKNOWN;
2245         V_UNKNOWN(&var) = NULL;
2246         hr = ISAXXMLReader_parse(reader, var);
2247         ok(hr == E_INVALIDARG, "got %#x\n", hr);
2248 
2249         V_VT(&var) = VT_DISPATCH;
2250         V_DISPATCH(&var) = NULL;
2251         hr = ISAXXMLReader_parse(reader, var);
2252         ok(hr == E_INVALIDARG, "got %#x\n", hr);
2253 
2254         stream = create_test_stream(testXML, -1);
2255         V_VT(&var) = VT_UNKNOWN;
2256         V_UNKNOWN(&var) = (IUnknown*)stream;
2257 
2258         set_expected_seq(test_seq);
2259         hr = ISAXXMLReader_parse(reader, var);
2260         EXPECT_HR(hr, S_OK);
2261         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE);
2262 
2263         IStream_Release(stream);
2264 
2265         stream = create_test_stream(test_attributes, -1);
2266         V_VT(&var) = VT_UNKNOWN;
2267         V_UNKNOWN(&var) = (IUnknown*)stream;
2268 
2269         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2270             test_seq = content_handler_test_attributes_alternate_4;
2271         else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2272             test_seq = content_handler_test_attributes_alternate_6;
2273         else
2274             test_seq = content_handler_test_attributes;
2275 
2276         set_expected_seq(test_seq);
2277         hr = ISAXXMLReader_parse(reader, var);
2278         EXPECT_HR(hr, S_OK);
2279 
2280         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2281             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2282             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2283         else
2284             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2285 
2286         IStream_Release(stream);
2287 
2288         V_VT(&var) = VT_UNKNOWN;
2289         V_UNKNOWN(&var) = (IUnknown*)&instream;
2290 
2291         test_seq = read_test_seq;
2292         read_cnt = 0;
2293         set_expected_seq(test_seq);
2294         hr = ISAXXMLReader_parse(reader, var);
2295         EXPECT_HR(hr, S_OK);
2296         ok(read_cnt == 7, "read_cnt = %d\n", read_cnt);
2297         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "Read call test", FALSE);
2298 
2299         V_VT(&var) = VT_BSTR;
2300         V_BSTR(&var) = SysAllocString(carriage_ret_test);
2301 
2302         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2303             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2304             test_seq = content_handler_test2_alternate;
2305         else
2306             test_seq = content_handler_test2;
2307 
2308         set_expected_seq(test_seq);
2309         hr = ISAXXMLReader_parse(reader, var);
2310         EXPECT_HR(hr, S_OK);
2311         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE);
2312 
2313         VariantClear(&var);
2314 
2315         /* from file url */
2316         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2317         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2318         WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL);
2319         CloseHandle(file);
2320 
2321         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2322             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2323             test_seq = content_handler_test1_alternate;
2324         else
2325             test_seq = content_handler_test1;
2326         set_expected_seq(test_seq);
2327         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2328         EXPECT_HR(hr, S_OK);
2329         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE);
2330 
2331         /* error handler */
2332         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2333             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2334             test_seq = content_handler_testerror_alternate;
2335         else
2336             test_seq = content_handler_testerror;
2337         set_expected_seq(test_seq);
2338         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2339         EXPECT_HR(hr, E_FAIL);
2340         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE);
2341 
2342         /* callback ret values */
2343         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2344             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2345         {
2346             test_seq = content_handler_test_callback_rets_alt;
2347             set_expected_seq(test_seq);
2348             hr = ISAXXMLReader_parseURL(reader, testXmlW);
2349             EXPECT_HR(hr, S_OK);
2350         }
2351         else
2352         {
2353             test_seq = content_handler_test_callback_rets;
2354             set_expected_seq(test_seq);
2355             hr = ISAXXMLReader_parseURL(reader, testXmlW);
2356             EXPECT_HR(hr, S_FALSE);
2357         }
2358         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE);
2359 
2360         DeleteFileA(testXmlA);
2361 
2362         /* parse from IXMLDOMDocument */
2363         hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
2364                 &IID_IXMLDOMDocument, (void**)&doc);
2365         EXPECT_HR(hr, S_OK);
2366 
2367         str = SysAllocString(szSimpleXML);
2368         hr = IXMLDOMDocument_loadXML(doc, str, &v);
2369         EXPECT_HR(hr, S_OK);
2370         SysFreeString(str);
2371 
2372         V_VT(&var) = VT_UNKNOWN;
2373         V_UNKNOWN(&var) = (IUnknown*)doc;
2374 
2375         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2376             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2377             test_seq = content_handler_test2_alternate;
2378         else
2379             test_seq = content_handler_test2;
2380 
2381         set_expected_seq(test_seq);
2382         hr = ISAXXMLReader_parse(reader, var);
2383         EXPECT_HR(hr, S_OK);
2384         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE);
2385         IXMLDOMDocument_Release(doc);
2386 
2387         /* xml:space test */
2388         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2389             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2390         {
2391             test_seq = xmlspaceattr_test_alternate;
2392         }
2393         else
2394             test_seq = xmlspaceattr_test;
2395 
2396         set_expected_seq(test_seq);
2397         V_VT(&var) = VT_BSTR;
2398         V_BSTR(&var) = _bstr_(xmlspace_attr);
2399         hr = ISAXXMLReader_parse(reader, var);
2400         EXPECT_HR(hr, S_OK);
2401 
2402         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2403             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2404         {
2405             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE);
2406         }
2407         else
2408             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE);
2409 
2410         /* switch off 'namespaces' feature */
2411         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_FALSE);
2412         EXPECT_HR(hr, S_OK);
2413 
2414         stream = create_test_stream(test_attributes, -1);
2415         V_VT(&var) = VT_UNKNOWN;
2416         V_UNKNOWN(&var) = (IUnknown*)stream;
2417 
2418         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2419             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2420         {
2421             test_seq = content_handler_test_attributes_alt_no_ns;
2422         }
2423         else
2424             test_seq = content_handler_test_attributes;
2425 
2426         set_expected_seq(test_seq);
2427         hr = ISAXXMLReader_parse(reader, var);
2428         EXPECT_HR(hr, S_OK);
2429         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2430         IStream_Release(stream);
2431         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_TRUE);
2432         EXPECT_HR(hr, S_OK);
2433 
2434         /* switch off 'namespace-prefixes' feature */
2435         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_FALSE);
2436         EXPECT_HR(hr, S_OK);
2437 
2438         stream = create_test_stream(test_attributes, -1);
2439         V_VT(&var) = VT_UNKNOWN;
2440         V_UNKNOWN(&var) = (IUnknown*)stream;
2441 
2442         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2443             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2444         {
2445             test_seq = content_handler_test_attributes_alt_no_prefix;
2446         }
2447         else
2448             test_seq = content_handler_test_attributes_no_prefix;
2449 
2450         set_expected_seq(test_seq);
2451         hr = ISAXXMLReader_parse(reader, var);
2452         EXPECT_HR(hr, S_OK);
2453         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2454         IStream_Release(stream);
2455 
2456         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_TRUE);
2457         EXPECT_HR(hr, S_OK);
2458 
2459         /* attribute normalization */
2460         stream = create_test_stream(attribute_normalize, -1);
2461         V_VT(&var) = VT_UNKNOWN;
2462         V_UNKNOWN(&var) = (IUnknown*)stream;
2463 
2464         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2465             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2466         {
2467             test_seq = attribute_norm_alt;
2468         }
2469         else
2470             test_seq = attribute_norm;
2471 
2472         set_expected_seq(test_seq);
2473         hr = ISAXXMLReader_parse(reader, var);
2474         EXPECT_HR(hr, S_OK);
2475         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "attribute value normalization", TRUE);
2476         IStream_Release(stream);
2477 
2478         resolver = (void*)0xdeadbeef;
2479         hr = ISAXXMLReader_getEntityResolver(reader, &resolver);
2480         ok(hr == S_OK, "got 0x%08x\n", hr);
2481         ok(resolver == NULL, "got %p\n", resolver);
2482 
2483         hr = ISAXXMLReader_putEntityResolver(reader, NULL);
2484         ok(hr == S_OK || broken(hr == E_FAIL), "got 0x%08x\n", hr);
2485 
2486         /* CDATA sections */
2487         init_saxlexicalhandler(&lexicalhandler, S_OK);
2488 
2489         V_VT(&var) = VT_UNKNOWN;
2490         V_UNKNOWN(&var) = (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface;
2491         hr = ISAXXMLReader_putProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), var);
2492         ok(hr == S_OK, "got 0x%08x\n", hr);
2493 
2494         stream = create_test_stream(test_cdata_xml, -1);
2495         V_VT(&var) = VT_UNKNOWN;
2496         V_UNKNOWN(&var) = (IUnknown*)stream;
2497 
2498         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
2499             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2500             test_seq = cdata_test_alt;
2501         else
2502             test_seq = cdata_test;
2503 
2504         set_expected_seq(test_seq);
2505         hr = ISAXXMLReader_parse(reader, var);
2506         ok(hr == S_OK, "got 0x%08x\n", hr);
2507         sprintf(seqname, "%s: cdata test", table->name);
2508         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
2509 
2510         IStream_Release(stream);
2511 
2512         /* 2. CDATA sections */
2513         stream = create_test_stream(test2_cdata_xml, -1);
2514         V_VT(&var) = VT_UNKNOWN;
2515         V_UNKNOWN(&var) = (IUnknown*)stream;
2516 
2517         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
2518             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2519             test_seq = cdata_test2_alt;
2520         else
2521             test_seq = cdata_test2;
2522 
2523         set_expected_seq(test_seq);
2524         hr = ISAXXMLReader_parse(reader, var);
2525         ok(hr == S_OK, "got 0x%08x\n", hr);
2526         sprintf(seqname, "%s: cdata test 2", table->name);
2527         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
2528 
2529         IStream_Release(stream);
2530 
2531         /* 3. CDATA sections */
2532         stream = create_test_stream(test3_cdata_xml, -1);
2533         V_VT(&var) = VT_UNKNOWN;
2534         V_UNKNOWN(&var) = (IUnknown*)stream;
2535 
2536         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
2537             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2538             test_seq = cdata_test3_alt;
2539         else
2540             test_seq = cdata_test3;
2541 
2542         set_expected_seq(test_seq);
2543         hr = ISAXXMLReader_parse(reader, var);
2544         ok(hr == S_OK, "got 0x%08x\n", hr);
2545         sprintf(seqname, "%s: cdata test 3", table->name);
2546         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
2547 
2548         IStream_Release(stream);
2549 
2550         ISAXXMLReader_Release(reader);
2551         table++;
2552     }
2553 
2554     free_bstrs();
2555 }
2556 
2557 struct saxreader_props_test_t
2558 {
2559     const char *prop_name;
2560     IUnknown   *iface;
2561 };
2562 
2563 static const struct saxreader_props_test_t props_test_data[] = {
2564     { "http://xml.org/sax/properties/lexical-handler", (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface },
2565     { "http://xml.org/sax/properties/declaration-handler", (IUnknown*)&declhandler.ISAXDeclHandler_iface },
2566     { 0 }
2567 };
2568 
2569 static void test_saxreader_properties(void)
2570 {
2571     const struct saxreader_props_test_t *ptr = props_test_data;
2572     ISAXXMLReader *reader;
2573     HRESULT hr;
2574     VARIANT v;
2575     BSTR str;
2576 
2577     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
2578             &IID_ISAXXMLReader, (void**)&reader);
2579     EXPECT_HR(hr, S_OK);
2580 
2581     hr = ISAXXMLReader_getProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), NULL);
2582     EXPECT_HR(hr, E_POINTER);
2583 
2584     while (ptr->prop_name)
2585     {
2586         VARIANT varref;
2587         LONG ref;
2588 
2589         init_saxlexicalhandler(&lexicalhandler, S_OK);
2590         init_saxdeclhandler(&declhandler, S_OK);
2591 
2592         V_VT(&v) = VT_EMPTY;
2593         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2594         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2595         EXPECT_HR(hr, S_OK);
2596         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2597         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2598 
2599         /* VT_UNKNOWN */
2600         V_VT(&v) = VT_UNKNOWN;
2601         V_UNKNOWN(&v) = ptr->iface;
2602         ref = get_refcount(ptr->iface);
2603         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2604         EXPECT_HR(hr, S_OK);
2605         ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2606 
2607         /* VT_DISPATCH */
2608         V_VT(&v) = VT_DISPATCH;
2609         V_UNKNOWN(&v) = ptr->iface;
2610         ref = get_refcount(ptr->iface);
2611         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2612         EXPECT_HR(hr, S_OK);
2613         ok(ref == get_refcount(ptr->iface), "got wrong refcount %d, expected %d\n", get_refcount(ptr->iface), ref);
2614 
2615         /* VT_VARIANT|VT_BYREF with VT_UNKNOWN in referenced variant */
2616         V_VT(&varref) = VT_UNKNOWN;
2617         V_UNKNOWN(&varref) = ptr->iface;
2618 
2619         V_VT(&v) = VT_VARIANT|VT_BYREF;
2620         V_VARIANTREF(&v) = &varref;
2621         ref = get_refcount(ptr->iface);
2622         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2623         EXPECT_HR(hr, S_OK);
2624         ok(ref == get_refcount(ptr->iface), "got wrong refcount %d, expected %d\n", get_refcount(ptr->iface), ref);
2625 
2626         /* VT_VARIANT|VT_BYREF with VT_DISPATCH in referenced variant */
2627         V_VT(&varref) = VT_DISPATCH;
2628         V_UNKNOWN(&varref) = ptr->iface;
2629 
2630         V_VT(&v) = VT_VARIANT|VT_BYREF;
2631         V_VARIANTREF(&v) = &varref;
2632         ref = get_refcount(ptr->iface);
2633         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2634         EXPECT_HR(hr, S_OK);
2635         ok(ref == get_refcount(ptr->iface), "got wrong refcount %d, expected %d\n", get_refcount(ptr->iface), ref);
2636 
2637         V_VT(&v) = VT_EMPTY;
2638         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2639 
2640         ref = get_refcount(ptr->iface);
2641         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2642         EXPECT_HR(hr, S_OK);
2643         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2644         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2645         ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2646         VariantClear(&v);
2647 
2648         V_VT(&v) = VT_EMPTY;
2649         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2650         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2651         EXPECT_HR(hr, S_OK);
2652 
2653         V_VT(&v) = VT_EMPTY;
2654         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2655         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2656         EXPECT_HR(hr, S_OK);
2657         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2658         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2659 
2660         V_VT(&v) = VT_UNKNOWN;
2661         V_UNKNOWN(&v) = ptr->iface;
2662         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2663         EXPECT_HR(hr, S_OK);
2664 
2665         /* only VT_EMPTY seems to be valid to reset property */
2666         V_VT(&v) = VT_I4;
2667         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2668         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2669         EXPECT_HR(hr, E_INVALIDARG);
2670 
2671         V_VT(&v) = VT_EMPTY;
2672         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2673         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2674         EXPECT_HR(hr, S_OK);
2675         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2676         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2677         VariantClear(&v);
2678 
2679         V_VT(&v) = VT_UNKNOWN;
2680         V_UNKNOWN(&v) = NULL;
2681         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2682         EXPECT_HR(hr, S_OK);
2683 
2684         V_VT(&v) = VT_EMPTY;
2685         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2686         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2687         EXPECT_HR(hr, S_OK);
2688         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2689         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2690 
2691         /* block QueryInterface on handler riid */
2692         V_VT(&v) = VT_UNKNOWN;
2693         V_UNKNOWN(&v) = ptr->iface;
2694         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2695         EXPECT_HR(hr, S_OK);
2696 
2697         init_saxlexicalhandler(&lexicalhandler, E_NOINTERFACE);
2698         init_saxdeclhandler(&declhandler, E_NOINTERFACE);
2699 
2700         V_VT(&v) = VT_UNKNOWN;
2701         V_UNKNOWN(&v) = ptr->iface;
2702         EXPECT_REF(ptr->iface, 1);
2703         ref = get_refcount(ptr->iface);
2704         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2705         EXPECT_HR(hr, E_NOINTERFACE);
2706         EXPECT_REF(ptr->iface, 1);
2707 
2708         V_VT(&v) = VT_EMPTY;
2709         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2710         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2711         EXPECT_HR(hr, S_OK);
2712         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2713         ok(V_UNKNOWN(&v) != NULL, "got %p\n", V_UNKNOWN(&v));
2714 
2715         ptr++;
2716         free_bstrs();
2717     }
2718 
2719     ISAXXMLReader_Release(reader);
2720 
2721     if (!is_clsid_supported(&CLSID_SAXXMLReader40, reader_support_data))
2722         return;
2723 
2724     hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER,
2725             &IID_ISAXXMLReader, (void**)&reader);
2726     EXPECT_HR(hr, S_OK);
2727 
2728     /* xmldecl-version property */
2729     V_VT(&v) = VT_EMPTY;
2730     V_BSTR(&v) = (void*)0xdeadbeef;
2731     hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2732     EXPECT_HR(hr, S_OK);
2733     ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2734     ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2735 
2736     /* stream without declaration */
2737     V_VT(&v) = VT_BSTR;
2738     V_BSTR(&v) = _bstr_("<element></element>");
2739     hr = ISAXXMLReader_parse(reader, v);
2740     EXPECT_HR(hr, S_OK);
2741 
2742     V_VT(&v) = VT_EMPTY;
2743     V_BSTR(&v) = (void*)0xdeadbeef;
2744     hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2745     EXPECT_HR(hr, S_OK);
2746     ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2747     ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2748 
2749     /* stream with declaration */
2750     V_VT(&v) = VT_BSTR;
2751     V_BSTR(&v) = _bstr_("<?xml version=\"1.0\"?><element></element>");
2752     hr = ISAXXMLReader_parse(reader, v);
2753     EXPECT_HR(hr, S_OK);
2754 
2755     /* VT_BSTR|VT_BYREF input type */
2756     str = _bstr_("<?xml version=\"1.0\"?><element></element>");
2757     V_VT(&v) = VT_BSTR|VT_BYREF;
2758     V_BSTRREF(&v) = &str;
2759     hr = ISAXXMLReader_parse(reader, v);
2760     EXPECT_HR(hr, S_OK);
2761 
2762     V_VT(&v) = VT_EMPTY;
2763     V_BSTR(&v) = (void*)0xdeadbeef;
2764     hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2765     EXPECT_HR(hr, S_OK);
2766     ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2767     ok(!lstrcmpW(V_BSTR(&v), _bstr_("1.0")), "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2768     VariantClear(&v);
2769 
2770     ISAXXMLReader_Release(reader);
2771     free_bstrs();
2772 }
2773 
2774 struct feature_ns_entry_t {
2775     const GUID *guid;
2776     const char *clsid;
2777     VARIANT_BOOL value;
2778     VARIANT_BOOL value2; /* feature value after feature set to 0xc */
2779 };
2780 
2781 static const struct feature_ns_entry_t feature_ns_entry_data[] = {
2782     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   VARIANT_TRUE, VARIANT_FALSE },
2783     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", VARIANT_TRUE, VARIANT_FALSE },
2784     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", VARIANT_TRUE, VARIANT_TRUE },
2785     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", VARIANT_TRUE, VARIANT_TRUE },
2786     { 0 }
2787 };
2788 
2789 static const char *feature_names[] = {
2790     "http://xml.org/sax/features/namespaces",
2791     "http://xml.org/sax/features/namespace-prefixes",
2792     0
2793 };
2794 
2795 static void test_saxreader_features(void)
2796 {
2797     const struct feature_ns_entry_t *entry = feature_ns_entry_data;
2798     ISAXXMLReader *reader;
2799 
2800     while (entry->guid)
2801     {
2802         VARIANT_BOOL value;
2803         const char **name;
2804         HRESULT hr;
2805 
2806         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2807         if (hr != S_OK)
2808         {
2809             win_skip("can't create %s instance\n", entry->clsid);
2810             entry++;
2811             continue;
2812         }
2813 
2814         if (IsEqualGUID(entry->guid, &CLSID_SAXXMLReader40) ||
2815                 IsEqualGUID(entry->guid, &CLSID_SAXXMLReader60))
2816         {
2817             value = VARIANT_TRUE;
2818             hr = ISAXXMLReader_getFeature(reader, _bstr_("exhaustive-errors"), &value);
2819             ok(hr == S_OK, "Failed to get feature value, hr %#x.\n", hr);
2820             ok(value == VARIANT_FALSE, "Unexpected default feature value.\n");
2821             hr = ISAXXMLReader_putFeature(reader, _bstr_("exhaustive-errors"), VARIANT_FALSE);
2822             ok(hr == S_OK, "Failed to put feature value, hr %#x.\n", hr);
2823 
2824             value = VARIANT_TRUE;
2825             hr = ISAXXMLReader_getFeature(reader, _bstr_("schema-validation"), &value);
2826             ok(hr == S_OK, "Failed to get feature value, hr %#x.\n", hr);
2827             ok(value == VARIANT_FALSE, "Unexpected default feature value.\n");
2828             hr = ISAXXMLReader_putFeature(reader, _bstr_("exhaustive-errors"), VARIANT_FALSE);
2829             ok(hr == S_OK, "Failed to put feature value, hr %#x.\n", hr);
2830         }
2831         else
2832         {
2833             value = 123;
2834             hr = ISAXXMLReader_getFeature(reader, _bstr_("exhaustive-errors"), &value);
2835             ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#x.\n", hr);
2836             ok(value == 123, "Unexpected value %d.\n", value);
2837 
2838             value = 123;
2839             hr = ISAXXMLReader_getFeature(reader, _bstr_("schema-validation"), &value);
2840             ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#x.\n", hr);
2841             ok(value == 123, "Unexpected value %d.\n", value);
2842         }
2843 
2844         name = feature_names;
2845         while (*name)
2846         {
2847             value = 0xc;
2848             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2849             EXPECT_HR(hr, S_OK);
2850             ok(entry->value == value, "%s: got wrong default value %x, expected %x\n", entry->clsid, value, entry->value);
2851 
2852             value = 0xc;
2853             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), value);
2854             EXPECT_HR(hr, S_OK);
2855 
2856             value = 0xd;
2857             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2858             EXPECT_HR(hr, S_OK);
2859             ok(entry->value2 == value, "%s: got wrong value %x, expected %x\n", entry->clsid, value, entry->value2);
2860 
2861             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_FALSE);
2862             EXPECT_HR(hr, S_OK);
2863             value = 0xd;
2864             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2865             EXPECT_HR(hr, S_OK);
2866             ok(value == VARIANT_FALSE, "%s: got wrong value %x, expected VARIANT_FALSE\n", entry->clsid, value);
2867 
2868             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_TRUE);
2869             EXPECT_HR(hr, S_OK);
2870             value = 0xd;
2871             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2872             EXPECT_HR(hr, S_OK);
2873             ok(value == VARIANT_TRUE, "%s: got wrong value %x, expected VARIANT_TRUE\n", entry->clsid, value);
2874 
2875             name++;
2876         }
2877 
2878         ISAXXMLReader_Release(reader);
2879 
2880         entry++;
2881     }
2882 }
2883 
2884 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
2885 static const CHAR UTF8BOMTest[] =
2886 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
2887 "<a></a>\n";
2888 
2889 struct enc_test_entry_t {
2890     const GUID *guid;
2891     const char *clsid;
2892     const char *data;
2893     HRESULT hr;
2894     BOOL todo;
2895 };
2896 
2897 static const struct enc_test_entry_t encoding_test_data[] = {
2898     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   UTF8BOMTest, 0xc00ce56f, TRUE },
2899     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, TRUE },
2900     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, FALSE },
2901     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, FALSE },
2902     { 0 }
2903 };
2904 
2905 static void test_saxreader_encoding(void)
2906 {
2907     const struct enc_test_entry_t *entry = encoding_test_data;
2908     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
2909     static const CHAR testXmlA[] = "test.xml";
2910 
2911     while (entry->guid)
2912     {
2913         ISAXXMLReader *reader;
2914         VARIANT input;
2915         DWORD written;
2916         HANDLE file;
2917         HRESULT hr;
2918 
2919         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2920         if (hr != S_OK)
2921         {
2922             win_skip("can't create %s instance\n", entry->clsid);
2923             entry++;
2924             continue;
2925         }
2926 
2927         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2928         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2929         WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
2930         CloseHandle(file);
2931 
2932         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2933         todo_wine_if(entry->todo)
2934             ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
2935 
2936         DeleteFileA(testXmlA);
2937 
2938         /* try BSTR input with no BOM or '<?xml' instruction */
2939         V_VT(&input) = VT_BSTR;
2940         V_BSTR(&input) = _bstr_("<element></element>");
2941         hr = ISAXXMLReader_parse(reader, input);
2942         EXPECT_HR(hr, S_OK);
2943 
2944         ISAXXMLReader_Release(reader);
2945 
2946         free_bstrs();
2947         entry++;
2948     }
2949 }
2950 
2951 static void test_mxwriter_handlers(void)
2952 {
2953     IMXWriter *writer;
2954     HRESULT hr;
2955     int i;
2956 
2957     static REFIID riids[] =
2958     {
2959         &IID_ISAXContentHandler,
2960         &IID_ISAXLexicalHandler,
2961         &IID_ISAXDeclHandler,
2962         &IID_ISAXDTDHandler,
2963         &IID_ISAXErrorHandler,
2964         &IID_IVBSAXDeclHandler,
2965         &IID_IVBSAXLexicalHandler,
2966         &IID_IVBSAXContentHandler,
2967         &IID_IVBSAXDTDHandler,
2968         &IID_IVBSAXErrorHandler
2969     };
2970 
2971     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2972             &IID_IMXWriter, (void**)&writer);
2973     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2974 
2975     EXPECT_REF(writer, 1);
2976 
2977     for (i = 0; i < ARRAY_SIZE(riids); i++)
2978     {
2979         IUnknown *handler;
2980         IMXWriter *writer2;
2981 
2982         /* handler from IMXWriter */
2983         hr = IMXWriter_QueryInterface(writer, riids[i], (void**)&handler);
2984         ok(hr == S_OK, "%s, expected S_OK, got %08x\n", wine_dbgstr_guid(riids[i]), hr);
2985         EXPECT_REF(writer, 2);
2986         EXPECT_REF(handler, 2);
2987 
2988         /* IMXWriter from a handler */
2989         hr = IUnknown_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
2990         ok(hr == S_OK, "%s, expected S_OK, got %08x\n", wine_dbgstr_guid(riids[i]), hr);
2991         ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2992         EXPECT_REF(writer, 3);
2993         IMXWriter_Release(writer2);
2994         IUnknown_Release(handler);
2995     }
2996 
2997     IMXWriter_Release(writer);
2998 }
2999 
3000 static struct msxmlsupported_data_t mxwriter_support_data[] =
3001 {
3002     { &CLSID_MXXMLWriter,   "MXXMLWriter"   },
3003     { &CLSID_MXXMLWriter30, "MXXMLWriter30" },
3004     { &CLSID_MXXMLWriter40, "MXXMLWriter40" },
3005     { &CLSID_MXXMLWriter60, "MXXMLWriter60" },
3006     { NULL }
3007 };
3008 
3009 static struct msxmlsupported_data_t mxattributes_support_data[] =
3010 {
3011     { &CLSID_SAXAttributes,   "SAXAttributes"   },
3012     { &CLSID_SAXAttributes30, "SAXAttributes30" },
3013     { &CLSID_SAXAttributes40, "SAXAttributes40" },
3014     { &CLSID_SAXAttributes60, "SAXAttributes60" },
3015     { NULL }
3016 };
3017 
3018 struct mxwriter_props_t
3019 {
3020     const GUID *clsid;
3021     VARIANT_BOOL bom;
3022     VARIANT_BOOL disable_escape;
3023     VARIANT_BOOL indent;
3024     VARIANT_BOOL omitdecl;
3025     VARIANT_BOOL standalone;
3026     const char *encoding;
3027 };
3028 
3029 static const struct mxwriter_props_t mxwriter_default_props[] =
3030 {
3031     { &CLSID_MXXMLWriter,   VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
3032     { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
3033     { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
3034     { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
3035     { NULL }
3036 };
3037 
3038 static void test_mxwriter_default_properties(const struct mxwriter_props_t *table)
3039 {
3040     int i = 0;
3041 
3042     while (table->clsid)
3043     {
3044         IMXWriter *writer;
3045         VARIANT_BOOL b;
3046         BSTR encoding;
3047         HRESULT hr;
3048 
3049         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3050         {
3051             table++;
3052             i++;
3053             continue;
3054         }
3055 
3056         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3057             &IID_IMXWriter, (void**)&writer);
3058         EXPECT_HR(hr, S_OK);
3059 
3060         b = !table->bom;
3061         hr = IMXWriter_get_byteOrderMark(writer, &b);
3062         EXPECT_HR(hr, S_OK);
3063         ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom);
3064 
3065         b = !table->disable_escape;
3066         hr = IMXWriter_get_disableOutputEscaping(writer, &b);
3067         EXPECT_HR(hr, S_OK);
3068         ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b,
3069            table->disable_escape);
3070 
3071         b = !table->indent;
3072         hr = IMXWriter_get_indent(writer, &b);
3073         EXPECT_HR(hr, S_OK);
3074         ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent);
3075 
3076         b = !table->omitdecl;
3077         hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
3078         EXPECT_HR(hr, S_OK);
3079         ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl);
3080 
3081         b = !table->standalone;
3082         hr = IMXWriter_get_standalone(writer, &b);
3083         EXPECT_HR(hr, S_OK);
3084         ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone);
3085 
3086         hr = IMXWriter_get_encoding(writer, &encoding);
3087         EXPECT_HR(hr, S_OK);
3088         ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n",
3089             i, wine_dbgstr_w(encoding), table->encoding);
3090         SysFreeString(encoding);
3091 
3092         IMXWriter_Release(writer);
3093 
3094         table++;
3095         i++;
3096     }
3097 }
3098 
3099 static void test_mxwriter_properties(void)
3100 {
3101     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
3102     static const WCHAR testW[] = {'t','e','s','t',0};
3103     ISAXContentHandler *content;
3104     IMXWriter *writer;
3105     VARIANT_BOOL b;
3106     HRESULT hr;
3107     BSTR str, str2;
3108     VARIANT dest;
3109 
3110     test_mxwriter_default_properties(mxwriter_default_props);
3111 
3112     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3113             &IID_IMXWriter, (void**)&writer);
3114     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3115 
3116     hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
3117     ok(hr == E_POINTER, "got %08x\n", hr);
3118 
3119     hr = IMXWriter_get_byteOrderMark(writer, NULL);
3120     ok(hr == E_POINTER, "got %08x\n", hr);
3121 
3122     hr = IMXWriter_get_indent(writer, NULL);
3123     ok(hr == E_POINTER, "got %08x\n", hr);
3124 
3125     hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
3126     ok(hr == E_POINTER, "got %08x\n", hr);
3127 
3128     hr = IMXWriter_get_standalone(writer, NULL);
3129     ok(hr == E_POINTER, "got %08x\n", hr);
3130 
3131     /* set and check */
3132     hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
3133     ok(hr == S_OK, "got %08x\n", hr);
3134 
3135     b = VARIANT_FALSE;
3136     hr = IMXWriter_get_standalone(writer, &b);
3137     ok(hr == S_OK, "got %08x\n", hr);
3138     ok(b == VARIANT_TRUE, "got %d\n", b);
3139 
3140     hr = IMXWriter_get_encoding(writer, NULL);
3141     EXPECT_HR(hr, E_POINTER);
3142 
3143     /* UTF-16 is a default setting apparently */
3144     str = (void*)0xdeadbeef;
3145     hr = IMXWriter_get_encoding(writer, &str);
3146     EXPECT_HR(hr, S_OK);
3147     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
3148 
3149     str2 = (void*)0xdeadbeef;
3150     hr = IMXWriter_get_encoding(writer, &str2);
3151     ok(hr == S_OK, "got %08x\n", hr);
3152     ok(str != str2, "expected newly allocated, got same %p\n", str);
3153 
3154     SysFreeString(str2);
3155     SysFreeString(str);
3156 
3157     /* put empty string */
3158     str = SysAllocString(emptyW);
3159     hr = IMXWriter_put_encoding(writer, str);
3160     ok(hr == E_INVALIDARG, "got %08x\n", hr);
3161     SysFreeString(str);
3162 
3163     str = (void*)0xdeadbeef;
3164     hr = IMXWriter_get_encoding(writer, &str);
3165     EXPECT_HR(hr, S_OK);
3166     ok(!lstrcmpW(str, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(str));
3167     SysFreeString(str);
3168 
3169     /* invalid encoding name */
3170     str = SysAllocString(testW);
3171     hr = IMXWriter_put_encoding(writer, str);
3172     ok(hr == E_INVALIDARG, "got %08x\n", hr);
3173     SysFreeString(str);
3174 
3175     /* test case sensivity */
3176     hr = IMXWriter_put_encoding(writer, _bstr_("utf-8"));
3177     EXPECT_HR(hr, S_OK);
3178     str = (void*)0xdeadbeef;
3179     hr = IMXWriter_get_encoding(writer, &str);
3180     EXPECT_HR(hr, S_OK);
3181     ok(!lstrcmpW(str, _bstr_("utf-8")), "got %s\n", wine_dbgstr_w(str));
3182     SysFreeString(str);
3183 
3184     hr = IMXWriter_put_encoding(writer, _bstr_("uTf-16"));
3185     EXPECT_HR(hr, S_OK);
3186     str = (void*)0xdeadbeef;
3187     hr = IMXWriter_get_encoding(writer, &str);
3188     EXPECT_HR(hr, S_OK);
3189     ok(!lstrcmpW(str, _bstr_("uTf-16")), "got %s\n", wine_dbgstr_w(str));
3190     SysFreeString(str);
3191 
3192     /* how it affects document creation */
3193     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3194     EXPECT_HR(hr, S_OK);
3195 
3196     hr = ISAXContentHandler_startDocument(content);
3197     EXPECT_HR(hr, S_OK);
3198     hr = ISAXContentHandler_endDocument(content);
3199     EXPECT_HR(hr, S_OK);
3200 
3201     V_VT(&dest) = VT_EMPTY;
3202     hr = IMXWriter_get_output(writer, &dest);
3203     EXPECT_HR(hr, S_OK);
3204     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3205     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>\r\n"),
3206         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3207     VariantClear(&dest);
3208     ISAXContentHandler_Release(content);
3209 
3210     hr = IMXWriter_get_version(writer, NULL);
3211     ok(hr == E_POINTER, "got %08x\n", hr);
3212     /* default version is 'surprisingly' 1.0 */
3213     hr = IMXWriter_get_version(writer, &str);
3214     ok(hr == S_OK, "got %08x\n", hr);
3215     ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
3216     SysFreeString(str);
3217 
3218     /* store version string as is */
3219     hr = IMXWriter_put_version(writer, NULL);
3220     ok(hr == E_INVALIDARG, "got %08x\n", hr);
3221 
3222     hr = IMXWriter_put_version(writer, _bstr_("1.0"));
3223     ok(hr == S_OK, "got %08x\n", hr);
3224 
3225     hr = IMXWriter_put_version(writer, _bstr_(""));
3226     ok(hr == S_OK, "got %08x\n", hr);
3227     hr = IMXWriter_get_version(writer, &str);
3228     ok(hr == S_OK, "got %08x\n", hr);
3229     ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
3230     SysFreeString(str);
3231 
3232     hr = IMXWriter_put_version(writer, _bstr_("a.b"));
3233     ok(hr == S_OK, "got %08x\n", hr);
3234     hr = IMXWriter_get_version(writer, &str);
3235     ok(hr == S_OK, "got %08x\n", hr);
3236     ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
3237     SysFreeString(str);
3238 
3239     hr = IMXWriter_put_version(writer, _bstr_("2.0"));
3240     ok(hr == S_OK, "got %08x\n", hr);
3241     hr = IMXWriter_get_version(writer, &str);
3242     ok(hr == S_OK, "got %08x\n", hr);
3243     ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
3244     SysFreeString(str);
3245 
3246     IMXWriter_Release(writer);
3247     free_bstrs();
3248 }
3249 
3250 static void test_mxwriter_flush(void)
3251 {
3252     ISAXContentHandler *content;
3253     IMXWriter *writer;
3254     LARGE_INTEGER pos;
3255     ULARGE_INTEGER pos2;
3256     IStream *stream;
3257     VARIANT dest;
3258     HRESULT hr;
3259     char *buff;
3260     LONG ref;
3261     int len;
3262 
3263     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3264             &IID_IMXWriter, (void**)&writer);
3265     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3266 
3267     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3268     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3269     EXPECT_REF(stream, 1);
3270 
3271     /* detach when nothing was attached */
3272     V_VT(&dest) = VT_EMPTY;
3273     hr = IMXWriter_put_output(writer, dest);
3274     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3275 
3276     /* attach stream */
3277     V_VT(&dest) = VT_UNKNOWN;
3278     V_UNKNOWN(&dest) = (IUnknown*)stream;
3279     hr = IMXWriter_put_output(writer, dest);
3280     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3281     todo_wine EXPECT_REF(stream, 3);
3282 
3283     /* detach setting VT_EMPTY destination */
3284     V_VT(&dest) = VT_EMPTY;
3285     hr = IMXWriter_put_output(writer, dest);
3286     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3287     EXPECT_REF(stream, 1);
3288 
3289     V_VT(&dest) = VT_UNKNOWN;
3290     V_UNKNOWN(&dest) = (IUnknown*)stream;
3291     hr = IMXWriter_put_output(writer, dest);
3292     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3293 
3294     /* flush() doesn't detach a stream */
3295     hr = IMXWriter_flush(writer);
3296     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3297     todo_wine EXPECT_REF(stream, 3);
3298 
3299     pos.QuadPart = 0;
3300     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3301     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3302     ok(pos2.QuadPart == 0, "expected stream beginning\n");
3303 
3304     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3305     ok(hr == S_OK, "got %08x\n", hr);
3306 
3307     hr = ISAXContentHandler_startDocument(content);
3308     ok(hr == S_OK, "got %08x\n", hr);
3309 
3310     pos.QuadPart = 0;
3311     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3312     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3313     ok(pos2.QuadPart != 0, "expected stream beginning\n");
3314 
3315     /* already started */
3316     hr = ISAXContentHandler_startDocument(content);
3317     ok(hr == S_OK, "got %08x\n", hr);
3318 
3319     hr = ISAXContentHandler_endDocument(content);
3320     ok(hr == S_OK, "got %08x\n", hr);
3321 
3322     /* flushed on endDocument() */
3323     pos.QuadPart = 0;
3324     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3325     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3326     ok(pos2.QuadPart != 0, "expected stream position moved\n");
3327 
3328     IStream_Release(stream);
3329 
3330     /* auto-flush feature */
3331     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3332     EXPECT_HR(hr, S_OK);
3333     EXPECT_REF(stream, 1);
3334 
3335     V_VT(&dest) = VT_UNKNOWN;
3336     V_UNKNOWN(&dest) = (IUnknown*)stream;
3337     hr = IMXWriter_put_output(writer, dest);
3338     EXPECT_HR(hr, S_OK);
3339 
3340     hr = IMXWriter_put_byteOrderMark(writer, VARIANT_FALSE);
3341     EXPECT_HR(hr, S_OK);
3342 
3343     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3344     EXPECT_HR(hr, S_OK);
3345 
3346     hr = ISAXContentHandler_startDocument(content);
3347     EXPECT_HR(hr, S_OK);
3348 
3349     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
3350     EXPECT_HR(hr, S_OK);
3351 
3352     /* internal buffer is flushed automatically on certain threshold */
3353     pos.QuadPart = 0;
3354     pos2.QuadPart = 1;
3355     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3356     EXPECT_HR(hr, S_OK);
3357     ok(pos2.QuadPart == 0, "expected stream beginning\n");
3358 
3359     len = 2048;
3360     buff = heap_alloc(len + 1);
3361     memset(buff, 'A', len);
3362     buff[len] = 0;
3363     hr = ISAXContentHandler_characters(content, _bstr_(buff), len);
3364     EXPECT_HR(hr, S_OK);
3365 
3366     pos.QuadPart = 0;
3367     pos2.QuadPart = 0;
3368     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3369     EXPECT_HR(hr, S_OK);
3370     ok(pos2.QuadPart != 0, "unexpected stream beginning\n");
3371 
3372     hr = IMXWriter_get_output(writer, NULL);
3373     EXPECT_HR(hr, E_POINTER);
3374 
3375     ref = get_refcount(stream);
3376     V_VT(&dest) = VT_EMPTY;
3377     hr = IMXWriter_get_output(writer, &dest);
3378     EXPECT_HR(hr, S_OK);
3379     ok(V_VT(&dest) == VT_UNKNOWN, "got vt type %d\n", V_VT(&dest));
3380     ok(V_UNKNOWN(&dest) == (IUnknown*)stream, "got pointer %p\n", V_UNKNOWN(&dest));
3381     ok(ref+1 == get_refcount(stream), "expected increased refcount\n");
3382     VariantClear(&dest);
3383 
3384     hr = ISAXContentHandler_endDocument(content);
3385     EXPECT_HR(hr, S_OK);
3386 
3387     IStream_Release(stream);
3388 
3389     /* test char count lower than threshold */
3390     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3391     EXPECT_HR(hr, S_OK);
3392     EXPECT_REF(stream, 1);
3393 
3394     hr = ISAXContentHandler_startDocument(content);
3395     EXPECT_HR(hr, S_OK);
3396 
3397     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
3398     EXPECT_HR(hr, S_OK);
3399 
3400     pos.QuadPart = 0;
3401     pos2.QuadPart = 1;
3402     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3403     EXPECT_HR(hr, S_OK);
3404     ok(pos2.QuadPart == 0, "expected stream beginning\n");
3405 
3406     memset(buff, 'A', len);
3407     buff[len] = 0;
3408     hr = ISAXContentHandler_characters(content, _bstr_(buff), len - 8);
3409     EXPECT_HR(hr, S_OK);
3410 
3411     pos.QuadPart = 0;
3412     pos2.QuadPart = 1;
3413     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3414     EXPECT_HR(hr, S_OK);
3415     ok(pos2.QuadPart == 0, "expected stream beginning\n");
3416 
3417     hr = ISAXContentHandler_endDocument(content);
3418     EXPECT_HR(hr, S_OK);
3419 
3420     /* test auto-flush function when stream is not set */
3421     V_VT(&dest) = VT_EMPTY;
3422     hr = IMXWriter_put_output(writer, dest);
3423     EXPECT_HR(hr, S_OK);
3424 
3425     hr = ISAXContentHandler_startDocument(content);
3426     EXPECT_HR(hr, S_OK);
3427 
3428     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
3429     EXPECT_HR(hr, S_OK);
3430 
3431     memset(buff, 'A', len);
3432     buff[len] = 0;
3433     hr = ISAXContentHandler_characters(content, _bstr_(buff), len);
3434     EXPECT_HR(hr, S_OK);
3435 
3436     V_VT(&dest) = VT_EMPTY;
3437     hr = IMXWriter_get_output(writer, &dest);
3438     EXPECT_HR(hr, S_OK);
3439     len += strlen("<a>");
3440     ok(SysStringLen(V_BSTR(&dest)) == len, "got len=%d, expected %d\n", SysStringLen(V_BSTR(&dest)), len);
3441     VariantClear(&dest);
3442 
3443     heap_free(buff);
3444     ISAXContentHandler_Release(content);
3445     IStream_Release(stream);
3446     IMXWriter_Release(writer);
3447     free_bstrs();
3448 }
3449 
3450 static void test_mxwriter_startenddocument(void)
3451 {
3452     ISAXContentHandler *content;
3453     IMXWriter *writer;
3454     VARIANT dest;
3455     HRESULT hr;
3456 
3457     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3458             &IID_IMXWriter, (void**)&writer);
3459     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3460 
3461     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3462     ok(hr == S_OK, "got %08x\n", hr);
3463 
3464     hr = ISAXContentHandler_startDocument(content);
3465     ok(hr == S_OK, "got %08x\n", hr);
3466 
3467     hr = ISAXContentHandler_endDocument(content);
3468     ok(hr == S_OK, "got %08x\n", hr);
3469 
3470     V_VT(&dest) = VT_EMPTY;
3471     hr = IMXWriter_get_output(writer, &dest);
3472     ok(hr == S_OK, "got %08x\n", hr);
3473     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3474     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3475         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3476     VariantClear(&dest);
3477 
3478     /* now try another startDocument */
3479     hr = ISAXContentHandler_startDocument(content);
3480     ok(hr == S_OK, "got %08x\n", hr);
3481     /* and get duplicated prolog */
3482     V_VT(&dest) = VT_EMPTY;
3483     hr = IMXWriter_get_output(writer, &dest);
3484     ok(hr == S_OK, "got %08x\n", hr);
3485     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3486     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
3487                         "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3488         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3489     VariantClear(&dest);
3490 
3491     ISAXContentHandler_Release(content);
3492     IMXWriter_Release(writer);
3493 
3494     /* now with omitted declaration */
3495     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3496             &IID_IMXWriter, (void**)&writer);
3497     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3498 
3499     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3500     ok(hr == S_OK, "got %08x\n", hr);
3501 
3502     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3503     ok(hr == S_OK, "got %08x\n", hr);
3504 
3505     hr = ISAXContentHandler_startDocument(content);
3506     ok(hr == S_OK, "got %08x\n", hr);
3507 
3508     hr = ISAXContentHandler_endDocument(content);
3509     ok(hr == S_OK, "got %08x\n", hr);
3510 
3511     V_VT(&dest) = VT_EMPTY;
3512     hr = IMXWriter_get_output(writer, &dest);
3513     ok(hr == S_OK, "got %08x\n", hr);
3514     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3515     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3516     VariantClear(&dest);
3517 
3518     ISAXContentHandler_Release(content);
3519     IMXWriter_Release(writer);
3520 
3521     free_bstrs();
3522 }
3523 
3524 enum startendtype
3525 {
3526     StartElement    = 0x001,
3527     EndElement      = 0x010,
3528     StartEndElement = 0x011,
3529     DisableEscaping = 0x100
3530 };
3531 
3532 struct writer_startendelement_t {
3533     const GUID *clsid;
3534     enum startendtype type;
3535     const char *uri;
3536     const char *local_name;
3537     const char *qname;
3538     const char *output;
3539     HRESULT hr;
3540     ISAXAttributes *attr;
3541 };
3542 
3543 static const char startelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\'\">";
3544 static const char startendelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\'\"/>";
3545 static const char startendelement_noescape_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&\">\'\"/>";
3546 
3547 static const struct writer_startendelement_t writer_startendelement[] = {
3548     /* 0 */
3549     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3550     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3551     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3552     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK },
3553     { &CLSID_MXXMLWriter,   StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3554     /* 5 */
3555     { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3556     { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3557     { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK },
3558     { &CLSID_MXXMLWriter,   StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3559     { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3560     /* 10 */
3561     { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3562     { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK },
3563     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3564     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3565     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3566     /* 15 */
3567     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "<qname>", S_OK },
3568     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "qname", "<qname>", S_OK },
3569     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3570     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3571     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3572     /* 20 */
3573     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3574     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3575     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3576     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK },
3577     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3578     /* 25 */
3579     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3580     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3581     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3582     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3583     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3584     /* 30 */
3585     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3586     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3587     /* endElement tests */
3588     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3589     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3590     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3591     /* 35 */
3592     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "</>", S_OK },
3593     { &CLSID_MXXMLWriter,   EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3594     { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3595     { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3596     { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "</>", S_OK },
3597     /* 40 */
3598     { &CLSID_MXXMLWriter,   EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3599     { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3600     { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3601     { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "</>", S_OK },
3602     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3603     /* 45 */
3604     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3605     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3606     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "</qname>", S_OK },
3607     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "qname", "</qname>", S_OK },
3608     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3609     /* 50 */
3610     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3611     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3612     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3613     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3614     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3615     /* 55 */
3616     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "</>", S_OK },
3617     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3618     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3619     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3620     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3621     /* 60 */
3622     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3623     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3624     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3625     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3626 
3627     /* with attributes */
3628     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3629     /* 65 */
3630     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3631     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3632     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3633     /* empty elements */
3634     { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3635     { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3636     /* 70 */
3637     { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3638     { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3639     { &CLSID_MXXMLWriter,   StartEndElement, "", "", "", "</>", S_OK },
3640     { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
3641     { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
3642     /* 75 */
3643     { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "</>", S_OK },
3644 
3645     /* with disabled output escaping */
3646     { &CLSID_MXXMLWriter,   StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3647     { &CLSID_MXXMLWriter30, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3648     { &CLSID_MXXMLWriter40, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3649     { &CLSID_MXXMLWriter60, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3650 
3651     { NULL }
3652 };
3653 
3654 static void get_class_support_data(struct msxmlsupported_data_t *table, REFIID riid)
3655 {
3656     while (table->clsid)
3657     {
3658         IUnknown *unk;
3659         HRESULT hr;
3660 
3661         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, riid, (void**)&unk);
3662         if (hr == S_OK) IUnknown_Release(unk);
3663 
3664         table->supported = hr == S_OK;
3665         if (hr != S_OK) win_skip("class %s not supported\n", table->name);
3666 
3667         table++;
3668     }
3669 }
3670 
3671 static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table)
3672 {
3673     int i = 0;
3674 
3675     while (table->clsid)
3676     {
3677         ISAXContentHandler *content;
3678         IMXWriter *writer;
3679         HRESULT hr;
3680 
3681         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3682         {
3683             table++;
3684             i++;
3685             continue;
3686         }
3687 
3688         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3689             &IID_IMXWriter, (void**)&writer);
3690         EXPECT_HR(hr, S_OK);
3691 
3692         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3693         EXPECT_HR(hr, S_OK);
3694 
3695         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3696         EXPECT_HR(hr, S_OK);
3697 
3698         hr = ISAXContentHandler_startDocument(content);
3699         EXPECT_HR(hr, S_OK);
3700 
3701         if (table->type & DisableEscaping)
3702         {
3703             hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
3704             EXPECT_HR(hr, S_OK);
3705         }
3706 
3707         if (table->type & StartElement)
3708         {
3709             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), table->uri ? strlen(table->uri) : 0,
3710                 _bstr_(table->local_name), table->local_name ? strlen(table->local_name) : 0, _bstr_(table->qname),
3711                 table->qname ? strlen(table->qname) : 0, table->attr);
3712             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3713         }
3714 
3715         if (table->type & EndElement)
3716         {
3717             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), table->uri ? strlen(table->uri) : 0,
3718                 _bstr_(table->local_name), table->local_name ? strlen(table->local_name) : 0, _bstr_(table->qname),
3719                 table->qname ? strlen(table->qname) : 0);
3720             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3721         }
3722 
3723         /* test output */
3724         if (hr == S_OK)
3725         {
3726             VARIANT dest;
3727 
3728             V_VT(&dest) = VT_EMPTY;
3729             hr = IMXWriter_get_output(writer, &dest);
3730             EXPECT_HR(hr, S_OK);
3731             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3732             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3733                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3734             VariantClear(&dest);
3735         }
3736 
3737         ISAXContentHandler_Release(content);
3738         IMXWriter_Release(writer);
3739 
3740         table++;
3741         i++;
3742     }
3743 
3744     free_bstrs();
3745 }
3746 
3747 /* point of these test is to start/end element with different names and name lengths */
3748 struct writer_startendelement2_t {
3749     const GUID *clsid;
3750     const char *qnamestart;
3751     int qnamestart_len;
3752     const char *qnameend;
3753     int qnameend_len;
3754     const char *output;
3755     HRESULT hr;
3756 };
3757 
3758 static const struct writer_startendelement2_t writer_startendelement2[] = {
3759     { &CLSID_MXXMLWriter,   "a", -1, "b", -1, "<a/>", S_OK },
3760     { &CLSID_MXXMLWriter30, "a", -1, "b", -1, "<a/>", S_OK },
3761     { &CLSID_MXXMLWriter40, "a", -1, "b", -1, "<a/>", S_OK },
3762     /* -1 length is not allowed for version 6 */
3763     { &CLSID_MXXMLWriter60, "a", -1, "b", -1, "<a/>", E_INVALIDARG },
3764 
3765     { &CLSID_MXXMLWriter,   "a", 1, "b", 1, "<a/>", S_OK },
3766     { &CLSID_MXXMLWriter30, "a", 1, "b", 1, "<a/>", S_OK },
3767     { &CLSID_MXXMLWriter40, "a", 1, "b", 1, "<a/>", S_OK },
3768     { &CLSID_MXXMLWriter60, "a", 1, "b", 1, "<a/>", S_OK },
3769     { NULL }
3770 };
3771 
3772 static void test_mxwriter_startendelement_batch2(const struct writer_startendelement2_t *table)
3773 {
3774     int i = 0;
3775 
3776     while (table->clsid)
3777     {
3778         ISAXContentHandler *content;
3779         IMXWriter *writer;
3780         HRESULT hr;
3781 
3782         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3783         {
3784             table++;
3785             i++;
3786             continue;
3787         }
3788 
3789         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3790             &IID_IMXWriter, (void**)&writer);
3791         EXPECT_HR(hr, S_OK);
3792 
3793         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3794         EXPECT_HR(hr, S_OK);
3795 
3796         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3797         EXPECT_HR(hr, S_OK);
3798 
3799         hr = ISAXContentHandler_startDocument(content);
3800         EXPECT_HR(hr, S_OK);
3801 
3802         hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0,
3803             _bstr_(table->qnamestart), table->qnamestart_len, NULL);
3804         ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3805 
3806         hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0,
3807             _bstr_(table->qnameend), table->qnameend_len);
3808         ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3809 
3810         /* test output */
3811         if (hr == S_OK)
3812         {
3813             VARIANT dest;
3814 
3815             V_VT(&dest) = VT_EMPTY;
3816             hr = IMXWriter_get_output(writer, &dest);
3817             EXPECT_HR(hr, S_OK);
3818             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3819             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3820                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3821             VariantClear(&dest);
3822         }
3823 
3824         ISAXContentHandler_Release(content);
3825         IMXWriter_Release(writer);
3826 
3827         table++;
3828         i++;
3829 
3830         free_bstrs();
3831     }
3832 }
3833 
3834 
3835 static void test_mxwriter_startendelement(void)
3836 {
3837     ISAXContentHandler *content;
3838     IMXWriter *writer;
3839     VARIANT dest;
3840     HRESULT hr;
3841 
3842     test_mxwriter_startendelement_batch(writer_startendelement);
3843     test_mxwriter_startendelement_batch2(writer_startendelement2);
3844 
3845     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3846             &IID_IMXWriter, (void**)&writer);
3847     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3848 
3849     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3850     ok(hr == S_OK, "got %08x\n", hr);
3851 
3852     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3853     ok(hr == S_OK, "got %08x\n", hr);
3854 
3855     hr = ISAXContentHandler_startDocument(content);
3856     ok(hr == S_OK, "got %08x\n", hr);
3857 
3858     /* all string pointers should be not null */
3859     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
3860     ok(hr == S_OK, "got %08x\n", hr);
3861 
3862     V_VT(&dest) = VT_EMPTY;
3863     hr = IMXWriter_get_output(writer, &dest);
3864     ok(hr == S_OK, "got %08x\n", hr);
3865     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3866     ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3867     VariantClear(&dest);
3868 
3869     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
3870     ok(hr == S_OK, "got %08x\n", hr);
3871 
3872     V_VT(&dest) = VT_EMPTY;
3873     hr = IMXWriter_get_output(writer, &dest);
3874     ok(hr == S_OK, "got %08x\n", hr);
3875     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3876     ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3877     VariantClear(&dest);
3878 
3879     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
3880     EXPECT_HR(hr, E_INVALIDARG);
3881 
3882     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
3883     EXPECT_HR(hr, E_INVALIDARG);
3884 
3885     /* only local name is an error too */
3886     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
3887     EXPECT_HR(hr, E_INVALIDARG);
3888 
3889     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
3890     EXPECT_HR(hr, S_OK);
3891 
3892     V_VT(&dest) = VT_EMPTY;
3893     hr = IMXWriter_get_output(writer, &dest);
3894     EXPECT_HR(hr, S_OK);
3895     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3896     ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3897     VariantClear(&dest);
3898 
3899     hr = ISAXContentHandler_endDocument(content);
3900     EXPECT_HR(hr, S_OK);
3901 
3902     V_VT(&dest) = VT_EMPTY;
3903     hr = IMXWriter_put_output(writer, dest);
3904     EXPECT_HR(hr, S_OK);
3905 
3906     V_VT(&dest) = VT_EMPTY;
3907     hr = IMXWriter_get_output(writer, &dest);
3908     EXPECT_HR(hr, S_OK);
3909     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3910     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3911     VariantClear(&dest);
3912 
3913     hr = ISAXContentHandler_startDocument(content);
3914     EXPECT_HR(hr, S_OK);
3915 
3916     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL);
3917     EXPECT_HR(hr, S_OK);
3918 
3919     V_VT(&dest) = VT_EMPTY;
3920     hr = IMXWriter_get_output(writer, &dest);
3921     EXPECT_HR(hr, S_OK);
3922     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3923     ok(!lstrcmpW(_bstr_("<abc>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3924     VariantClear(&dest);
3925 
3926     hr = ISAXContentHandler_endDocument(content);
3927     EXPECT_HR(hr, S_OK);
3928     IMXWriter_flush(writer);
3929 
3930     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abdcdef"), 3);
3931     EXPECT_HR(hr, S_OK);
3932     V_VT(&dest) = VT_EMPTY;
3933     hr = IMXWriter_get_output(writer, &dest);
3934     EXPECT_HR(hr, S_OK);
3935     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3936     ok(!lstrcmpW(_bstr_("<abc></abd>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3937     VariantClear(&dest);
3938 
3939     V_VT(&dest) = VT_EMPTY;
3940     hr = IMXWriter_put_output(writer, dest);
3941     EXPECT_HR(hr, S_OK);
3942 
3943     /* length -1 */
3944     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), -1, NULL);
3945     EXPECT_HR(hr, S_OK);
3946     V_VT(&dest) = VT_EMPTY;
3947     hr = IMXWriter_get_output(writer, &dest);
3948     EXPECT_HR(hr, S_OK);
3949     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3950     ok(!lstrcmpW(_bstr_("<a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3951     VariantClear(&dest);
3952 
3953     ISAXContentHandler_Release(content);
3954     IMXWriter_Release(writer);
3955     free_bstrs();
3956 }
3957 
3958 struct writer_characters_t {
3959     const GUID *clsid;
3960     const char *data;
3961     const char *output;
3962 };
3963 
3964 static const struct writer_characters_t writer_characters[] = {
3965     { &CLSID_MXXMLWriter,   "< > & \" \'", "&lt; &gt; &amp; \" \'" },
3966     { &CLSID_MXXMLWriter30, "< > & \" \'", "&lt; &gt; &amp; \" \'" },
3967     { &CLSID_MXXMLWriter40, "< > & \" \'", "&lt; &gt; &amp; \" \'" },
3968     { &CLSID_MXXMLWriter60, "< > & \" \'", "&lt; &gt; &amp; \" \'" },
3969     { NULL }
3970 };
3971 
3972 static void test_mxwriter_characters(void)
3973 {
3974     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
3975     static const WCHAR embedded_nullbytes[] = {'a',0,'b',0,0,0,'c',0};
3976     const struct writer_characters_t *table = writer_characters;
3977     IVBSAXContentHandler *vb_content;
3978     ISAXContentHandler *content;
3979     IMXWriter *writer;
3980     VARIANT dest;
3981     BSTR str;
3982     HRESULT hr;
3983     int i = 0;
3984 
3985     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3986             &IID_IMXWriter, (void**)&writer);
3987     EXPECT_HR(hr, S_OK);
3988 
3989     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3990     EXPECT_HR(hr, S_OK);
3991 
3992     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void**)&vb_content);
3993     EXPECT_HR(hr, S_OK);
3994 
3995     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3996     EXPECT_HR(hr, S_OK);
3997 
3998     hr = ISAXContentHandler_startDocument(content);
3999     EXPECT_HR(hr, S_OK);
4000 
4001     hr = ISAXContentHandler_characters(content, NULL, 0);
4002     EXPECT_HR(hr, E_INVALIDARG);
4003 
4004     hr = ISAXContentHandler_characters(content, chardataW, 0);
4005     EXPECT_HR(hr, S_OK);
4006 
4007     str = _bstr_("VbChars");
4008     hr = IVBSAXContentHandler_characters(vb_content, &str);
4009     EXPECT_HR(hr, S_OK);
4010 
4011     hr = ISAXContentHandler_characters(content, chardataW, ARRAY_SIZE(chardataW) - 1);
4012     EXPECT_HR(hr, S_OK);
4013 
4014     V_VT(&dest) = VT_EMPTY;
4015     hr = IMXWriter_get_output(writer, &dest);
4016     EXPECT_HR(hr, S_OK);
4017     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4018     ok(!lstrcmpW(_bstr_("VbCharsTESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4019     VariantClear(&dest);
4020 
4021     hr = ISAXContentHandler_endDocument(content);
4022     EXPECT_HR(hr, S_OK);
4023 
4024     ISAXContentHandler_Release(content);
4025     IVBSAXContentHandler_Release(vb_content);
4026     IMXWriter_Release(writer);
4027 
4028     /* try empty characters data to see if element is closed */
4029     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4030             &IID_IMXWriter, (void**)&writer);
4031     EXPECT_HR(hr, S_OK);
4032 
4033     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4034     EXPECT_HR(hr, S_OK);
4035 
4036     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4037     EXPECT_HR(hr, S_OK);
4038 
4039     hr = ISAXContentHandler_startDocument(content);
4040     EXPECT_HR(hr, S_OK);
4041 
4042     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
4043     EXPECT_HR(hr, S_OK);
4044 
4045     hr = ISAXContentHandler_characters(content, chardataW, 0);
4046     EXPECT_HR(hr, S_OK);
4047 
4048     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
4049     EXPECT_HR(hr, S_OK);
4050 
4051     V_VT(&dest) = VT_EMPTY;
4052     hr = IMXWriter_get_output(writer, &dest);
4053     EXPECT_HR(hr, S_OK);
4054     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4055     ok(!lstrcmpW(_bstr_("<a></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4056     VariantClear(&dest);
4057 
4058     ISAXContentHandler_Release(content);
4059     IMXWriter_Release(writer);
4060 
4061     /* test embedded null bytes */
4062     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4063             &IID_IMXWriter, (void**)&writer);
4064     EXPECT_HR(hr, S_OK);
4065 
4066     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4067     EXPECT_HR(hr, S_OK);
4068 
4069     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4070     EXPECT_HR(hr, S_OK);
4071 
4072     hr = ISAXContentHandler_startDocument(content);
4073     EXPECT_HR(hr, S_OK);
4074 
4075     hr = ISAXContentHandler_characters(content, embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes));
4076     EXPECT_HR(hr, S_OK);
4077 
4078     V_VT(&dest) = VT_EMPTY;
4079     hr = IMXWriter_get_output(writer, &dest);
4080     EXPECT_HR(hr, S_OK);
4081     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4082     ok(SysStringLen(V_BSTR(&dest)) == ARRAY_SIZE(embedded_nullbytes), "unexpected len %d\n", SysStringLen(V_BSTR(&dest)));
4083     ok(!memcmp(V_BSTR(&dest), embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)),
4084        "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4085     VariantClear(&dest);
4086 
4087     ISAXContentHandler_Release(content);
4088     IMXWriter_Release(writer);
4089 
4090     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4091             &IID_IMXWriter, (void**)&writer);
4092     EXPECT_HR(hr, S_OK);
4093 
4094     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void**)&vb_content);
4095     EXPECT_HR(hr, S_OK);
4096 
4097     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4098     EXPECT_HR(hr, S_OK);
4099 
4100     hr = IVBSAXContentHandler_startDocument(vb_content);
4101     EXPECT_HR(hr, S_OK);
4102 
4103     str = SysAllocStringLen(embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes));
4104     hr = IVBSAXContentHandler_characters(vb_content, &str);
4105     EXPECT_HR(hr, S_OK);
4106     SysFreeString(str);
4107 
4108     V_VT(&dest) = VT_EMPTY;
4109     hr = IMXWriter_get_output(writer, &dest);
4110     EXPECT_HR(hr, S_OK);
4111     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4112     ok(SysStringLen(V_BSTR(&dest)) == ARRAY_SIZE(embedded_nullbytes), "unexpected len %d\n", SysStringLen(V_BSTR(&dest)));
4113     ok(!memcmp(V_BSTR(&dest), embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)),
4114        "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4115     VariantClear(&dest);
4116 
4117     IVBSAXContentHandler_Release(vb_content);
4118     IMXWriter_Release(writer);
4119 
4120     /* batch tests */
4121     while (table->clsid)
4122     {
4123         ISAXContentHandler *content;
4124         IMXWriter *writer;
4125         VARIANT dest;
4126         HRESULT hr;
4127 
4128         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
4129         {
4130             table++;
4131             i++;
4132             continue;
4133         }
4134 
4135         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
4136             &IID_IMXWriter, (void**)&writer);
4137         EXPECT_HR(hr, S_OK);
4138 
4139         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4140         EXPECT_HR(hr, S_OK);
4141 
4142         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4143         EXPECT_HR(hr, S_OK);
4144 
4145         hr = ISAXContentHandler_startDocument(content);
4146         EXPECT_HR(hr, S_OK);
4147 
4148         hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
4149         EXPECT_HR(hr, S_OK);
4150 
4151         /* test output */
4152         if (hr == S_OK)
4153         {
4154             V_VT(&dest) = VT_EMPTY;
4155             hr = IMXWriter_get_output(writer, &dest);
4156             EXPECT_HR(hr, S_OK);
4157             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4158             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
4159                 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
4160             VariantClear(&dest);
4161         }
4162 
4163         /* with disabled escaping */
4164         V_VT(&dest) = VT_EMPTY;
4165         hr = IMXWriter_put_output(writer, dest);
4166         EXPECT_HR(hr, S_OK);
4167 
4168         hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
4169         EXPECT_HR(hr, S_OK);
4170 
4171         hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
4172         EXPECT_HR(hr, S_OK);
4173 
4174         /* test output */
4175         if (hr == S_OK)
4176         {
4177             V_VT(&dest) = VT_EMPTY;
4178             hr = IMXWriter_get_output(writer, &dest);
4179             EXPECT_HR(hr, S_OK);
4180             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4181             ok(!lstrcmpW(_bstr_(table->data), V_BSTR(&dest)),
4182                 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->data);
4183             VariantClear(&dest);
4184         }
4185 
4186         ISAXContentHandler_Release(content);
4187         IMXWriter_Release(writer);
4188 
4189         table++;
4190         i++;
4191     }
4192 
4193     free_bstrs();
4194 }
4195 
4196 static const mxwriter_stream_test mxwriter_stream_tests[] = {
4197     {
4198         VARIANT_TRUE,"UTF-16",
4199         {
4200             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
4201             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
4202             {TRUE}
4203         }
4204     },
4205     {
4206         VARIANT_FALSE,"UTF-16",
4207         {
4208             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
4209             {TRUE}
4210         }
4211     },
4212     {
4213         VARIANT_TRUE,"UTF-8",
4214         {
4215             {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
4216             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
4217              * and the writer is released.
4218              */
4219             {FALSE,NULL,0},
4220             {TRUE}
4221         }
4222     },
4223     {
4224         VARIANT_TRUE,"utf-8",
4225         {
4226             {FALSE,(const BYTE*)utf8xml2,sizeof(utf8xml2)-1},
4227             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
4228              * and the writer is released.
4229              */
4230             {FALSE,NULL,0},
4231             {TRUE}
4232         }
4233     },
4234     {
4235         VARIANT_TRUE,"UTF-16",
4236         {
4237             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
4238             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
4239             {TRUE}
4240         }
4241     },
4242     {
4243         VARIANT_TRUE,"UTF-16",
4244         {
4245             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
4246             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
4247             {TRUE}
4248         }
4249     }
4250 };
4251 
4252 static void test_mxwriter_stream(void)
4253 {
4254     IMXWriter *writer;
4255     ISAXContentHandler *content;
4256     HRESULT hr;
4257     VARIANT dest;
4258     IStream *stream;
4259     LARGE_INTEGER pos;
4260     ULARGE_INTEGER pos2;
4261     DWORD test_count = ARRAY_SIZE(mxwriter_stream_tests);
4262 
4263     for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
4264         const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
4265 
4266         hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4267                 &IID_IMXWriter, (void**)&writer);
4268         ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
4269 
4270         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4271         ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
4272 
4273         hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
4274         ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
4275 
4276         V_VT(&dest) = VT_UNKNOWN;
4277         V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
4278         hr = IMXWriter_put_output(writer, dest);
4279         ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
4280 
4281         hr = IMXWriter_put_byteOrderMark(writer, test->bom);
4282         ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
4283 
4284         current_write_test = test->expected_writes;
4285 
4286         hr = ISAXContentHandler_startDocument(content);
4287         ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
4288 
4289         hr = ISAXContentHandler_endDocument(content);
4290         ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
4291 
4292         ISAXContentHandler_Release(content);
4293         IMXWriter_Release(writer);
4294 
4295         ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
4296             (int)(current_write_test-test->expected_writes), current_stream_test_index);
4297     }
4298 
4299     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4300             &IID_IMXWriter, (void**)&writer);
4301     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
4302 
4303     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
4304     ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
4305 
4306     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4307     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
4308 
4309     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
4310     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
4311 
4312     V_VT(&dest) = VT_UNKNOWN;
4313     V_UNKNOWN(&dest) = (IUnknown*)stream;
4314     hr = IMXWriter_put_output(writer, dest);
4315     ok(hr == S_OK, "put_output failed: %08x\n", hr);
4316 
4317     hr = ISAXContentHandler_startDocument(content);
4318     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
4319 
4320     /* Setting output of the mxwriter causes the current output to be flushed,
4321      * and the writer to start over.
4322      */
4323     V_VT(&dest) = VT_EMPTY;
4324     hr = IMXWriter_put_output(writer, dest);
4325     ok(hr == S_OK, "put_output failed: %08x\n", hr);
4326 
4327     pos.QuadPart = 0;
4328     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
4329     ok(hr == S_OK, "Seek failed: %08x\n", hr);
4330     ok(pos2.QuadPart != 0, "expected stream position moved\n");
4331 
4332     hr = ISAXContentHandler_startDocument(content);
4333     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
4334 
4335     hr = ISAXContentHandler_endDocument(content);
4336     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
4337 
4338     V_VT(&dest) = VT_EMPTY;
4339     hr = IMXWriter_get_output(writer, &dest);
4340     ok(hr == S_OK, "get_output failed: %08x\n", hr);
4341     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
4342     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
4343             "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4344     VariantClear(&dest);
4345 
4346     /* test when BOM is written to output stream */
4347     V_VT(&dest) = VT_EMPTY;
4348     hr = IMXWriter_put_output(writer, dest);
4349     EXPECT_HR(hr, S_OK);
4350 
4351     pos.QuadPart = 0;
4352     hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
4353     EXPECT_HR(hr, S_OK);
4354 
4355     V_VT(&dest) = VT_UNKNOWN;
4356     V_UNKNOWN(&dest) = (IUnknown*)stream;
4357     hr = IMXWriter_put_output(writer, dest);
4358     EXPECT_HR(hr, S_OK);
4359 
4360     hr = IMXWriter_put_byteOrderMark(writer, VARIANT_TRUE);
4361     EXPECT_HR(hr, S_OK);
4362 
4363     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
4364     EXPECT_HR(hr, S_OK);
4365 
4366     hr = ISAXContentHandler_startDocument(content);
4367     EXPECT_HR(hr, S_OK);
4368 
4369     pos.QuadPart = 0;
4370     pos2.QuadPart = 0;
4371     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
4372     EXPECT_HR(hr, S_OK);
4373     ok(pos2.QuadPart == 2, "got wrong position\n");
4374 
4375     IStream_Release(stream);
4376     ISAXContentHandler_Release(content);
4377     IMXWriter_Release(writer);
4378 
4379     free_bstrs();
4380 }
4381 
4382 static const char *encoding_names[] = {
4383     "iso-8859-1",
4384     "iso-8859-2",
4385     "iso-8859-3",
4386     "iso-8859-4",
4387     "iso-8859-5",
4388     "iso-8859-7",
4389     "iso-8859-9",
4390     "iso-8859-13",
4391     "iso-8859-15",
4392     NULL
4393 };
4394 
4395 static void test_mxwriter_encoding(void)
4396 {
4397     ISAXContentHandler *content;
4398     IMXWriter *writer;
4399     IStream *stream;
4400     const char *enc;
4401     VARIANT dest;
4402     HRESULT hr;
4403     HGLOBAL g;
4404     char *ptr;
4405     BSTR s;
4406     int i;
4407 
4408     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4409             &IID_IMXWriter, (void**)&writer);
4410     EXPECT_HR(hr, S_OK);
4411 
4412     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4413     EXPECT_HR(hr, S_OK);
4414 
4415     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
4416     EXPECT_HR(hr, S_OK);
4417 
4418     hr = ISAXContentHandler_startDocument(content);
4419     EXPECT_HR(hr, S_OK);
4420 
4421     hr = ISAXContentHandler_endDocument(content);
4422     EXPECT_HR(hr, S_OK);
4423 
4424     /* The content is always re-encoded to UTF-16 when the output is
4425      * retrieved as a BSTR.
4426      */
4427     V_VT(&dest) = VT_EMPTY;
4428     hr = IMXWriter_get_output(writer, &dest);
4429     EXPECT_HR(hr, S_OK);
4430     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
4431     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
4432             "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4433     VariantClear(&dest);
4434 
4435     /* switch encoding when something is written already */
4436     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
4437     EXPECT_HR(hr, S_OK);
4438 
4439     V_VT(&dest) = VT_UNKNOWN;
4440     V_UNKNOWN(&dest) = (IUnknown*)stream;
4441     hr = IMXWriter_put_output(writer, dest);
4442     EXPECT_HR(hr, S_OK);
4443 
4444     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
4445     EXPECT_HR(hr, S_OK);
4446 
4447     /* write empty element */
4448     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
4449     EXPECT_HR(hr, S_OK);
4450 
4451     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
4452     EXPECT_HR(hr, S_OK);
4453 
4454     /* switch */
4455     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
4456     EXPECT_HR(hr, S_OK);
4457 
4458     hr = IMXWriter_flush(writer);
4459     EXPECT_HR(hr, S_OK);
4460 
4461     hr = GetHGlobalFromStream(stream, &g);
4462     EXPECT_HR(hr, S_OK);
4463 
4464     ptr = GlobalLock(g);
4465     ok(!strncmp(ptr, "<a/>", 4), "got %c%c%c%c\n", ptr[0],ptr[1],ptr[2],ptr[3]);
4466     GlobalUnlock(g);
4467 
4468     /* so output is unaffected, encoding name is stored however */
4469     hr = IMXWriter_get_encoding(writer, &s);
4470     EXPECT_HR(hr, S_OK);
4471     ok(!lstrcmpW(s, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(s));
4472     SysFreeString(s);
4473 
4474     IStream_Release(stream);
4475 
4476     i = 0;
4477     enc = encoding_names[i];
4478     while (enc)
4479     {
4480         char expectedA[200];
4481 
4482         hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
4483         EXPECT_HR(hr, S_OK);
4484 
4485         V_VT(&dest) = VT_UNKNOWN;
4486         V_UNKNOWN(&dest) = (IUnknown*)stream;
4487         hr = IMXWriter_put_output(writer, dest);
4488         EXPECT_HR(hr, S_OK);
4489 
4490         hr = IMXWriter_put_encoding(writer, _bstr_(enc));
4491         ok(hr == S_OK || broken(hr != S_OK) /* old win versions do not support certain encodings */,
4492             "%s: encoding not accepted\n", enc);
4493         if (hr != S_OK)
4494         {
4495             enc = encoding_names[++i];
4496             IStream_Release(stream);
4497             continue;
4498         }
4499 
4500         hr = ISAXContentHandler_startDocument(content);
4501         EXPECT_HR(hr, S_OK);
4502 
4503         hr = ISAXContentHandler_endDocument(content);
4504         EXPECT_HR(hr, S_OK);
4505 
4506         hr = IMXWriter_flush(writer);
4507         EXPECT_HR(hr, S_OK);
4508 
4509         /* prepare expected string */
4510         *expectedA = 0;
4511         strcat(expectedA, "<?xml version=\"1.0\" encoding=\"");
4512         strcat(expectedA, enc);
4513         strcat(expectedA, "\" standalone=\"no\"?>\r\n");
4514 
4515         hr = GetHGlobalFromStream(stream, &g);
4516         EXPECT_HR(hr, S_OK);
4517 
4518         ptr = GlobalLock(g);
4519         ok(!strncmp(ptr, expectedA, strlen(expectedA)), "%s: got %s, expected %.50s\n", enc, ptr, expectedA);
4520         GlobalUnlock(g);
4521 
4522         V_VT(&dest) = VT_EMPTY;
4523         hr = IMXWriter_put_output(writer, dest);
4524         EXPECT_HR(hr, S_OK);
4525 
4526         IStream_Release(stream);
4527 
4528         enc = encoding_names[++i];
4529     }
4530 
4531     ISAXContentHandler_Release(content);
4532     IMXWriter_Release(writer);
4533 
4534     free_bstrs();
4535 }
4536 
4537 static void test_obj_dispex(IUnknown *obj)
4538 {
4539     static const WCHAR testW[] = {'t','e','s','t','p','r','o','p',0};
4540     static const WCHAR starW[] = {'*',0};
4541     DISPID dispid = DISPID_SAX_XMLREADER_GETFEATURE;
4542     IDispatchEx *dispex;
4543     IUnknown *unk;
4544     DWORD props;
4545     UINT ticnt;
4546     HRESULT hr;
4547     BSTR name;
4548     DISPID did;
4549 
4550     hr = IUnknown_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex);
4551     EXPECT_HR(hr, S_OK);
4552     if (FAILED(hr)) return;
4553 
4554     ticnt = 0;
4555     hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt);
4556     EXPECT_HR(hr, S_OK);
4557     ok(ticnt == 1, "ticnt=%u\n", ticnt);
4558 
4559     name = SysAllocString(starW);
4560     hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive);
4561     EXPECT_HR(hr, E_NOTIMPL);
4562     SysFreeString(name);
4563 
4564     hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid);
4565     EXPECT_HR(hr, E_NOTIMPL);
4566 
4567     props = 0;
4568     hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props);
4569     EXPECT_HR(hr, E_NOTIMPL);
4570     ok(props == 0, "expected 0 got %d\n", props);
4571 
4572     hr = IDispatchEx_GetMemberName(dispex, dispid, &name);
4573     EXPECT_HR(hr, E_NOTIMPL);
4574     if (SUCCEEDED(hr)) SysFreeString(name);
4575 
4576     hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_SAX_XMLREADER_GETFEATURE, &dispid);
4577     EXPECT_HR(hr, E_NOTIMPL);
4578 
4579     unk = (IUnknown*)0xdeadbeef;
4580     hr = IDispatchEx_GetNameSpaceParent(dispex, &unk);
4581     EXPECT_HR(hr, E_NOTIMPL);
4582     ok(unk == (IUnknown*)0xdeadbeef, "got %p\n", unk);
4583 
4584     name = SysAllocString(testW);
4585     hr = IDispatchEx_GetDispID(dispex, name, fdexNameEnsure, &did);
4586     ok(hr == DISP_E_UNKNOWNNAME, "got 0x%08x\n", hr);
4587     SysFreeString(name);
4588 
4589     IDispatchEx_Release(dispex);
4590 }
4591 
4592 static void test_saxreader_dispex(void)
4593 {
4594     IVBSAXXMLReader *vbreader;
4595     ISAXXMLReader *reader;
4596     DISPPARAMS dispparams;
4597     DISPID dispid;
4598     IUnknown *unk;
4599     VARIANT arg;
4600     HRESULT hr;
4601 
4602     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
4603                 &IID_ISAXXMLReader, (void**)&reader);
4604     EXPECT_HR(hr, S_OK);
4605 
4606     hr = ISAXXMLReader_QueryInterface(reader, &IID_IUnknown, (void**)&unk);
4607     EXPECT_HR(hr, S_OK);
4608     test_obj_dispex(unk);
4609     IUnknown_Release(unk);
4610 
4611     hr = ISAXXMLReader_QueryInterface(reader, &IID_IVBSAXXMLReader, (void**)&vbreader);
4612     EXPECT_HR(hr, S_OK);
4613     hr = IVBSAXXMLReader_QueryInterface(vbreader, &IID_IUnknown, (void**)&unk);
4614     EXPECT_HR(hr, S_OK);
4615     test_obj_dispex(unk);
4616     IUnknown_Release(unk);
4617 
4618     dispid = DISPID_PROPERTYPUT;
4619     dispparams.cArgs = 1;
4620     dispparams.cNamedArgs = 1;
4621     dispparams.rgdispidNamedArgs = &dispid;
4622     dispparams.rgvarg = &arg;
4623 
4624     V_VT(&arg) = VT_DISPATCH;
4625     V_DISPATCH(&arg) = NULL;
4626 
4627     /* propputref is callable as PROPERTYPUT and PROPERTYPUTREF */
4628     hr = IVBSAXXMLReader_Invoke(vbreader,
4629         DISPID_SAX_XMLREADER_CONTENTHANDLER,
4630        &IID_NULL,
4631         0,
4632         DISPATCH_PROPERTYPUT,
4633        &dispparams,
4634         NULL,
4635         NULL,
4636         NULL);
4637     ok(hr == S_OK, "got 0x%08x\n", hr);
4638 
4639     hr = IVBSAXXMLReader_Invoke(vbreader,
4640         DISPID_SAX_XMLREADER_CONTENTHANDLER,
4641        &IID_NULL,
4642         0,
4643         DISPATCH_PROPERTYPUTREF,
4644        &dispparams,
4645         NULL,
4646         NULL,
4647         NULL);
4648     ok(hr == S_OK, "got 0x%08x\n", hr);
4649 
4650     IVBSAXXMLReader_Release(vbreader);
4651     ISAXXMLReader_Release(reader);
4652 
4653     if (is_clsid_supported(&CLSID_SAXXMLReader60, reader_support_data))
4654     {
4655         hr = CoCreateInstance(&CLSID_SAXXMLReader60, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&unk);
4656         ok(hr == S_OK, "got 0x%08x\n", hr);
4657         test_obj_dispex(unk);
4658         IUnknown_Release(unk);
4659     }
4660 }
4661 
4662 static void test_mxwriter_dispex(void)
4663 {
4664     IDispatchEx *dispex;
4665     IMXWriter *writer;
4666     IUnknown *unk;
4667     HRESULT hr;
4668 
4669     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4670             &IID_IMXWriter, (void**)&writer);
4671     EXPECT_HR(hr, S_OK);
4672 
4673     hr = IMXWriter_QueryInterface(writer, &IID_IDispatchEx, (void**)&dispex);
4674     EXPECT_HR(hr, S_OK);
4675     hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
4676     test_obj_dispex(unk);
4677     IUnknown_Release(unk);
4678     IDispatchEx_Release(dispex);
4679     IMXWriter_Release(writer);
4680 
4681     if (is_clsid_supported(&CLSID_MXXMLWriter60, mxwriter_support_data))
4682     {
4683         hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&unk);
4684         ok(hr == S_OK, "got 0x%08x\n", hr);
4685         test_obj_dispex(unk);
4686         IUnknown_Release(unk);
4687     }
4688 }
4689 
4690 static void test_mxwriter_comment(void)
4691 {
4692     static const WCHAR commentW[] = {'c','o','m','m','e','n','t',0};
4693     IVBSAXLexicalHandler *vblexical;
4694     ISAXContentHandler *content;
4695     ISAXLexicalHandler *lexical;
4696     IMXWriter *writer;
4697     VARIANT dest;
4698     HRESULT hr;
4699 
4700     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4701             &IID_IMXWriter, (void**)&writer);
4702     EXPECT_HR(hr, S_OK);
4703 
4704     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4705     EXPECT_HR(hr, S_OK);
4706 
4707     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4708     EXPECT_HR(hr, S_OK);
4709 
4710     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical);
4711     EXPECT_HR(hr, S_OK);
4712 
4713     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4714     EXPECT_HR(hr, S_OK);
4715 
4716     hr = ISAXContentHandler_startDocument(content);
4717     EXPECT_HR(hr, S_OK);
4718 
4719     hr = ISAXLexicalHandler_comment(lexical, NULL, 0);
4720     EXPECT_HR(hr, E_INVALIDARG);
4721 
4722     hr = IVBSAXLexicalHandler_comment(vblexical, NULL);
4723     EXPECT_HR(hr, E_POINTER);
4724 
4725     hr = ISAXLexicalHandler_comment(lexical, commentW, 0);
4726     EXPECT_HR(hr, S_OK);
4727 
4728     V_VT(&dest) = VT_EMPTY;
4729     hr = IMXWriter_get_output(writer, &dest);
4730     EXPECT_HR(hr, S_OK);
4731     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4732     ok(!lstrcmpW(_bstr_("<!---->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4733     VariantClear(&dest);
4734 
4735     hr = ISAXLexicalHandler_comment(lexical, commentW, ARRAY_SIZE(commentW) - 1);
4736     EXPECT_HR(hr, S_OK);
4737 
4738     V_VT(&dest) = VT_EMPTY;
4739     hr = IMXWriter_get_output(writer, &dest);
4740     EXPECT_HR(hr, S_OK);
4741     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4742     ok(!lstrcmpW(_bstr_("<!---->\r\n<!--comment-->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4743     VariantClear(&dest);
4744 
4745     ISAXContentHandler_Release(content);
4746     ISAXLexicalHandler_Release(lexical);
4747     IVBSAXLexicalHandler_Release(vblexical);
4748     IMXWriter_Release(writer);
4749     free_bstrs();
4750 }
4751 
4752 static void test_mxwriter_cdata(void)
4753 {
4754     IVBSAXLexicalHandler *vblexical;
4755     ISAXContentHandler *content;
4756     ISAXLexicalHandler *lexical;
4757     IMXWriter *writer;
4758     VARIANT dest;
4759     HRESULT hr;
4760 
4761     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4762             &IID_IMXWriter, (void**)&writer);
4763     EXPECT_HR(hr, S_OK);
4764 
4765     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4766     EXPECT_HR(hr, S_OK);
4767 
4768     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4769     EXPECT_HR(hr, S_OK);
4770 
4771     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical);
4772     EXPECT_HR(hr, S_OK);
4773 
4774     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4775     EXPECT_HR(hr, S_OK);
4776 
4777     hr = ISAXContentHandler_startDocument(content);
4778     EXPECT_HR(hr, S_OK);
4779 
4780     hr = ISAXLexicalHandler_startCDATA(lexical);
4781     EXPECT_HR(hr, S_OK);
4782 
4783     V_VT(&dest) = VT_EMPTY;
4784     hr = IMXWriter_get_output(writer, &dest);
4785     EXPECT_HR(hr, S_OK);
4786     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4787     ok(!lstrcmpW(_bstr_("<![CDATA["), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4788     VariantClear(&dest);
4789 
4790     hr = IVBSAXLexicalHandler_startCDATA(vblexical);
4791     EXPECT_HR(hr, S_OK);
4792 
4793     /* all these are escaped for text nodes */
4794     hr = ISAXContentHandler_characters(content, _bstr_("< > & \""), 7);
4795     EXPECT_HR(hr, S_OK);
4796 
4797     hr = ISAXLexicalHandler_endCDATA(lexical);
4798     EXPECT_HR(hr, S_OK);
4799 
4800     V_VT(&dest) = VT_EMPTY;
4801     hr = IMXWriter_get_output(writer, &dest);
4802     EXPECT_HR(hr, S_OK);
4803     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4804     ok(!lstrcmpW(_bstr_("<![CDATA[<![CDATA[< > & \"]]>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4805     VariantClear(&dest);
4806 
4807     ISAXContentHandler_Release(content);
4808     ISAXLexicalHandler_Release(lexical);
4809     IVBSAXLexicalHandler_Release(vblexical);
4810     IMXWriter_Release(writer);
4811     free_bstrs();
4812 }
4813 
4814 static void test_mxwriter_pi(void)
4815 {
4816     static const WCHAR targetW[] = {'t','a','r','g','e','t',0};
4817     static const WCHAR dataW[] = {'d','a','t','a',0};
4818     ISAXContentHandler *content;
4819     IMXWriter *writer;
4820     VARIANT dest;
4821     HRESULT hr;
4822 
4823     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4824             &IID_IMXWriter, (void**)&writer);
4825     EXPECT_HR(hr, S_OK);
4826 
4827     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4828     EXPECT_HR(hr, S_OK);
4829 
4830     hr = ISAXContentHandler_processingInstruction(content, NULL, 0, NULL, 0);
4831     EXPECT_HR(hr, E_INVALIDARG);
4832 
4833     hr = ISAXContentHandler_processingInstruction(content, targetW, 0, NULL, 0);
4834     EXPECT_HR(hr, S_OK);
4835 
4836     hr = ISAXContentHandler_processingInstruction(content, targetW, 6, NULL, 0);
4837     EXPECT_HR(hr, S_OK);
4838 
4839     V_VT(&dest) = VT_EMPTY;
4840     hr = IMXWriter_get_output(writer, &dest);
4841     EXPECT_HR(hr, S_OK);
4842     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4843     ok(!lstrcmpW(_bstr_("<?\?>\r\n<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4844     VariantClear(&dest);
4845 
4846     hr = ISAXContentHandler_processingInstruction(content, targetW, 4, dataW, 4);
4847     EXPECT_HR(hr, S_OK);
4848 
4849     V_VT(&dest) = VT_EMPTY;
4850     hr = IMXWriter_get_output(writer, &dest);
4851     EXPECT_HR(hr, S_OK);
4852     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4853     ok(!lstrcmpW(_bstr_("<?\?>\r\n<?target?>\r\n<?targ data?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4854     VariantClear(&dest);
4855 
4856     V_VT(&dest) = VT_EMPTY;
4857     hr = IMXWriter_put_output(writer, dest);
4858     EXPECT_HR(hr, S_OK);
4859 
4860     hr = ISAXContentHandler_processingInstruction(content, targetW, 6, dataW, 0);
4861     EXPECT_HR(hr, S_OK);
4862 
4863     V_VT(&dest) = VT_EMPTY;
4864     hr = IMXWriter_get_output(writer, &dest);
4865     EXPECT_HR(hr, S_OK);
4866     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4867     ok(!lstrcmpW(_bstr_("<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4868     VariantClear(&dest);
4869 
4870 
4871     ISAXContentHandler_Release(content);
4872     IMXWriter_Release(writer);
4873 }
4874 
4875 static void test_mxwriter_ignorablespaces(void)
4876 {
4877     static const WCHAR dataW[] = {'d','a','t','a',0};
4878     ISAXContentHandler *content;
4879     IMXWriter *writer;
4880     VARIANT dest;
4881     HRESULT hr;
4882 
4883     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4884             &IID_IMXWriter, (void**)&writer);
4885     EXPECT_HR(hr, S_OK);
4886 
4887     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4888     EXPECT_HR(hr, S_OK);
4889 
4890     hr = ISAXContentHandler_ignorableWhitespace(content, NULL, 0);
4891     EXPECT_HR(hr, E_INVALIDARG);
4892 
4893     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 0);
4894     EXPECT_HR(hr, S_OK);
4895 
4896     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 4);
4897     EXPECT_HR(hr, S_OK);
4898 
4899     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 1);
4900     EXPECT_HR(hr, S_OK);
4901 
4902     V_VT(&dest) = VT_EMPTY;
4903     hr = IMXWriter_get_output(writer, &dest);
4904     EXPECT_HR(hr, S_OK);
4905     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4906     ok(!lstrcmpW(_bstr_("datad"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4907     VariantClear(&dest);
4908 
4909     ISAXContentHandler_Release(content);
4910     IMXWriter_Release(writer);
4911 }
4912 
4913 static void test_mxwriter_dtd(void)
4914 {
4915     static const WCHAR contentW[] = {'c','o','n','t','e','n','t'};
4916     static const WCHAR nameW[] = {'n','a','m','e'};
4917     static const WCHAR pubW[] = {'p','u','b'};
4918     static const WCHAR sysW[] = {'s','y','s'};
4919     IVBSAXLexicalHandler *vblexical;
4920     ISAXContentHandler *content;
4921     ISAXLexicalHandler *lexical;
4922     IVBSAXDeclHandler *vbdecl;
4923     ISAXDeclHandler *decl;
4924     ISAXDTDHandler *dtd;
4925     IMXWriter *writer;
4926     VARIANT dest;
4927     HRESULT hr;
4928 
4929     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4930             &IID_IMXWriter, (void**)&writer);
4931     EXPECT_HR(hr, S_OK);
4932 
4933     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4934     EXPECT_HR(hr, S_OK);
4935 
4936     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4937     EXPECT_HR(hr, S_OK);
4938 
4939     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl);
4940     EXPECT_HR(hr, S_OK);
4941 
4942     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXDeclHandler, (void**)&vbdecl);
4943     EXPECT_HR(hr, S_OK);
4944 
4945     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical);
4946     EXPECT_HR(hr, S_OK);
4947 
4948     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4949     EXPECT_HR(hr, S_OK);
4950 
4951     hr = ISAXContentHandler_startDocument(content);
4952     EXPECT_HR(hr, S_OK);
4953 
4954     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0);
4955     EXPECT_HR(hr, E_INVALIDARG);
4956 
4957     hr = IVBSAXLexicalHandler_startDTD(vblexical, NULL, NULL, NULL);
4958     EXPECT_HR(hr, E_POINTER);
4959 
4960     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, ARRAY_SIZE(pubW), NULL, 0);
4961     EXPECT_HR(hr, E_INVALIDARG);
4962 
4963     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, sysW, ARRAY_SIZE(sysW));
4964     EXPECT_HR(hr, E_INVALIDARG);
4965 
4966     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, ARRAY_SIZE(pubW), sysW, ARRAY_SIZE(sysW));
4967     EXPECT_HR(hr, E_INVALIDARG);
4968 
4969     hr = ISAXLexicalHandler_startDTD(lexical, nameW, ARRAY_SIZE(nameW), NULL, 0, NULL, 0);
4970     EXPECT_HR(hr, S_OK);
4971 
4972     V_VT(&dest) = VT_EMPTY;
4973     hr = IMXWriter_get_output(writer, &dest);
4974     EXPECT_HR(hr, S_OK);
4975     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4976     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4977     VariantClear(&dest);
4978 
4979     /* system id is required if public is present */
4980     hr = ISAXLexicalHandler_startDTD(lexical, nameW, ARRAY_SIZE(nameW), pubW, ARRAY_SIZE(pubW), NULL, 0);
4981     EXPECT_HR(hr, E_INVALIDARG);
4982 
4983     hr = ISAXLexicalHandler_startDTD(lexical, nameW, ARRAY_SIZE(nameW),
4984         pubW, ARRAY_SIZE(pubW), sysW, ARRAY_SIZE(sysW));
4985     EXPECT_HR(hr, S_OK);
4986 
4987     V_VT(&dest) = VT_EMPTY;
4988     hr = IMXWriter_get_output(writer, &dest);
4989     EXPECT_HR(hr, S_OK);
4990     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4991     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4992         "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4993     VariantClear(&dest);
4994 
4995     hr = ISAXLexicalHandler_endDTD(lexical);
4996     EXPECT_HR(hr, S_OK);
4997 
4998     hr = IVBSAXLexicalHandler_endDTD(vblexical);
4999     EXPECT_HR(hr, S_OK);
5000 
5001     V_VT(&dest) = VT_EMPTY;
5002     hr = IMXWriter_get_output(writer, &dest);
5003     EXPECT_HR(hr, S_OK);
5004     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5005     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
5006          "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\r\n]>\r\n"),
5007         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5008     VariantClear(&dest);
5009 
5010     /* element declaration */
5011     V_VT(&dest) = VT_EMPTY;
5012     hr = IMXWriter_put_output(writer, dest);
5013     EXPECT_HR(hr, S_OK);
5014 
5015     hr = ISAXDeclHandler_elementDecl(decl, NULL, 0, NULL, 0);
5016     EXPECT_HR(hr, E_INVALIDARG);
5017 
5018     hr = IVBSAXDeclHandler_elementDecl(vbdecl, NULL, NULL);
5019     EXPECT_HR(hr, E_POINTER);
5020 
5021     hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), NULL, 0);
5022     EXPECT_HR(hr, E_INVALIDARG);
5023 
5024     hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), contentW, ARRAY_SIZE(contentW));
5025     EXPECT_HR(hr, S_OK);
5026 
5027     V_VT(&dest) = VT_EMPTY;
5028     hr = IMXWriter_get_output(writer, &dest);
5029     EXPECT_HR(hr, S_OK);
5030     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5031     ok(!lstrcmpW(_bstr_("<!ELEMENT name content>\r\n"),
5032         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5033     VariantClear(&dest);
5034 
5035     V_VT(&dest) = VT_EMPTY;
5036     hr = IMXWriter_put_output(writer, dest);
5037     EXPECT_HR(hr, S_OK);
5038 
5039     hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), contentW, 0);
5040     EXPECT_HR(hr, S_OK);
5041 
5042     V_VT(&dest) = VT_EMPTY;
5043     hr = IMXWriter_get_output(writer, &dest);
5044     EXPECT_HR(hr, S_OK);
5045     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5046     ok(!lstrcmpW(_bstr_("<!ELEMENT name >\r\n"),
5047         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5048     VariantClear(&dest);
5049 
5050     /* attribute declaration */
5051     V_VT(&dest) = VT_EMPTY;
5052     hr = IMXWriter_put_output(writer, dest);
5053     EXPECT_HR(hr, S_OK);
5054 
5055     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"),
5056         _bstr_("attribute"), strlen("attribute"), _bstr_("CDATA"), strlen("CDATA"),
5057         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value"), strlen("value"));
5058     EXPECT_HR(hr, S_OK);
5059 
5060     V_VT(&dest) = VT_EMPTY;
5061     hr = IMXWriter_get_output(writer, &dest);
5062     EXPECT_HR(hr, S_OK);
5063     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5064     ok(!lstrcmpW(_bstr_("<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"),
5065         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5066     VariantClear(&dest);
5067 
5068     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"),
5069         _bstr_("attribute2"), strlen("attribute2"), _bstr_("CDATA"), strlen("CDATA"),
5070         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value2"), strlen("value2"));
5071     EXPECT_HR(hr, S_OK);
5072 
5073     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element2"), strlen("element2"),
5074         _bstr_("attribute3"), strlen("attribute3"), _bstr_("CDATA"), strlen("CDATA"),
5075         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value3"), strlen("value3"));
5076     EXPECT_HR(hr, S_OK);
5077 
5078     V_VT(&dest) = VT_EMPTY;
5079     hr = IMXWriter_get_output(writer, &dest);
5080     EXPECT_HR(hr, S_OK);
5081     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5082     ok(!lstrcmpW(_bstr_("<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"
5083                         "<!ATTLIST element attribute2 CDATA #REQUIRED \"value2\">\r\n"
5084                         "<!ATTLIST element2 attribute3 CDATA #REQUIRED \"value3\">\r\n"),
5085         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5086     VariantClear(&dest);
5087 
5088     /* internal entities */
5089     V_VT(&dest) = VT_EMPTY;
5090     hr = IMXWriter_put_output(writer, dest);
5091     EXPECT_HR(hr, S_OK);
5092 
5093     hr = ISAXDeclHandler_internalEntityDecl(decl, NULL, 0, NULL, 0);
5094     EXPECT_HR(hr, E_INVALIDARG);
5095 
5096     hr = IVBSAXDeclHandler_internalEntityDecl(vbdecl, NULL, NULL);
5097     EXPECT_HR(hr, E_POINTER);
5098 
5099     hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), -1, NULL, 0);
5100     EXPECT_HR(hr, E_INVALIDARG);
5101 
5102     hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("value"), strlen("value"));
5103     EXPECT_HR(hr, S_OK);
5104 
5105     V_VT(&dest) = VT_EMPTY;
5106     hr = IMXWriter_get_output(writer, &dest);
5107     EXPECT_HR(hr, S_OK);
5108     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5109     ok(!lstrcmpW(_bstr_("<!ENTITY name \"value\">\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5110     VariantClear(&dest);
5111 
5112     /* external entities */
5113     V_VT(&dest) = VT_EMPTY;
5114     hr = IMXWriter_put_output(writer, dest);
5115     ok(hr == S_OK, "got 0x%08x\n", hr);
5116 
5117     hr = ISAXDeclHandler_externalEntityDecl(decl, NULL, 0, NULL, 0, NULL, 0);
5118     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5119 
5120     hr = IVBSAXDeclHandler_externalEntityDecl(vbdecl, NULL, NULL, NULL);
5121     ok(hr == E_POINTER, "got 0x%08x\n", hr);
5122 
5123     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), 0, NULL, 0, NULL, 0);
5124     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5125 
5126     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), -1, NULL, 0, NULL, 0);
5127     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5128 
5129     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"),
5130         _bstr_("sysid"), strlen("sysid"));
5131     ok(hr == S_OK, "got 0x%08x\n", hr);
5132 
5133     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), NULL, 0, _bstr_("sysid"), strlen("sysid"));
5134     ok(hr == S_OK, "got 0x%08x\n", hr);
5135 
5136     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"),
5137         NULL, 0);
5138     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5139 
5140     V_VT(&dest) = VT_EMPTY;
5141     hr = IMXWriter_get_output(writer, &dest);
5142     ok(hr == S_OK, "got 0x%08x\n", hr);
5143     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5144     ok(!lstrcmpW(_bstr_(
5145         "<!ENTITY name PUBLIC \"pubid\" \"sysid\">\r\n"
5146         "<!ENTITY name SYSTEM \"sysid\">\r\n"),
5147         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5148 
5149     VariantClear(&dest);
5150 
5151     /* notation declaration */
5152     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDTDHandler, (void**)&dtd);
5153     ok(hr == S_OK, "got 0x%08x\n", hr);
5154 
5155     V_VT(&dest) = VT_EMPTY;
5156     hr = IMXWriter_put_output(writer, dest);
5157     ok(hr == S_OK, "got 0x%08x\n", hr);
5158 
5159     hr = ISAXDTDHandler_notationDecl(dtd, NULL, 0, NULL, 0, NULL, 0);
5160     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5161 
5162     hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), NULL, 0, NULL, 0);
5163     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5164 
5165     hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), NULL, 0);
5166     ok(hr == S_OK, "got 0x%08x\n", hr);
5167 
5168     hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), _bstr_("sysid"), strlen("sysid"));
5169     ok(hr == S_OK, "got 0x%08x\n", hr);
5170 
5171     hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), NULL, 0, _bstr_("sysid"), strlen("sysid"));
5172     ok(hr == S_OK, "got 0x%08x\n", hr);
5173 
5174     hr = IMXWriter_get_output(writer, &dest);
5175     ok(hr == S_OK, "got 0x%08x\n", hr);
5176     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5177     ok(!lstrcmpW(_bstr_(
5178         "<!NOTATION name"
5179         "<!NOTATION name PUBLIC \"pubid\">\r\n"
5180         "<!NOTATION name PUBLIC \"pubid\" \"sysid\">\r\n"
5181         "<!NOTATION name SYSTEM \"sysid\">\r\n"),
5182         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5183 
5184     VariantClear(&dest);
5185 
5186     ISAXDTDHandler_Release(dtd);
5187 
5188     ISAXContentHandler_Release(content);
5189     ISAXLexicalHandler_Release(lexical);
5190     IVBSAXLexicalHandler_Release(vblexical);
5191     IVBSAXDeclHandler_Release(vbdecl);
5192     ISAXDeclHandler_Release(decl);
5193     IMXWriter_Release(writer);
5194     free_bstrs();
5195 }
5196 
5197 typedef struct {
5198     const CLSID *clsid;
5199     const char *uri;
5200     const char *local;
5201     const char *qname;
5202     const char *type;
5203     const char *value;
5204     HRESULT hr;
5205 } addattribute_test_t;
5206 
5207 static const addattribute_test_t addattribute_data[] = {
5208     { &CLSID_SAXAttributes,   NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
5209     { &CLSID_SAXAttributes30, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
5210     { &CLSID_SAXAttributes40, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
5211     { &CLSID_SAXAttributes60, NULL, NULL, "ns:qname", NULL, "value", S_OK },
5212 
5213     { &CLSID_SAXAttributes,   NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5214     { &CLSID_SAXAttributes30, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5215     { &CLSID_SAXAttributes40, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5216     { &CLSID_SAXAttributes60, NULL, "qname", "ns:qname", NULL, "value", S_OK },
5217 
5218     { &CLSID_SAXAttributes,   "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5219     { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5220     { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5221     { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", NULL, "value", S_OK },
5222 
5223     { &CLSID_SAXAttributes,   "uri", "qname", "ns:qname", "type", "value", S_OK },
5224     { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", "type", "value", S_OK },
5225     { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", "type", "value", S_OK },
5226     { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", "type", "value", S_OK },
5227 
5228     { NULL }
5229 };
5230 
5231 static void test_mxattr_addAttribute(void)
5232 {
5233     const addattribute_test_t *table = addattribute_data;
5234     int i = 0;
5235 
5236     while (table->clsid)
5237     {
5238         ISAXAttributes *saxattr;
5239         IMXAttributes *mxattr;
5240         const WCHAR *value;
5241         int len, index;
5242         HRESULT hr;
5243 
5244         if (!is_clsid_supported(table->clsid, mxattributes_support_data))
5245         {
5246             table++;
5247             i++;
5248             continue;
5249         }
5250 
5251         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
5252             &IID_IMXAttributes, (void**)&mxattr);
5253         EXPECT_HR(hr, S_OK);
5254 
5255         hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5256         EXPECT_HR(hr, S_OK);
5257 
5258         /* SAXAttributes40 and SAXAttributes60 both crash on this test */
5259         if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5260             IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5261         {
5262             hr = ISAXAttributes_getLength(saxattr, NULL);
5263             EXPECT_HR(hr, E_POINTER);
5264         }
5265 
5266         len = -1;
5267         hr = ISAXAttributes_getLength(saxattr, &len);
5268         EXPECT_HR(hr, S_OK);
5269         ok(len == 0, "got %d\n", len);
5270 
5271         hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
5272         EXPECT_HR(hr, E_INVALIDARG);
5273 
5274         hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
5275         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5276 
5277         hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
5278         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5279 
5280         hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
5281         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5282 
5283         hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
5284         EXPECT_HR(hr, E_INVALIDARG);
5285 
5286         hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
5287         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5288 
5289         hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
5290         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5291 
5292         hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
5293         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5294 
5295         hr = IMXAttributes_addAttribute(mxattr, _bstr_(table->uri), _bstr_(table->local),
5296             _bstr_(table->qname), _bstr_(table->type), _bstr_(table->value));
5297         ok(hr == table->hr, "%d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
5298 
5299         if (hr == S_OK)
5300         {
5301             /* SAXAttributes40 and SAXAttributes60 both crash on this test */
5302             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5303                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5304             {
5305                hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
5306                EXPECT_HR(hr, E_POINTER);
5307 
5308                hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
5309                EXPECT_HR(hr, E_POINTER);
5310 
5311                hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
5312                EXPECT_HR(hr, E_POINTER);
5313 
5314                hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
5315                EXPECT_HR(hr, E_POINTER);
5316 
5317                hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
5318                EXPECT_HR(hr, E_POINTER);
5319 
5320                hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
5321                EXPECT_HR(hr, E_POINTER);
5322             }
5323 
5324             len = -1;
5325             hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
5326             EXPECT_HR(hr, S_OK);
5327             ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
5328                 table->value);
5329             ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
5330 
5331             len = -1;
5332             value = (void*)0xdeadbeef;
5333             hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
5334             EXPECT_HR(hr, S_OK);
5335 
5336             if (table->type)
5337             {
5338                 ok(!lstrcmpW(_bstr_(table->type), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
5339                     table->type);
5340                 ok(lstrlenW(value) == len, "%d: got wrong type value length %d\n", i, len);
5341             }
5342             else
5343             {
5344                 ok(*value == 0, "%d: got type value %s\n", i, wine_dbgstr_w(value));
5345                 ok(len == 0, "%d: got wrong type value length %d\n", i, len);
5346             }
5347 
5348             hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, NULL);
5349             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5350                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5351             {
5352                 EXPECT_HR(hr, E_POINTER);
5353             }
5354             else
5355                 EXPECT_HR(hr, E_INVALIDARG);
5356 
5357             hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, &index);
5358             EXPECT_HR(hr, E_INVALIDARG);
5359 
5360             index = -1;
5361             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_("nonexistent"), 11, &index);
5362             EXPECT_HR(hr, E_INVALIDARG);
5363             ok(index == -1, "%d: got wrong index %d\n", i, index);
5364 
5365             index = -1;
5366             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), 0, &index);
5367             EXPECT_HR(hr, E_INVALIDARG);
5368             ok(index == -1, "%d: got wrong index %d\n", i, index);
5369 
5370             index = -1;
5371             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &index);
5372             EXPECT_HR(hr, S_OK);
5373             ok(index == 0, "%d: got wrong index %d\n", i, index);
5374 
5375             index = -1;
5376             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname)-1, &index);
5377             EXPECT_HR(hr, E_INVALIDARG);
5378             ok(index == -1, "%d: got wrong index %d\n", i, index);
5379 
5380             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes40) ||
5381                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes60))
5382             {
5383                 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
5384                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5385 
5386                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
5387                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5388 
5389                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
5390                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5391 
5392                 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
5393                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5394 
5395                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
5396                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5397 
5398                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
5399                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5400             }
5401             else
5402             {
5403                 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
5404                 EXPECT_HR(hr, E_POINTER);
5405 
5406                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
5407                 EXPECT_HR(hr, E_POINTER);
5408 
5409                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
5410                 EXPECT_HR(hr, E_POINTER);
5411 
5412                 /* versions 4 and 6 crash */
5413                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL);
5414                 EXPECT_HR(hr, E_POINTER);
5415 
5416                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len);
5417                 EXPECT_HR(hr, E_POINTER);
5418 
5419                 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
5420                 EXPECT_HR(hr, E_POINTER);
5421 
5422                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
5423                 EXPECT_HR(hr, E_POINTER);
5424 
5425                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
5426                 EXPECT_HR(hr, E_POINTER);
5427 
5428                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL);
5429                 EXPECT_HR(hr, E_POINTER);
5430 
5431                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len);
5432                 EXPECT_HR(hr, E_POINTER);
5433 
5434                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local),
5435                     strlen(table->local), NULL, NULL);
5436                 EXPECT_HR(hr, E_POINTER);
5437             }
5438 
5439             hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len);
5440             EXPECT_HR(hr, S_OK);
5441             ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
5442                 table->value);
5443             ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
5444 
5445             if (table->uri) {
5446                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri),
5447                     _bstr_(table->local), strlen(table->local), &value, &len);
5448                 EXPECT_HR(hr, S_OK);
5449                 ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
5450                     table->value);
5451                 ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
5452             }
5453         }
5454 
5455         len = -1;
5456         hr = ISAXAttributes_getLength(saxattr, &len);
5457         EXPECT_HR(hr, S_OK);
5458         if (table->hr == S_OK)
5459             ok(len == 1, "%d: got %d length, expected 1\n", i, len);
5460         else
5461             ok(len == 0, "%d: got %d length, expected 0\n", i, len);
5462 
5463         ISAXAttributes_Release(saxattr);
5464         IMXAttributes_Release(mxattr);
5465 
5466         table++;
5467         i++;
5468     }
5469 
5470     free_bstrs();
5471 }
5472 
5473 static void test_mxattr_clear(void)
5474 {
5475     ISAXAttributes *saxattr;
5476     IMXAttributes *mxattr;
5477     const WCHAR *ptr;
5478     HRESULT hr;
5479     int len;
5480 
5481     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
5482         &IID_IMXAttributes, (void**)&mxattr);
5483     EXPECT_HR(hr, S_OK);
5484 
5485     hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5486     EXPECT_HR(hr, S_OK);
5487 
5488     hr = ISAXAttributes_getQName(saxattr, 0, NULL, NULL);
5489     EXPECT_HR(hr, E_INVALIDARG);
5490 
5491     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
5492     EXPECT_HR(hr, E_INVALIDARG);
5493 
5494     hr = IMXAttributes_clear(mxattr);
5495     EXPECT_HR(hr, S_OK);
5496 
5497     hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("local"),
5498         _bstr_("qname"), _bstr_("type"), _bstr_("value"));
5499     EXPECT_HR(hr, S_OK);
5500 
5501     len = -1;
5502     hr = ISAXAttributes_getLength(saxattr, &len);
5503     EXPECT_HR(hr, S_OK);
5504     ok(len == 1, "got %d\n", len);
5505 
5506     len = -1;
5507     hr = ISAXAttributes_getQName(saxattr, 0, NULL, &len);
5508     EXPECT_HR(hr, E_POINTER);
5509     ok(len == -1, "got %d\n", len);
5510 
5511     ptr = (void*)0xdeadbeef;
5512     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, NULL);
5513     EXPECT_HR(hr, E_POINTER);
5514     ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
5515 
5516     len = 0;
5517     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
5518     EXPECT_HR(hr, S_OK);
5519     ok(len == 5, "got %d\n", len);
5520     ok(!lstrcmpW(ptr, _bstr_("qname")), "got %s\n", wine_dbgstr_w(ptr));
5521 
5522     hr = IMXAttributes_clear(mxattr);
5523     EXPECT_HR(hr, S_OK);
5524 
5525     len = -1;
5526     hr = ISAXAttributes_getLength(saxattr, &len);
5527     EXPECT_HR(hr, S_OK);
5528     ok(len == 0, "got %d\n", len);
5529 
5530     len = -1;
5531     ptr = (void*)0xdeadbeef;
5532     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
5533     EXPECT_HR(hr, E_INVALIDARG);
5534     ok(len == -1, "got %d\n", len);
5535     ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
5536 
5537     IMXAttributes_Release(mxattr);
5538     ISAXAttributes_Release(saxattr);
5539     free_bstrs();
5540 }
5541 
5542 static void test_mxattr_dispex(void)
5543 {
5544     IMXAttributes *mxattr;
5545     IDispatchEx *dispex;
5546     IUnknown *unk;
5547     HRESULT hr;
5548 
5549     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
5550             &IID_IMXAttributes, (void**)&mxattr);
5551     EXPECT_HR(hr, S_OK);
5552 
5553     hr = IMXAttributes_QueryInterface(mxattr, &IID_IDispatchEx, (void**)&dispex);
5554     EXPECT_HR(hr, S_OK);
5555     hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
5556     test_obj_dispex(unk);
5557     IUnknown_Release(unk);
5558     IDispatchEx_Release(dispex);
5559 
5560     IMXAttributes_Release(mxattr);
5561 }
5562 
5563 static void test_mxattr_qi(void)
5564 {
5565     IVBSAXAttributes *vbsaxattr, *vbsaxattr2;
5566     ISAXAttributes *saxattr;
5567     IMXAttributes *mxattr;
5568     HRESULT hr;
5569 
5570     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
5571             &IID_IMXAttributes, (void**)&mxattr);
5572     EXPECT_HR(hr, S_OK);
5573 
5574     EXPECT_REF(mxattr, 1);
5575     hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5576     EXPECT_HR(hr, S_OK);
5577 
5578     EXPECT_REF(mxattr, 2);
5579     EXPECT_REF(saxattr, 2);
5580 
5581     hr = IMXAttributes_QueryInterface(mxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr);
5582     EXPECT_HR(hr, S_OK);
5583 
5584     EXPECT_REF(vbsaxattr, 3);
5585     EXPECT_REF(mxattr, 3);
5586     EXPECT_REF(saxattr, 3);
5587 
5588     hr = ISAXAttributes_QueryInterface(saxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr2);
5589     EXPECT_HR(hr, S_OK);
5590 
5591     EXPECT_REF(vbsaxattr, 4);
5592     EXPECT_REF(mxattr, 4);
5593     EXPECT_REF(saxattr, 4);
5594 
5595     IMXAttributes_Release(mxattr);
5596     ISAXAttributes_Release(saxattr);
5597     IVBSAXAttributes_Release(vbsaxattr);
5598     IVBSAXAttributes_Release(vbsaxattr2);
5599 }
5600 
5601 static struct msxmlsupported_data_t saxattr_support_data[] =
5602 {
5603     { &CLSID_SAXAttributes,   "SAXAttributes"   },
5604     { &CLSID_SAXAttributes30, "SAXAttributes30" },
5605     { &CLSID_SAXAttributes40, "SAXAttributes40" },
5606     { &CLSID_SAXAttributes60, "SAXAttributes60" },
5607     { NULL }
5608 };
5609 
5610 static void test_mxattr_localname(void)
5611 {
5612     static const WCHAR localname1W[] = {'l','o','c','a','l','n','a','m','e','1',0};
5613     static const WCHAR localnameW[] = {'l','o','c','a','l','n','a','m','e',0};
5614     static const WCHAR uri1W[] = {'u','r','i','1',0};
5615     static const WCHAR uriW[] = {'u','r','i',0};
5616 
5617     const struct msxmlsupported_data_t *table = saxattr_support_data;
5618 
5619     while (table->clsid)
5620     {
5621         ISAXAttributes *saxattr;
5622         IMXAttributes *mxattr;
5623         HRESULT hr;
5624         int index;
5625 
5626         if (!is_clsid_supported(table->clsid, mxattributes_support_data))
5627         {
5628             table++;
5629             continue;
5630         }
5631 
5632         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
5633             &IID_IMXAttributes, (void**)&mxattr);
5634         EXPECT_HR(hr, S_OK);
5635 
5636         hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5637         EXPECT_HR(hr, S_OK);
5638 
5639         hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, &index);
5640         EXPECT_HR(hr, E_INVALIDARG);
5641 
5642         /* add some ambiguos attribute names */
5643         hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
5644             _bstr_("a:localname"), _bstr_(""), _bstr_("value"));
5645         EXPECT_HR(hr, S_OK);
5646         hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
5647             _bstr_("b:localname"), _bstr_(""), _bstr_("value"));
5648         EXPECT_HR(hr, S_OK);
5649 
5650         index = -1;
5651         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localnameW, lstrlenW(localnameW), &index);
5652         EXPECT_HR(hr, S_OK);
5653         ok(index == 0, "%s: got index %d\n", table->name, index);
5654 
5655         index = -1;
5656         hr = ISAXAttributes_getIndexFromName(saxattr, uri1W, lstrlenW(uri1W), localnameW, lstrlenW(localnameW), &index);
5657         EXPECT_HR(hr, E_INVALIDARG);
5658         ok(index == -1, "%s: got index %d\n", table->name, index);
5659 
5660         index = -1;
5661         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), &index);
5662         EXPECT_HR(hr, E_INVALIDARG);
5663         ok(index == -1, "%s: got index %d\n", table->name, index);
5664 
5665         if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5666             IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5667         {
5668             hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
5669             EXPECT_HR(hr, E_POINTER);
5670 
5671             hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
5672             EXPECT_HR(hr, E_POINTER);
5673         }
5674         else
5675         {
5676             hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
5677             EXPECT_HR(hr, E_INVALIDARG);
5678 
5679             hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
5680             EXPECT_HR(hr, E_INVALIDARG);
5681         }
5682 
5683         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), NULL, 0, &index);
5684         EXPECT_HR(hr, E_INVALIDARG);
5685 
5686         hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, localname1W, lstrlenW(localname1W), &index);
5687         EXPECT_HR(hr, E_INVALIDARG);
5688 
5689         table++;
5690 
5691         ISAXAttributes_Release(saxattr);
5692         IMXAttributes_Release(mxattr);
5693         free_bstrs();
5694     }
5695 }
5696 
5697 static void test_mxwriter_indent(void)
5698 {
5699     ISAXContentHandler *content;
5700     IMXWriter *writer;
5701     VARIANT dest;
5702     HRESULT hr;
5703 
5704     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer);
5705     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
5706 
5707     hr = IMXWriter_put_indent(writer, VARIANT_TRUE);
5708     ok(hr == S_OK, "got %08x\n", hr);
5709 
5710     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
5711     ok(hr == S_OK, "got %08x\n", hr);
5712 
5713     hr = ISAXContentHandler_startDocument(content);
5714     ok(hr == S_OK, "got %08x\n", hr);
5715 
5716     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
5717     ok(hr == S_OK, "got %08x\n", hr);
5718 
5719     hr = ISAXContentHandler_characters(content, _bstr_(""), 0);
5720     ok(hr == S_OK, "got %08x\n", hr);
5721 
5722     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("b"), -1, NULL);
5723     ok(hr == S_OK, "got %08x\n", hr);
5724 
5725     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("c"), -1, NULL);
5726     ok(hr == S_OK, "got %08x\n", hr);
5727 
5728     hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("c"), -1);
5729     ok(hr == S_OK, "got %08x\n", hr);
5730 
5731     hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("b"), -1);
5732     ok(hr == S_OK, "got %08x\n", hr);
5733 
5734     hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1);
5735     ok(hr == S_OK, "got %08x\n", hr);
5736 
5737     hr = ISAXContentHandler_endDocument(content);
5738     ok(hr == S_OK, "got %08x\n", hr);
5739 
5740     V_VT(&dest) = VT_EMPTY;
5741     hr = IMXWriter_get_output(writer, &dest);
5742     ok(hr == S_OK, "got %08x\n", hr);
5743     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5744     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n<a><b>\r\n\t\t<c/>\r\n\t</b>\r\n</a>"), V_BSTR(&dest)),
5745         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5746     VariantClear(&dest);
5747 
5748     ISAXContentHandler_Release(content);
5749     IMXWriter_Release(writer);
5750 
5751     free_bstrs();
5752 }
5753 
5754 START_TEST(saxreader)
5755 {
5756     ISAXXMLReader *reader;
5757     HRESULT hr;
5758 
5759     hr = CoInitialize(NULL);
5760     ok(hr == S_OK, "failed to init com\n");
5761 
5762     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
5763             &IID_ISAXXMLReader, (void**)&reader);
5764 
5765     if(FAILED(hr))
5766     {
5767         win_skip("Failed to create SAXXMLReader instance\n");
5768         CoUninitialize();
5769         return;
5770     }
5771     ISAXXMLReader_Release(reader);
5772 
5773     init_call_sequences(sequences, NUM_CALL_SEQUENCES);
5774 
5775     get_class_support_data(reader_support_data, &IID_ISAXXMLReader);
5776 
5777     test_saxreader();
5778     test_saxreader_properties();
5779     test_saxreader_features();
5780     test_saxreader_encoding();
5781     test_saxreader_dispex();
5782 
5783     /* MXXMLWriter tests */
5784     get_class_support_data(mxwriter_support_data, &IID_IMXWriter);
5785     if (is_clsid_supported(&CLSID_MXXMLWriter, mxwriter_support_data))
5786     {
5787         test_mxwriter_handlers();
5788         test_mxwriter_startenddocument();
5789         test_mxwriter_startendelement();
5790         test_mxwriter_characters();
5791         test_mxwriter_comment();
5792         test_mxwriter_cdata();
5793         test_mxwriter_pi();
5794         test_mxwriter_ignorablespaces();
5795         test_mxwriter_dtd();
5796         test_mxwriter_properties();
5797         test_mxwriter_flush();
5798         test_mxwriter_stream();
5799         test_mxwriter_encoding();
5800         test_mxwriter_dispex();
5801         test_mxwriter_indent();
5802     }
5803     else
5804         win_skip("MXXMLWriter not supported\n");
5805 
5806     /* SAXAttributes tests */
5807     get_class_support_data(mxattributes_support_data, &IID_IMXAttributes);
5808     if (is_clsid_supported(&CLSID_SAXAttributes, mxattributes_support_data))
5809     {
5810         test_mxattr_qi();
5811         test_mxattr_addAttribute();
5812         test_mxattr_clear();
5813         test_mxattr_localname();
5814         test_mxattr_dispex();
5815     }
5816     else
5817         win_skip("SAXAttributes not supported\n");
5818 
5819     CoUninitialize();
5820 }
5821