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     init_call_entry(locator, &call);
1166     call.id = CH_STARTPREFIXMAPPING;
1167     call.arg1W = SysAllocStringLen(prefix, prefix_len);
1168     call.arg2W = SysAllocStringLen(uri, uri_len);
1169     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1170 
1171     return get_expected_ret();
1172 }
1173 
1174 static HRESULT WINAPI contentHandler_endPrefixMapping(
1175         ISAXContentHandler* iface,
1176         const WCHAR *prefix, int len)
1177 {
1178     struct call_entry call;
1179 
1180     init_call_entry(locator, &call);
1181     call.id = CH_ENDPREFIXMAPPING;
1182     call.arg1W = SysAllocStringLen(prefix, len);
1183     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1184 
1185     return get_expected_ret();
1186 }
1187 
1188 static HRESULT WINAPI contentHandler_startElement(
1189         ISAXContentHandler* iface,
1190         const WCHAR *uri, int uri_len,
1191         const WCHAR *localname, int local_len,
1192         const WCHAR *qname, int qname_len,
1193         ISAXAttributes *saxattr)
1194 {
1195     struct call_entry call;
1196     IMXAttributes *mxattr;
1197     HRESULT hr;
1198     int len;
1199 
1200     hr = ISAXAttributes_QueryInterface(saxattr, &IID_IMXAttributes, (void**)&mxattr);
1201     EXPECT_HR(hr, E_NOINTERFACE);
1202 
1203     init_call_entry(locator, &call);
1204     call.id = CH_STARTELEMENT;
1205     call.arg1W = SysAllocStringLen(uri, uri_len);
1206     call.arg2W = SysAllocStringLen(localname, local_len);
1207     call.arg3W = SysAllocStringLen(qname, qname_len);
1208 
1209     if(!test_attr_ptr)
1210         test_attr_ptr = saxattr;
1211     ok(test_attr_ptr == saxattr, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr);
1212 
1213     /* store actual attributes */
1214     len = 0;
1215     hr = ISAXAttributes_getLength(saxattr, &len);
1216     EXPECT_HR(hr, S_OK);
1217 
1218     if (len)
1219     {
1220         VARIANT_BOOL v;
1221         int i;
1222 
1223         struct attribute_entry *attr;
1224         attr = heap_alloc_zero(len * sizeof(*attr));
1225 
1226         v = VARIANT_TRUE;
1227         hr = ISAXXMLReader_getFeature(g_reader, _bstr_("http://xml.org/sax/features/namespaces"), &v);
1228         EXPECT_HR(hr, S_OK);
1229 
1230         for (i = 0; i < len; i++)
1231         {
1232             const WCHAR *value;
1233             int value_len;
1234 
1235             hr = ISAXAttributes_getName(saxattr, i, &uri, &uri_len,
1236                 &localname, &local_len, &qname, &qname_len);
1237             EXPECT_HR(hr, S_OK);
1238 
1239             hr = ISAXAttributes_getValue(saxattr, i, &value, &value_len);
1240             EXPECT_HR(hr, S_OK);
1241 
1242             /* if 'namespaces' switched off uri and local name contains garbage */
1243             if (v == VARIANT_FALSE && msxml_version > 0)
1244             {
1245                 attr[i].uriW   = SysAllocStringLen(NULL, 0);
1246                 attr[i].localW = SysAllocStringLen(NULL, 0);
1247             }
1248             else
1249             {
1250                 attr[i].uriW   = SysAllocStringLen(uri, uri_len);
1251                 attr[i].localW = SysAllocStringLen(localname, local_len);
1252             }
1253 
1254             attr[i].qnameW = SysAllocStringLen(qname, qname_len);
1255             attr[i].valueW = SysAllocStringLen(value, value_len);
1256         }
1257 
1258         call.attributes = attr;
1259         call.attr_count = len;
1260     }
1261 
1262     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1263 
1264     return get_expected_ret();
1265 }
1266 
1267 static HRESULT WINAPI contentHandler_endElement(
1268         ISAXContentHandler* iface,
1269         const WCHAR *uri, int uri_len,
1270         const WCHAR *localname, int local_len,
1271         const WCHAR *qname, int qname_len)
1272 {
1273     struct call_entry call;
1274 
1275     init_call_entry(locator, &call);
1276     call.id = CH_ENDELEMENT;
1277     call.arg1W = SysAllocStringLen(uri, uri_len);
1278     call.arg2W = SysAllocStringLen(localname, local_len);
1279     call.arg3W = SysAllocStringLen(qname, qname_len);
1280     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1281 
1282     return get_expected_ret();
1283 }
1284 
1285 static HRESULT WINAPI contentHandler_characters(
1286         ISAXContentHandler* iface,
1287         const WCHAR *chars,
1288         int len)
1289 {
1290     struct call_entry call;
1291 
1292     init_call_entry(locator, &call);
1293     call.id = CH_CHARACTERS;
1294     call.arg1W = SysAllocStringLen(chars, len);
1295     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1296 
1297     return get_expected_ret();
1298 }
1299 
1300 static HRESULT WINAPI contentHandler_ignorableWhitespace(
1301         ISAXContentHandler* iface,
1302         const WCHAR *chars, int len)
1303 {
1304     struct call_entry call;
1305 
1306     init_call_entry(locator, &call);
1307     call.id = CH_IGNORABLEWHITESPACE;
1308     call.arg1W = SysAllocStringLen(chars, len);
1309     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1310 
1311     return get_expected_ret();
1312 }
1313 
1314 static HRESULT WINAPI contentHandler_processingInstruction(
1315         ISAXContentHandler* iface,
1316         const WCHAR *target, int target_len,
1317         const WCHAR *data, int data_len)
1318 {
1319     struct call_entry call;
1320 
1321     init_call_entry(locator, &call);
1322     call.id = CH_PROCESSINGINSTRUCTION;
1323     call.arg1W = SysAllocStringLen(target, target_len);
1324     call.arg2W = SysAllocStringLen(data, data_len);
1325     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1326 
1327     return get_expected_ret();
1328 }
1329 
1330 static HRESULT WINAPI contentHandler_skippedEntity(
1331         ISAXContentHandler* iface,
1332         const WCHAR *name, int len)
1333 {
1334     struct call_entry call;
1335 
1336     init_call_entry(locator, &call);
1337     call.id = CH_SKIPPEDENTITY;
1338     call.arg1W = SysAllocStringLen(name, len);
1339     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1340 
1341     return get_expected_ret();
1342 }
1343 
1344 static const ISAXContentHandlerVtbl contentHandlerVtbl =
1345 {
1346     contentHandler_QueryInterface,
1347     contentHandler_AddRef,
1348     contentHandler_Release,
1349     contentHandler_putDocumentLocator,
1350     contentHandler_startDocument,
1351     contentHandler_endDocument,
1352     contentHandler_startPrefixMapping,
1353     contentHandler_endPrefixMapping,
1354     contentHandler_startElement,
1355     contentHandler_endElement,
1356     contentHandler_characters,
1357     contentHandler_ignorableWhitespace,
1358     contentHandler_processingInstruction,
1359     contentHandler_skippedEntity
1360 };
1361 
1362 static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
1363 
1364 static HRESULT WINAPI isaxerrorHandler_QueryInterface(
1365         ISAXErrorHandler* iface,
1366         REFIID riid,
1367         void **ppvObject)
1368 {
1369     *ppvObject = NULL;
1370 
1371     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
1372     {
1373         *ppvObject = iface;
1374     }
1375     else
1376     {
1377         return E_NOINTERFACE;
1378     }
1379 
1380     return S_OK;
1381 }
1382 
1383 static ULONG WINAPI isaxerrorHandler_AddRef(
1384         ISAXErrorHandler* iface)
1385 {
1386     return 2;
1387 }
1388 
1389 static ULONG WINAPI isaxerrorHandler_Release(
1390         ISAXErrorHandler* iface)
1391 {
1392     return 1;
1393 }
1394 
1395 static HRESULT WINAPI isaxerrorHandler_error(
1396         ISAXErrorHandler* iface,
1397         ISAXLocator *pLocator,
1398         const WCHAR *pErrorMessage,
1399         HRESULT hrErrorCode)
1400 {
1401     ok(0, "unexpected call\n");
1402     return S_OK;
1403 }
1404 
1405 static HRESULT WINAPI isaxerrorHandler_fatalError(
1406         ISAXErrorHandler* iface,
1407         ISAXLocator *pLocator,
1408         const WCHAR *message,
1409         HRESULT hr)
1410 {
1411     struct call_entry call;
1412 
1413     init_call_entry(locator, &call);
1414     call.id  = EH_FATALERROR;
1415     call.ret = hr;
1416 
1417     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1418 
1419     get_expected_ret();
1420     return S_OK;
1421 }
1422 
1423 static HRESULT WINAPI isaxerrorHandler_ignorableWarning(
1424         ISAXErrorHandler* iface,
1425         ISAXLocator *pLocator,
1426         const WCHAR *pErrorMessage,
1427         HRESULT hrErrorCode)
1428 {
1429     ok(0, "unexpected call\n");
1430     return S_OK;
1431 }
1432 
1433 static const ISAXErrorHandlerVtbl errorHandlerVtbl =
1434 {
1435     isaxerrorHandler_QueryInterface,
1436     isaxerrorHandler_AddRef,
1437     isaxerrorHandler_Release,
1438     isaxerrorHandler_error,
1439     isaxerrorHandler_fatalError,
1440     isaxerrorHandler_ignorableWarning
1441 };
1442 
1443 static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
1444 
1445 static HRESULT WINAPI isaxattributes_QueryInterface(
1446         ISAXAttributes* iface,
1447         REFIID riid,
1448         void **ppvObject)
1449 {
1450     *ppvObject = NULL;
1451 
1452     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
1453     {
1454         *ppvObject = iface;
1455     }
1456     else
1457     {
1458         return E_NOINTERFACE;
1459     }
1460 
1461     return S_OK;
1462 }
1463 
1464 static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
1465 {
1466     return 2;
1467 }
1468 
1469 static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
1470 {
1471     return 1;
1472 }
1473 
1474 static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
1475 {
1476     *length = 3;
1477     return S_OK;
1478 }
1479 
1480 static HRESULT WINAPI isaxattributes_getURI(
1481     ISAXAttributes* iface,
1482     int nIndex,
1483     const WCHAR **pUrl,
1484     int *pUriSize)
1485 {
1486     ok(0, "unexpected call\n");
1487     return E_NOTIMPL;
1488 }
1489 
1490 static HRESULT WINAPI isaxattributes_getLocalName(
1491     ISAXAttributes* iface,
1492     int nIndex,
1493     const WCHAR **pLocalName,
1494     int *pLocalNameLength)
1495 {
1496     ok(0, "unexpected call\n");
1497     return E_NOTIMPL;
1498 }
1499 
1500 static HRESULT WINAPI isaxattributes_getQName(
1501     ISAXAttributes* iface,
1502     int index,
1503     const WCHAR **QName,
1504     int *QNameLength)
1505 {
1506     static const WCHAR attrqnamesW[][15] = {{'a',':','a','t','t','r','1','j','u','n','k',0},
1507                                             {'a','t','t','r','2','j','u','n','k',0},
1508                                             {'a','t','t','r','3',0}};
1509     static const int attrqnamelen[] = {7, 5, 5};
1510 
1511     ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1512 
1513     if (index >= 0 && index <= 2) {
1514         *QName = attrqnamesW[index];
1515         *QNameLength = attrqnamelen[index];
1516     } else {
1517         *QName = NULL;
1518         *QNameLength = 0;
1519     }
1520 
1521     return S_OK;
1522 }
1523 
1524 static HRESULT WINAPI isaxattributes_getName(
1525     ISAXAttributes* iface,
1526     int nIndex,
1527     const WCHAR **pUri,
1528     int * pUriLength,
1529     const WCHAR ** pLocalName,
1530     int * pLocalNameSize,
1531     const WCHAR ** pQName,
1532     int * pQNameLength)
1533 {
1534     ok(0, "unexpected call\n");
1535     return E_NOTIMPL;
1536 }
1537 
1538 static HRESULT WINAPI isaxattributes_getIndexFromName(
1539     ISAXAttributes* iface,
1540     const WCHAR * pUri,
1541     int cUriLength,
1542     const WCHAR * pLocalName,
1543     int cocalNameLength,
1544     int * index)
1545 {
1546     ok(0, "unexpected call\n");
1547     return E_NOTIMPL;
1548 }
1549 
1550 static HRESULT WINAPI isaxattributes_getIndexFromQName(
1551     ISAXAttributes* iface,
1552     const WCHAR * pQName,
1553     int nQNameLength,
1554     int * index)
1555 {
1556     ok(0, "unexpected call\n");
1557     return E_NOTIMPL;
1558 }
1559 
1560 static HRESULT WINAPI isaxattributes_getType(
1561     ISAXAttributes* iface,
1562     int nIndex,
1563     const WCHAR ** pType,
1564     int * pTypeLength)
1565 {
1566     ok(0, "unexpected call\n");
1567     return E_NOTIMPL;
1568 }
1569 
1570 static HRESULT WINAPI isaxattributes_getTypeFromName(
1571     ISAXAttributes* iface,
1572     const WCHAR * pUri,
1573     int nUri,
1574     const WCHAR * pLocalName,
1575     int nLocalName,
1576     const WCHAR ** pType,
1577     int * nType)
1578 {
1579     ok(0, "unexpected call\n");
1580     return E_NOTIMPL;
1581 }
1582 
1583 static HRESULT WINAPI isaxattributes_getTypeFromQName(
1584     ISAXAttributes* iface,
1585     const WCHAR * pQName,
1586     int nQName,
1587     const WCHAR ** pType,
1588     int * nType)
1589 {
1590     ok(0, "unexpected call\n");
1591     return E_NOTIMPL;
1592 }
1593 
1594 static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes* iface, int index,
1595     const WCHAR **value, int *nValue)
1596 {
1597     static const WCHAR attrvaluesW[][10] = {{'a','1','j','u','n','k',0},
1598                                             {'a','2','j','u','n','k',0},
1599                                             {'<','&','"','>','\'',0}};
1600     static const int attrvalueslen[] = {2, 2, 5};
1601 
1602     ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1603 
1604     if (index >= 0 && index <= 2) {
1605         *value = attrvaluesW[index];
1606         *nValue = attrvalueslen[index];
1607     } else {
1608         *value = NULL;
1609         *nValue = 0;
1610     }
1611 
1612     return S_OK;
1613 }
1614 
1615 static HRESULT WINAPI isaxattributes_getValueFromName(
1616     ISAXAttributes* iface,
1617     const WCHAR * pUri,
1618     int nUri,
1619     const WCHAR * pLocalName,
1620     int nLocalName,
1621     const WCHAR ** pValue,
1622     int * nValue)
1623 {
1624     ok(0, "unexpected call\n");
1625     return E_NOTIMPL;
1626 }
1627 
1628 static HRESULT WINAPI isaxattributes_getValueFromQName(
1629     ISAXAttributes* iface,
1630     const WCHAR * pQName,
1631     int nQName,
1632     const WCHAR ** pValue,
1633     int * nValue)
1634 {
1635     ok(0, "unexpected call\n");
1636     return E_NOTIMPL;
1637 }
1638 
1639 static const ISAXAttributesVtbl SAXAttributesVtbl =
1640 {
1641     isaxattributes_QueryInterface,
1642     isaxattributes_AddRef,
1643     isaxattributes_Release,
1644     isaxattributes_getLength,
1645     isaxattributes_getURI,
1646     isaxattributes_getLocalName,
1647     isaxattributes_getQName,
1648     isaxattributes_getName,
1649     isaxattributes_getIndexFromName,
1650     isaxattributes_getIndexFromQName,
1651     isaxattributes_getType,
1652     isaxattributes_getTypeFromName,
1653     isaxattributes_getTypeFromQName,
1654     isaxattributes_getValue,
1655     isaxattributes_getValueFromName,
1656     isaxattributes_getValueFromQName
1657 };
1658 
1659 static ISAXAttributes saxattributes = { &SAXAttributesVtbl };
1660 
1661 struct saxlexicalhandler
1662 {
1663     ISAXLexicalHandler ISAXLexicalHandler_iface;
1664     LONG ref;
1665 
1666     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1667 };
1668 
1669 static inline struct saxlexicalhandler *impl_from_ISAXLexicalHandler( ISAXLexicalHandler *iface )
1670 {
1671     return CONTAINING_RECORD(iface, struct saxlexicalhandler, ISAXLexicalHandler_iface);
1672 }
1673 
1674 static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out)
1675 {
1676     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1677 
1678     *out = NULL;
1679 
1680     if (IsEqualGUID(riid, &IID_IUnknown))
1681     {
1682         *out = iface;
1683         ok(0, "got unexpected IID_IUnknown query\n");
1684     }
1685     else if (IsEqualGUID(riid, &IID_ISAXLexicalHandler))
1686     {
1687         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1688         *out = iface;
1689     }
1690 
1691     if (*out)
1692         ISAXLexicalHandler_AddRef(iface);
1693     else
1694         return E_NOINTERFACE;
1695 
1696     return S_OK;
1697 }
1698 
1699 static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface)
1700 {
1701     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1702     return InterlockedIncrement(&handler->ref);
1703 }
1704 
1705 static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface)
1706 {
1707     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1708     return InterlockedDecrement(&handler->ref);
1709 }
1710 
1711 static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
1712     const WCHAR * pName, int nName, const WCHAR * pPublicId,
1713     int nPublicId, const WCHAR * pSystemId, int nSystemId)
1714 {
1715     ok(0, "call not expected\n");
1716     return E_NOTIMPL;
1717 }
1718 
1719 static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface)
1720 {
1721     ok(0, "call not expected\n");
1722     return E_NOTIMPL;
1723 }
1724 
1725 static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface,
1726     const WCHAR * pName, int nName)
1727 {
1728     ok(0, "call not expected\n");
1729     return E_NOTIMPL;
1730 }
1731 
1732 static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface,
1733     const WCHAR * pName, int nName)
1734 {
1735     ok(0, "call not expected\n");
1736     return E_NOTIMPL;
1737 }
1738 
1739 static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
1740 {
1741     struct call_entry call;
1742 
1743     init_call_entry(locator, &call);
1744     call.id = LH_STARTCDATA;
1745     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1746 
1747     return get_expected_ret();
1748 }
1749 
1750 static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
1751 {
1752     struct call_entry call;
1753 
1754     init_call_entry(locator, &call);
1755     call.id = LH_ENDCDATA;
1756     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1757 
1758     return get_expected_ret();
1759 }
1760 
1761 static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface,
1762     const WCHAR * pChars, int nChars)
1763 {
1764     ok(0, "call not expected\n");
1765     return E_NOTIMPL;
1766 }
1767 
1768 static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1769 {
1770    isaxlexical_QueryInterface,
1771    isaxlexical_AddRef,
1772    isaxlexical_Release,
1773    isaxlexical_startDTD,
1774    isaxlexical_endDTD,
1775    isaxlexical_startEntity,
1776    isaxlexical_endEntity,
1777    isaxlexical_startCDATA,
1778    isaxlexical_endCDATA,
1779    isaxlexical_comment
1780 };
1781 
1782 static void init_saxlexicalhandler(struct saxlexicalhandler *handler, HRESULT hr)
1783 {
1784     handler->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1785     handler->ref = 1;
1786     handler->qi_hr = hr;
1787 }
1788 
1789 struct saxdeclhandler
1790 {
1791     ISAXDeclHandler ISAXDeclHandler_iface;
1792     LONG ref;
1793 
1794     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1795 };
1796 
1797 static inline struct saxdeclhandler *impl_from_ISAXDeclHandler( ISAXDeclHandler *iface )
1798 {
1799     return CONTAINING_RECORD(iface, struct saxdeclhandler, ISAXDeclHandler_iface);
1800 }
1801 
1802 static HRESULT WINAPI isaxdecl_QueryInterface(ISAXDeclHandler* iface, REFIID riid, void **out)
1803 {
1804     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1805 
1806     *out = NULL;
1807 
1808     if (IsEqualGUID(riid, &IID_IUnknown))
1809     {
1810         *out = iface;
1811         ok(0, "got unexpected IID_IUnknown query\n");
1812     }
1813     else if (IsEqualGUID(riid, &IID_ISAXDeclHandler))
1814     {
1815         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1816         *out = iface;
1817     }
1818 
1819     if (*out)
1820         ISAXDeclHandler_AddRef(iface);
1821     else
1822         return E_NOINTERFACE;
1823 
1824     return S_OK;
1825 }
1826 
1827 static ULONG WINAPI isaxdecl_AddRef(ISAXDeclHandler* iface)
1828 {
1829     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1830     return InterlockedIncrement(&handler->ref);
1831 }
1832 
1833 static ULONG WINAPI isaxdecl_Release(ISAXDeclHandler* iface)
1834 {
1835     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1836     return InterlockedDecrement(&handler->ref);
1837 }
1838 
1839 static HRESULT WINAPI isaxdecl_elementDecl(ISAXDeclHandler* iface,
1840     const WCHAR * pName, int nName, const WCHAR * pModel, int nModel)
1841 {
1842     ok(0, "call not expected\n");
1843     return E_NOTIMPL;
1844 }
1845 
1846 static HRESULT WINAPI isaxdecl_attributeDecl(ISAXDeclHandler* iface,
1847     const WCHAR * pElementName, int nElementName, const WCHAR * pAttributeName,
1848     int nAttributeName, const WCHAR * pType, int nType, const WCHAR * pValueDefault,
1849     int nValueDefault, const WCHAR * pValue, int nValue)
1850 {
1851     ok(0, "call not expected\n");
1852     return E_NOTIMPL;
1853 }
1854 
1855 static HRESULT WINAPI isaxdecl_internalEntityDecl(ISAXDeclHandler* iface,
1856     const WCHAR * pName, int nName, const WCHAR * pValue, int nValue)
1857 {
1858     ok(0, "call not expected\n");
1859     return E_NOTIMPL;
1860 }
1861 
1862 static HRESULT WINAPI isaxdecl_externalEntityDecl(ISAXDeclHandler* iface,
1863     const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId,
1864     const WCHAR * pSystemId, int nSystemId)
1865 {
1866     ok(0, "call not expected\n");
1867     return E_NOTIMPL;
1868 }
1869 
1870 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl =
1871 {
1872    isaxdecl_QueryInterface,
1873    isaxdecl_AddRef,
1874    isaxdecl_Release,
1875    isaxdecl_elementDecl,
1876    isaxdecl_attributeDecl,
1877    isaxdecl_internalEntityDecl,
1878    isaxdecl_externalEntityDecl
1879 };
1880 
1881 static void init_saxdeclhandler(struct saxdeclhandler *handler, HRESULT hr)
1882 {
1883     handler->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1884     handler->ref = 1;
1885     handler->qi_hr = hr;
1886 }
1887 
1888 typedef struct mxwriter_write_test_t {
1889     BOOL        last;
1890     const BYTE  *data;
1891     DWORD       cb;
1892     BOOL        null_written;
1893     BOOL        fail_write;
1894 } mxwriter_write_test;
1895 
1896 typedef struct mxwriter_stream_test_t {
1897     VARIANT_BOOL        bom;
1898     const char          *encoding;
1899     mxwriter_write_test expected_writes[4];
1900 } mxwriter_stream_test;
1901 
1902 static const mxwriter_write_test *current_write_test;
1903 static DWORD current_stream_test_index;
1904 
1905 static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject)
1906 {
1907     *ppvObject = NULL;
1908 
1909     ok(!IsEqualGUID(riid, &IID_IPersistStream), "Did not expect QI for IPersistStream\n");
1910 
1911     if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown))
1912         *ppvObject = iface;
1913     else
1914         return E_NOINTERFACE;
1915 
1916     return S_OK;
1917 }
1918 
1919 static ULONG WINAPI istream_AddRef(IStream *iface)
1920 {
1921     return 2;
1922 }
1923 
1924 static ULONG WINAPI istream_Release(IStream *iface)
1925 {
1926     return 1;
1927 }
1928 
1929 static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
1930 {
1931     ok(0, "unexpected call\n");
1932     return E_NOTIMPL;
1933 }
1934 
1935 static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
1936 {
1937     ok(0, "unexpected call\n");
1938     return E_NOTIMPL;
1939 }
1940 
1941 static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
1942         ULARGE_INTEGER *plibNewPosition)
1943 {
1944     ok(0, "unexpected call\n");
1945     return E_NOTIMPL;
1946 }
1947 
1948 static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
1949 {
1950     ok(0, "unexpected call\n");
1951     return E_NOTIMPL;
1952 }
1953 
1954 static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
1955         ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten)
1956 {
1957     ok(0, "unexpected call\n");
1958     return E_NOTIMPL;
1959 }
1960 
1961 static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags)
1962 {
1963     ok(0, "unexpected call\n");
1964     return E_NOTIMPL;
1965 }
1966 
1967 static HRESULT WINAPI istream_Revert(IStream *iface)
1968 {
1969     ok(0, "unexpected call\n");
1970     return E_NOTIMPL;
1971 }
1972 
1973 static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1974         ULARGE_INTEGER cb, DWORD dwLockType)
1975 {
1976     ok(0, "unexpected call\n");
1977     return E_NOTIMPL;
1978 }
1979 
1980 static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1981         ULARGE_INTEGER cb, DWORD dwLockType)
1982 {
1983     ok(0, "unexpected call\n");
1984     return E_NOTIMPL;
1985 }
1986 
1987 static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag)
1988 {
1989     return E_NOTIMPL;
1990 }
1991 
1992 static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm)
1993 {
1994     ok(0, "unexpected call\n");
1995     return E_NOTIMPL;
1996 }
1997 
1998 static HRESULT WINAPI mxstream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
1999 {
2000     BOOL fail = FALSE;
2001 
2002     ok(pv != NULL, "pv == NULL\n");
2003 
2004     if(current_write_test->last) {
2005         ok(0, "Too many Write calls made on test %d\n", current_stream_test_index);
2006         return E_FAIL;
2007     }
2008 
2009     fail = current_write_test->fail_write;
2010 
2011     ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n",
2012         current_write_test->cb, cb, current_stream_test_index);
2013 
2014     if(!pcbWritten)
2015         ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index);
2016     else
2017         ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index);
2018 
2019     ++current_write_test;
2020 
2021     if(pcbWritten)
2022         *pcbWritten = cb;
2023 
2024     return fail ? E_FAIL : S_OK;
2025 }
2026 
2027 static const IStreamVtbl mxstreamVtbl = {
2028     istream_QueryInterface,
2029     istream_AddRef,
2030     istream_Release,
2031     istream_Read,
2032     mxstream_Write,
2033     istream_Seek,
2034     istream_SetSize,
2035     istream_CopyTo,
2036     istream_Commit,
2037     istream_Revert,
2038     istream_LockRegion,
2039     istream_UnlockRegion,
2040     istream_Stat,
2041     istream_Clone
2042 };
2043 
2044 static IStream mxstream = { &mxstreamVtbl };
2045 
2046 static int read_cnt;
2047 
2048 static HRESULT WINAPI instream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
2049 {
2050     static const char *ret_str;
2051 
2052     if(!read_cnt)
2053         ret_str = "<?xml version=\"1.0\" ?>\n<rootelem>";
2054     else if(read_cnt < 5)
2055         ret_str = "<elem attr=\"val\">text</elem>";
2056     else if(read_cnt == 5)
2057         ret_str = "</rootelem>\n";
2058     else
2059         ret_str = "";
2060 
2061     read_cnt++;
2062     strcpy(pv, ret_str);
2063     *pcbRead = strlen(ret_str);
2064     return S_OK;
2065 }
2066 
2067 static const IStreamVtbl instreamVtbl = {
2068     istream_QueryInterface,
2069     istream_AddRef,
2070     istream_Release,
2071     instream_Read,
2072     istream_Write,
2073     istream_Seek,
2074     istream_SetSize,
2075     istream_CopyTo,
2076     istream_Commit,
2077     istream_Revert,
2078     istream_LockRegion,
2079     istream_UnlockRegion,
2080     istream_Stat,
2081     istream_Clone
2082 };
2083 
2084 static IStream instream = { &instreamVtbl };
2085 
2086 static struct msxmlsupported_data_t reader_support_data[] =
2087 {
2088     { &CLSID_SAXXMLReader,   "SAXReader"   },
2089     { &CLSID_SAXXMLReader30, "SAXReader30" },
2090     { &CLSID_SAXXMLReader40, "SAXReader40" },
2091     { &CLSID_SAXXMLReader60, "SAXReader60" },
2092     { NULL }
2093 };
2094 
2095 static struct saxlexicalhandler lexicalhandler;
2096 static struct saxdeclhandler declhandler;
2097 
2098 static IStream *create_test_stream(const char *data, int len)
2099 {
2100      ULARGE_INTEGER size;
2101      LARGE_INTEGER pos;
2102      IStream *stream;
2103      ULONG written;
2104 
2105      if (len == -1) len = strlen(data);
2106      CreateStreamOnHGlobal(NULL, TRUE, &stream);
2107      size.QuadPart = len;
2108      IStream_SetSize(stream, size);
2109      IStream_Write(stream, data, len, &written);
2110      pos.QuadPart = 0;
2111      IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2112 
2113      return stream;
2114 }
2115 
2116 static void test_saxreader(void)
2117 {
2118     const struct msxmlsupported_data_t *table = reader_support_data;
2119     HRESULT hr;
2120     ISAXXMLReader *reader = NULL;
2121     VARIANT var;
2122     ISAXContentHandler *content;
2123     ISAXErrorHandler *lpErrorHandler;
2124     SAFEARRAY *sa;
2125     SAFEARRAYBOUND SADim[1];
2126     char *ptr = NULL;
2127     IStream *stream;
2128     ULONG written;
2129     HANDLE file;
2130     static const CHAR testXmlA[] = "test.xml";
2131     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
2132     IXMLDOMDocument *doc;
2133     char seqname[50];
2134     VARIANT_BOOL v;
2135 
2136     while (table->clsid)
2137     {
2138         struct call_entry *test_seq;
2139         ISAXEntityResolver *resolver;
2140         BSTR str;
2141 
2142         if (!is_clsid_supported(table->clsid, reader_support_data))
2143         {
2144             table++;
2145             continue;
2146         }
2147 
2148         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2149         EXPECT_HR(hr, S_OK);
2150         g_reader = reader;
2151 
2152         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2153             msxml_version = 4;
2154         else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2155             msxml_version = 6;
2156         else
2157             msxml_version = 0;
2158 
2159         /* crashes on old versions */
2160         if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) &&
2161             !IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2162         {
2163             hr = ISAXXMLReader_getContentHandler(reader, NULL);
2164             EXPECT_HR(hr, E_POINTER);
2165 
2166             hr = ISAXXMLReader_getErrorHandler(reader, NULL);
2167             EXPECT_HR(hr, E_POINTER);
2168         }
2169 
2170         hr = ISAXXMLReader_getContentHandler(reader, &content);
2171         EXPECT_HR(hr, S_OK);
2172         ok(content == NULL, "Expected %p, got %p\n", NULL, content);
2173 
2174         hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
2175         EXPECT_HR(hr, S_OK);
2176         ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
2177 
2178         hr = ISAXXMLReader_putContentHandler(reader, NULL);
2179         EXPECT_HR(hr, S_OK);
2180 
2181         hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
2182         EXPECT_HR(hr, S_OK);
2183 
2184         hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
2185         EXPECT_HR(hr, S_OK);
2186 
2187         hr = ISAXXMLReader_getContentHandler(reader, &content);
2188         EXPECT_HR(hr, S_OK);
2189         ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content);
2190 
2191         V_VT(&var) = VT_BSTR;
2192         V_BSTR(&var) = SysAllocString(szSimpleXML);
2193 
2194         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2195             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2196             test_seq = content_handler_test1_alternate;
2197         else
2198             test_seq = content_handler_test1;
2199         set_expected_seq(test_seq);
2200         hr = ISAXXMLReader_parse(reader, var);
2201         EXPECT_HR(hr, S_OK);
2202         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE);
2203 
2204         VariantClear(&var);
2205 
2206         SADim[0].lLbound = 0;
2207         SADim[0].cElements = sizeof(testXML)-1;
2208         sa = SafeArrayCreate(VT_UI1, 1, SADim);
2209         SafeArrayAccessData(sa, (void**)&ptr);
2210         memcpy(ptr, testXML, sizeof(testXML)-1);
2211         SafeArrayUnaccessData(sa);
2212         V_VT(&var) = VT_ARRAY|VT_UI1;
2213         V_ARRAY(&var) = sa;
2214 
2215         set_expected_seq(test_seq);
2216         hr = ISAXXMLReader_parse(reader, var);
2217         EXPECT_HR(hr, S_OK);
2218         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE);
2219 
2220         SafeArrayDestroy(sa);
2221 
2222         V_VT(&var) = VT_UNKNOWN;
2223         V_UNKNOWN(&var) = NULL;
2224         hr = ISAXXMLReader_parse(reader, var);
2225         ok(hr == E_INVALIDARG, "got %#x\n", hr);
2226 
2227         V_VT(&var) = VT_DISPATCH;
2228         V_DISPATCH(&var) = NULL;
2229         hr = ISAXXMLReader_parse(reader, var);
2230         ok(hr == E_INVALIDARG, "got %#x\n", hr);
2231 
2232         stream = create_test_stream(testXML, -1);
2233         V_VT(&var) = VT_UNKNOWN;
2234         V_UNKNOWN(&var) = (IUnknown*)stream;
2235 
2236         set_expected_seq(test_seq);
2237         hr = ISAXXMLReader_parse(reader, var);
2238         EXPECT_HR(hr, S_OK);
2239         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE);
2240 
2241         IStream_Release(stream);
2242 
2243         stream = create_test_stream(test_attributes, -1);
2244         V_VT(&var) = VT_UNKNOWN;
2245         V_UNKNOWN(&var) = (IUnknown*)stream;
2246 
2247         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2248             test_seq = content_handler_test_attributes_alternate_4;
2249         else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2250             test_seq = content_handler_test_attributes_alternate_6;
2251         else
2252             test_seq = content_handler_test_attributes;
2253 
2254         set_expected_seq(test_seq);
2255         hr = ISAXXMLReader_parse(reader, var);
2256         EXPECT_HR(hr, S_OK);
2257 
2258         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2259             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2260             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2261         else
2262             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2263 
2264         IStream_Release(stream);
2265 
2266         V_VT(&var) = VT_UNKNOWN;
2267         V_UNKNOWN(&var) = (IUnknown*)&instream;
2268 
2269         test_seq = read_test_seq;
2270         read_cnt = 0;
2271         set_expected_seq(test_seq);
2272         hr = ISAXXMLReader_parse(reader, var);
2273         EXPECT_HR(hr, S_OK);
2274         ok(read_cnt == 7, "read_cnt = %d\n", read_cnt);
2275         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "Read call test", FALSE);
2276 
2277         V_VT(&var) = VT_BSTR;
2278         V_BSTR(&var) = SysAllocString(carriage_ret_test);
2279 
2280         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2281             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2282             test_seq = content_handler_test2_alternate;
2283         else
2284             test_seq = content_handler_test2;
2285 
2286         set_expected_seq(test_seq);
2287         hr = ISAXXMLReader_parse(reader, var);
2288         EXPECT_HR(hr, S_OK);
2289         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE);
2290 
2291         VariantClear(&var);
2292 
2293         /* from file url */
2294         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2295         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2296         WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL);
2297         CloseHandle(file);
2298 
2299         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2300             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2301             test_seq = content_handler_test1_alternate;
2302         else
2303             test_seq = content_handler_test1;
2304         set_expected_seq(test_seq);
2305         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2306         EXPECT_HR(hr, S_OK);
2307         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE);
2308 
2309         /* error handler */
2310         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2311             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2312             test_seq = content_handler_testerror_alternate;
2313         else
2314             test_seq = content_handler_testerror;
2315         set_expected_seq(test_seq);
2316         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2317         EXPECT_HR(hr, E_FAIL);
2318         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE);
2319 
2320         /* callback ret values */
2321         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2322             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2323         {
2324             test_seq = content_handler_test_callback_rets_alt;
2325             set_expected_seq(test_seq);
2326             hr = ISAXXMLReader_parseURL(reader, testXmlW);
2327             EXPECT_HR(hr, S_OK);
2328         }
2329         else
2330         {
2331             test_seq = content_handler_test_callback_rets;
2332             set_expected_seq(test_seq);
2333             hr = ISAXXMLReader_parseURL(reader, testXmlW);
2334             EXPECT_HR(hr, S_FALSE);
2335         }
2336         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE);
2337 
2338         DeleteFileA(testXmlA);
2339 
2340         /* parse from IXMLDOMDocument */
2341         hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
2342                 &IID_IXMLDOMDocument, (void**)&doc);
2343         EXPECT_HR(hr, S_OK);
2344 
2345         str = SysAllocString(szSimpleXML);
2346         hr = IXMLDOMDocument_loadXML(doc, str, &v);
2347         EXPECT_HR(hr, S_OK);
2348         SysFreeString(str);
2349 
2350         V_VT(&var) = VT_UNKNOWN;
2351         V_UNKNOWN(&var) = (IUnknown*)doc;
2352 
2353         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2354             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2355             test_seq = content_handler_test2_alternate;
2356         else
2357             test_seq = content_handler_test2;
2358 
2359         set_expected_seq(test_seq);
2360         hr = ISAXXMLReader_parse(reader, var);
2361         EXPECT_HR(hr, S_OK);
2362         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE);
2363         IXMLDOMDocument_Release(doc);
2364 
2365         /* xml:space test */
2366         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2367             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2368         {
2369             test_seq = xmlspaceattr_test_alternate;
2370         }
2371         else
2372             test_seq = xmlspaceattr_test;
2373 
2374         set_expected_seq(test_seq);
2375         V_VT(&var) = VT_BSTR;
2376         V_BSTR(&var) = _bstr_(xmlspace_attr);
2377         hr = ISAXXMLReader_parse(reader, var);
2378         EXPECT_HR(hr, S_OK);
2379 
2380         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2381             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2382         {
2383             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE);
2384         }
2385         else
2386             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE);
2387 
2388         /* switch off 'namespaces' feature */
2389         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_FALSE);
2390         EXPECT_HR(hr, S_OK);
2391 
2392         stream = create_test_stream(test_attributes, -1);
2393         V_VT(&var) = VT_UNKNOWN;
2394         V_UNKNOWN(&var) = (IUnknown*)stream;
2395 
2396         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2397             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2398         {
2399             test_seq = content_handler_test_attributes_alt_no_ns;
2400         }
2401         else
2402             test_seq = content_handler_test_attributes;
2403 
2404         set_expected_seq(test_seq);
2405         hr = ISAXXMLReader_parse(reader, var);
2406         EXPECT_HR(hr, S_OK);
2407         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2408         IStream_Release(stream);
2409         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_TRUE);
2410         EXPECT_HR(hr, S_OK);
2411 
2412         /* switch off 'namespace-prefixes' feature */
2413         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_FALSE);
2414         EXPECT_HR(hr, S_OK);
2415 
2416         stream = create_test_stream(test_attributes, -1);
2417         V_VT(&var) = VT_UNKNOWN;
2418         V_UNKNOWN(&var) = (IUnknown*)stream;
2419 
2420         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2421             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2422         {
2423             test_seq = content_handler_test_attributes_alt_no_prefix;
2424         }
2425         else
2426             test_seq = content_handler_test_attributes_no_prefix;
2427 
2428         set_expected_seq(test_seq);
2429         hr = ISAXXMLReader_parse(reader, var);
2430         EXPECT_HR(hr, S_OK);
2431         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2432         IStream_Release(stream);
2433 
2434         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_TRUE);
2435         EXPECT_HR(hr, S_OK);
2436 
2437         /* attribute normalization */
2438         stream = create_test_stream(attribute_normalize, -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 = attribute_norm_alt;
2446         }
2447         else
2448             test_seq = attribute_norm;
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, "attribute value normalization", TRUE);
2454         IStream_Release(stream);
2455 
2456         resolver = (void*)0xdeadbeef;
2457         hr = ISAXXMLReader_getEntityResolver(reader, &resolver);
2458         ok(hr == S_OK, "got 0x%08x\n", hr);
2459         ok(resolver == NULL, "got %p\n", resolver);
2460 
2461         hr = ISAXXMLReader_putEntityResolver(reader, NULL);
2462         ok(hr == S_OK || broken(hr == E_FAIL), "got 0x%08x\n", hr);
2463 
2464         /* CDATA sections */
2465         init_saxlexicalhandler(&lexicalhandler, S_OK);
2466 
2467         V_VT(&var) = VT_UNKNOWN;
2468         V_UNKNOWN(&var) = (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface;
2469         hr = ISAXXMLReader_putProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), var);
2470         ok(hr == S_OK, "got 0x%08x\n", hr);
2471 
2472         stream = create_test_stream(test_cdata_xml, -1);
2473         V_VT(&var) = VT_UNKNOWN;
2474         V_UNKNOWN(&var) = (IUnknown*)stream;
2475 
2476         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
2477             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2478             test_seq = cdata_test_alt;
2479         else
2480             test_seq = cdata_test;
2481 
2482         set_expected_seq(test_seq);
2483         hr = ISAXXMLReader_parse(reader, var);
2484         ok(hr == S_OK, "got 0x%08x\n", hr);
2485         sprintf(seqname, "%s: cdata test", table->name);
2486         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
2487 
2488         IStream_Release(stream);
2489 
2490         /* 2. CDATA sections */
2491         stream = create_test_stream(test2_cdata_xml, -1);
2492         V_VT(&var) = VT_UNKNOWN;
2493         V_UNKNOWN(&var) = (IUnknown*)stream;
2494 
2495         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
2496             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2497             test_seq = cdata_test2_alt;
2498         else
2499             test_seq = cdata_test2;
2500 
2501         set_expected_seq(test_seq);
2502         hr = ISAXXMLReader_parse(reader, var);
2503         ok(hr == S_OK, "got 0x%08x\n", hr);
2504         sprintf(seqname, "%s: cdata test 2", table->name);
2505         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
2506 
2507         IStream_Release(stream);
2508 
2509         /* 3. CDATA sections */
2510         stream = create_test_stream(test3_cdata_xml, -1);
2511         V_VT(&var) = VT_UNKNOWN;
2512         V_UNKNOWN(&var) = (IUnknown*)stream;
2513 
2514         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) ||
2515             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2516             test_seq = cdata_test3_alt;
2517         else
2518             test_seq = cdata_test3;
2519 
2520         set_expected_seq(test_seq);
2521         hr = ISAXXMLReader_parse(reader, var);
2522         ok(hr == S_OK, "got 0x%08x\n", hr);
2523         sprintf(seqname, "%s: cdata test 3", table->name);
2524         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE);
2525 
2526         IStream_Release(stream);
2527 
2528         ISAXXMLReader_Release(reader);
2529         table++;
2530     }
2531 
2532     free_bstrs();
2533 }
2534 
2535 struct saxreader_props_test_t
2536 {
2537     const char *prop_name;
2538     IUnknown   *iface;
2539 };
2540 
2541 static const struct saxreader_props_test_t props_test_data[] = {
2542     { "http://xml.org/sax/properties/lexical-handler", (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface },
2543     { "http://xml.org/sax/properties/declaration-handler", (IUnknown*)&declhandler.ISAXDeclHandler_iface },
2544     { 0 }
2545 };
2546 
2547 static void test_saxreader_properties(void)
2548 {
2549     const struct saxreader_props_test_t *ptr = props_test_data;
2550     ISAXXMLReader *reader;
2551     HRESULT hr;
2552     VARIANT v;
2553     BSTR str;
2554 
2555     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
2556             &IID_ISAXXMLReader, (void**)&reader);
2557     EXPECT_HR(hr, S_OK);
2558 
2559     hr = ISAXXMLReader_getProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), NULL);
2560     EXPECT_HR(hr, E_POINTER);
2561 
2562     while (ptr->prop_name)
2563     {
2564         VARIANT varref;
2565         LONG ref;
2566 
2567         init_saxlexicalhandler(&lexicalhandler, S_OK);
2568         init_saxdeclhandler(&declhandler, S_OK);
2569 
2570         V_VT(&v) = VT_EMPTY;
2571         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2572         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2573         EXPECT_HR(hr, S_OK);
2574         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2575         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2576 
2577         /* VT_UNKNOWN */
2578         V_VT(&v) = VT_UNKNOWN;
2579         V_UNKNOWN(&v) = ptr->iface;
2580         ref = get_refcount(ptr->iface);
2581         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2582         EXPECT_HR(hr, S_OK);
2583         ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2584 
2585         /* VT_DISPATCH */
2586         V_VT(&v) = VT_DISPATCH;
2587         V_UNKNOWN(&v) = ptr->iface;
2588         ref = get_refcount(ptr->iface);
2589         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2590         EXPECT_HR(hr, S_OK);
2591         ok(ref == get_refcount(ptr->iface), "got wrong refcount %d, expected %d\n", get_refcount(ptr->iface), ref);
2592 
2593         /* VT_VARIANT|VT_BYREF with VT_UNKNOWN in referenced variant */
2594         V_VT(&varref) = VT_UNKNOWN;
2595         V_UNKNOWN(&varref) = ptr->iface;
2596 
2597         V_VT(&v) = VT_VARIANT|VT_BYREF;
2598         V_VARIANTREF(&v) = &varref;
2599         ref = get_refcount(ptr->iface);
2600         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2601         EXPECT_HR(hr, S_OK);
2602         ok(ref == get_refcount(ptr->iface), "got wrong refcount %d, expected %d\n", get_refcount(ptr->iface), ref);
2603 
2604         /* VT_VARIANT|VT_BYREF with VT_DISPATCH in referenced variant */
2605         V_VT(&varref) = VT_DISPATCH;
2606         V_UNKNOWN(&varref) = ptr->iface;
2607 
2608         V_VT(&v) = VT_VARIANT|VT_BYREF;
2609         V_VARIANTREF(&v) = &varref;
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         V_VT(&v) = VT_EMPTY;
2616         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2617 
2618         ref = get_refcount(ptr->iface);
2619         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2620         EXPECT_HR(hr, S_OK);
2621         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2622         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2623         ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2624         VariantClear(&v);
2625 
2626         V_VT(&v) = VT_EMPTY;
2627         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2628         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2629         EXPECT_HR(hr, S_OK);
2630 
2631         V_VT(&v) = VT_EMPTY;
2632         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2633         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2634         EXPECT_HR(hr, S_OK);
2635         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2636         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2637 
2638         V_VT(&v) = VT_UNKNOWN;
2639         V_UNKNOWN(&v) = ptr->iface;
2640         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2641         EXPECT_HR(hr, S_OK);
2642 
2643         /* only VT_EMPTY seems to be valid to reset property */
2644         V_VT(&v) = VT_I4;
2645         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2646         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2647         EXPECT_HR(hr, E_INVALIDARG);
2648 
2649         V_VT(&v) = VT_EMPTY;
2650         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2651         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2652         EXPECT_HR(hr, S_OK);
2653         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2654         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2655         VariantClear(&v);
2656 
2657         V_VT(&v) = VT_UNKNOWN;
2658         V_UNKNOWN(&v) = NULL;
2659         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2660         EXPECT_HR(hr, S_OK);
2661 
2662         V_VT(&v) = VT_EMPTY;
2663         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2664         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2665         EXPECT_HR(hr, S_OK);
2666         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2667         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2668 
2669         /* block QueryInterface on handler riid */
2670         V_VT(&v) = VT_UNKNOWN;
2671         V_UNKNOWN(&v) = ptr->iface;
2672         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2673         EXPECT_HR(hr, S_OK);
2674 
2675         init_saxlexicalhandler(&lexicalhandler, E_NOINTERFACE);
2676         init_saxdeclhandler(&declhandler, E_NOINTERFACE);
2677 
2678         V_VT(&v) = VT_UNKNOWN;
2679         V_UNKNOWN(&v) = ptr->iface;
2680         EXPECT_REF(ptr->iface, 1);
2681         ref = get_refcount(ptr->iface);
2682         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2683         EXPECT_HR(hr, E_NOINTERFACE);
2684         EXPECT_REF(ptr->iface, 1);
2685 
2686         V_VT(&v) = VT_EMPTY;
2687         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2688         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2689         EXPECT_HR(hr, S_OK);
2690         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2691         ok(V_UNKNOWN(&v) != NULL, "got %p\n", V_UNKNOWN(&v));
2692 
2693         ptr++;
2694         free_bstrs();
2695     }
2696 
2697     ISAXXMLReader_Release(reader);
2698 
2699     if (!is_clsid_supported(&CLSID_SAXXMLReader40, reader_support_data))
2700         return;
2701 
2702     hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER,
2703             &IID_ISAXXMLReader, (void**)&reader);
2704     EXPECT_HR(hr, S_OK);
2705 
2706     /* xmldecl-version property */
2707     V_VT(&v) = VT_EMPTY;
2708     V_BSTR(&v) = (void*)0xdeadbeef;
2709     hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2710     EXPECT_HR(hr, S_OK);
2711     ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2712     ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2713 
2714     /* stream without declaration */
2715     V_VT(&v) = VT_BSTR;
2716     V_BSTR(&v) = _bstr_("<element></element>");
2717     hr = ISAXXMLReader_parse(reader, v);
2718     EXPECT_HR(hr, S_OK);
2719 
2720     V_VT(&v) = VT_EMPTY;
2721     V_BSTR(&v) = (void*)0xdeadbeef;
2722     hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2723     EXPECT_HR(hr, S_OK);
2724     ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2725     ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2726 
2727     /* stream with declaration */
2728     V_VT(&v) = VT_BSTR;
2729     V_BSTR(&v) = _bstr_("<?xml version=\"1.0\"?><element></element>");
2730     hr = ISAXXMLReader_parse(reader, v);
2731     EXPECT_HR(hr, S_OK);
2732 
2733     /* VT_BSTR|VT_BYREF input type */
2734     str = _bstr_("<?xml version=\"1.0\"?><element></element>");
2735     V_VT(&v) = VT_BSTR|VT_BYREF;
2736     V_BSTRREF(&v) = &str;
2737     hr = ISAXXMLReader_parse(reader, v);
2738     EXPECT_HR(hr, S_OK);
2739 
2740     V_VT(&v) = VT_EMPTY;
2741     V_BSTR(&v) = (void*)0xdeadbeef;
2742     hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2743     EXPECT_HR(hr, S_OK);
2744     ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2745     ok(!lstrcmpW(V_BSTR(&v), _bstr_("1.0")), "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2746     VariantClear(&v);
2747 
2748     ISAXXMLReader_Release(reader);
2749     free_bstrs();
2750 }
2751 
2752 struct feature_ns_entry_t {
2753     const GUID *guid;
2754     const char *clsid;
2755     VARIANT_BOOL value;
2756     VARIANT_BOOL value2; /* feature value after feature set to 0xc */
2757 };
2758 
2759 static const struct feature_ns_entry_t feature_ns_entry_data[] = {
2760     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   VARIANT_TRUE, VARIANT_FALSE },
2761     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", VARIANT_TRUE, VARIANT_FALSE },
2762     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", VARIANT_TRUE, VARIANT_TRUE },
2763     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", VARIANT_TRUE, VARIANT_TRUE },
2764     { 0 }
2765 };
2766 
2767 static const char *feature_names[] = {
2768     "http://xml.org/sax/features/namespaces",
2769     "http://xml.org/sax/features/namespace-prefixes",
2770     0
2771 };
2772 
2773 static void test_saxreader_features(void)
2774 {
2775     const struct feature_ns_entry_t *entry = feature_ns_entry_data;
2776     ISAXXMLReader *reader;
2777 
2778     while (entry->guid)
2779     {
2780         VARIANT_BOOL value;
2781         const char **name;
2782         HRESULT hr;
2783 
2784         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2785         if (hr != S_OK)
2786         {
2787             win_skip("can't create %s instance\n", entry->clsid);
2788             entry++;
2789             continue;
2790         }
2791 
2792         if (IsEqualGUID(entry->guid, &CLSID_SAXXMLReader40) ||
2793                 IsEqualGUID(entry->guid, &CLSID_SAXXMLReader60))
2794         {
2795             value = VARIANT_TRUE;
2796             hr = ISAXXMLReader_getFeature(reader, _bstr_("exhaustive-errors"), &value);
2797             ok(hr == S_OK, "Failed to get feature value, hr %#x.\n", hr);
2798             ok(value == VARIANT_FALSE, "Unexpected default feature value.\n");
2799             hr = ISAXXMLReader_putFeature(reader, _bstr_("exhaustive-errors"), VARIANT_FALSE);
2800             ok(hr == S_OK, "Failed to put feature value, hr %#x.\n", hr);
2801 
2802             value = VARIANT_TRUE;
2803             hr = ISAXXMLReader_getFeature(reader, _bstr_("schema-validation"), &value);
2804             ok(hr == S_OK, "Failed to get feature value, hr %#x.\n", hr);
2805             ok(value == VARIANT_FALSE, "Unexpected default feature value.\n");
2806             hr = ISAXXMLReader_putFeature(reader, _bstr_("exhaustive-errors"), VARIANT_FALSE);
2807             ok(hr == S_OK, "Failed to put feature value, hr %#x.\n", hr);
2808         }
2809         else
2810         {
2811             value = 123;
2812             hr = ISAXXMLReader_getFeature(reader, _bstr_("exhaustive-errors"), &value);
2813             ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#x.\n", hr);
2814             ok(value == 123, "Unexpected value %d.\n", value);
2815 
2816             value = 123;
2817             hr = ISAXXMLReader_getFeature(reader, _bstr_("schema-validation"), &value);
2818             ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#x.\n", hr);
2819             ok(value == 123, "Unexpected value %d.\n", value);
2820         }
2821 
2822         name = feature_names;
2823         while (*name)
2824         {
2825             value = 0xc;
2826             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2827             EXPECT_HR(hr, S_OK);
2828             ok(entry->value == value, "%s: got wrong default value %x, expected %x\n", entry->clsid, value, entry->value);
2829 
2830             value = 0xc;
2831             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), value);
2832             EXPECT_HR(hr, S_OK);
2833 
2834             value = 0xd;
2835             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2836             EXPECT_HR(hr, S_OK);
2837             ok(entry->value2 == value, "%s: got wrong value %x, expected %x\n", entry->clsid, value, entry->value2);
2838 
2839             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_FALSE);
2840             EXPECT_HR(hr, S_OK);
2841             value = 0xd;
2842             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2843             EXPECT_HR(hr, S_OK);
2844             ok(value == VARIANT_FALSE, "%s: got wrong value %x, expected VARIANT_FALSE\n", entry->clsid, value);
2845 
2846             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_TRUE);
2847             EXPECT_HR(hr, S_OK);
2848             value = 0xd;
2849             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2850             EXPECT_HR(hr, S_OK);
2851             ok(value == VARIANT_TRUE, "%s: got wrong value %x, expected VARIANT_TRUE\n", entry->clsid, value);
2852 
2853             name++;
2854         }
2855 
2856         ISAXXMLReader_Release(reader);
2857 
2858         entry++;
2859     }
2860 }
2861 
2862 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
2863 static const CHAR UTF8BOMTest[] =
2864 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
2865 "<a></a>\n";
2866 
2867 struct enc_test_entry_t {
2868     const GUID *guid;
2869     const char *clsid;
2870     const char *data;
2871     HRESULT hr;
2872     BOOL todo;
2873 };
2874 
2875 static const struct enc_test_entry_t encoding_test_data[] = {
2876     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   UTF8BOMTest, 0xc00ce56f, TRUE },
2877     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, TRUE },
2878     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, FALSE },
2879     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, FALSE },
2880     { 0 }
2881 };
2882 
2883 static void test_saxreader_encoding(void)
2884 {
2885     const struct enc_test_entry_t *entry = encoding_test_data;
2886     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
2887     static const CHAR testXmlA[] = "test.xml";
2888 
2889     while (entry->guid)
2890     {
2891         ISAXXMLReader *reader;
2892         VARIANT input;
2893         DWORD written;
2894         HANDLE file;
2895         HRESULT hr;
2896 
2897         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2898         if (hr != S_OK)
2899         {
2900             win_skip("can't create %s instance\n", entry->clsid);
2901             entry++;
2902             continue;
2903         }
2904 
2905         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2906         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2907         WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
2908         CloseHandle(file);
2909 
2910         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2911         todo_wine_if(entry->todo)
2912             ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
2913 
2914         DeleteFileA(testXmlA);
2915 
2916         /* try BSTR input with no BOM or '<?xml' instruction */
2917         V_VT(&input) = VT_BSTR;
2918         V_BSTR(&input) = _bstr_("<element></element>");
2919         hr = ISAXXMLReader_parse(reader, input);
2920         EXPECT_HR(hr, S_OK);
2921 
2922         ISAXXMLReader_Release(reader);
2923 
2924         free_bstrs();
2925         entry++;
2926     }
2927 }
2928 
2929 static void test_mxwriter_handlers(void)
2930 {
2931     IMXWriter *writer;
2932     HRESULT hr;
2933     int i;
2934 
2935     static REFIID riids[] =
2936     {
2937         &IID_ISAXContentHandler,
2938         &IID_ISAXLexicalHandler,
2939         &IID_ISAXDeclHandler,
2940         &IID_ISAXDTDHandler,
2941         &IID_ISAXErrorHandler,
2942         &IID_IVBSAXDeclHandler,
2943         &IID_IVBSAXLexicalHandler,
2944         &IID_IVBSAXContentHandler,
2945         &IID_IVBSAXDTDHandler,
2946         &IID_IVBSAXErrorHandler
2947     };
2948 
2949     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2950             &IID_IMXWriter, (void**)&writer);
2951     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2952 
2953     EXPECT_REF(writer, 1);
2954 
2955     for (i = 0; i < ARRAY_SIZE(riids); i++)
2956     {
2957         IUnknown *handler;
2958         IMXWriter *writer2;
2959 
2960         /* handler from IMXWriter */
2961         hr = IMXWriter_QueryInterface(writer, riids[i], (void**)&handler);
2962         ok(hr == S_OK, "%s, expected S_OK, got %08x\n", wine_dbgstr_guid(riids[i]), hr);
2963         EXPECT_REF(writer, 2);
2964         EXPECT_REF(handler, 2);
2965 
2966         /* IMXWriter from a handler */
2967         hr = IUnknown_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
2968         ok(hr == S_OK, "%s, expected S_OK, got %08x\n", wine_dbgstr_guid(riids[i]), hr);
2969         ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2970         EXPECT_REF(writer, 3);
2971         IMXWriter_Release(writer2);
2972         IUnknown_Release(handler);
2973     }
2974 
2975     IMXWriter_Release(writer);
2976 }
2977 
2978 static struct msxmlsupported_data_t mxwriter_support_data[] =
2979 {
2980     { &CLSID_MXXMLWriter,   "MXXMLWriter"   },
2981     { &CLSID_MXXMLWriter30, "MXXMLWriter30" },
2982     { &CLSID_MXXMLWriter40, "MXXMLWriter40" },
2983     { &CLSID_MXXMLWriter60, "MXXMLWriter60" },
2984     { NULL }
2985 };
2986 
2987 static struct msxmlsupported_data_t mxattributes_support_data[] =
2988 {
2989     { &CLSID_SAXAttributes,   "SAXAttributes"   },
2990     { &CLSID_SAXAttributes30, "SAXAttributes30" },
2991     { &CLSID_SAXAttributes40, "SAXAttributes40" },
2992     { &CLSID_SAXAttributes60, "SAXAttributes60" },
2993     { NULL }
2994 };
2995 
2996 struct mxwriter_props_t
2997 {
2998     const GUID *clsid;
2999     VARIANT_BOOL bom;
3000     VARIANT_BOOL disable_escape;
3001     VARIANT_BOOL indent;
3002     VARIANT_BOOL omitdecl;
3003     VARIANT_BOOL standalone;
3004     const char *encoding;
3005 };
3006 
3007 static const struct mxwriter_props_t mxwriter_default_props[] =
3008 {
3009     { &CLSID_MXXMLWriter,   VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
3010     { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
3011     { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
3012     { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
3013     { NULL }
3014 };
3015 
3016 static void test_mxwriter_default_properties(const struct mxwriter_props_t *table)
3017 {
3018     int i = 0;
3019 
3020     while (table->clsid)
3021     {
3022         IMXWriter *writer;
3023         VARIANT_BOOL b;
3024         BSTR encoding;
3025         HRESULT hr;
3026 
3027         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3028         {
3029             table++;
3030             i++;
3031             continue;
3032         }
3033 
3034         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3035             &IID_IMXWriter, (void**)&writer);
3036         EXPECT_HR(hr, S_OK);
3037 
3038         b = !table->bom;
3039         hr = IMXWriter_get_byteOrderMark(writer, &b);
3040         EXPECT_HR(hr, S_OK);
3041         ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom);
3042 
3043         b = !table->disable_escape;
3044         hr = IMXWriter_get_disableOutputEscaping(writer, &b);
3045         EXPECT_HR(hr, S_OK);
3046         ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b,
3047            table->disable_escape);
3048 
3049         b = !table->indent;
3050         hr = IMXWriter_get_indent(writer, &b);
3051         EXPECT_HR(hr, S_OK);
3052         ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent);
3053 
3054         b = !table->omitdecl;
3055         hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
3056         EXPECT_HR(hr, S_OK);
3057         ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl);
3058 
3059         b = !table->standalone;
3060         hr = IMXWriter_get_standalone(writer, &b);
3061         EXPECT_HR(hr, S_OK);
3062         ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone);
3063 
3064         hr = IMXWriter_get_encoding(writer, &encoding);
3065         EXPECT_HR(hr, S_OK);
3066         ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n",
3067             i, wine_dbgstr_w(encoding), table->encoding);
3068         SysFreeString(encoding);
3069 
3070         IMXWriter_Release(writer);
3071 
3072         table++;
3073         i++;
3074     }
3075 }
3076 
3077 static void test_mxwriter_properties(void)
3078 {
3079     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
3080     static const WCHAR testW[] = {'t','e','s','t',0};
3081     ISAXContentHandler *content;
3082     IMXWriter *writer;
3083     VARIANT_BOOL b;
3084     HRESULT hr;
3085     BSTR str, str2;
3086     VARIANT dest;
3087 
3088     test_mxwriter_default_properties(mxwriter_default_props);
3089 
3090     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3091             &IID_IMXWriter, (void**)&writer);
3092     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3093 
3094     hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
3095     ok(hr == E_POINTER, "got %08x\n", hr);
3096 
3097     hr = IMXWriter_get_byteOrderMark(writer, NULL);
3098     ok(hr == E_POINTER, "got %08x\n", hr);
3099 
3100     hr = IMXWriter_get_indent(writer, NULL);
3101     ok(hr == E_POINTER, "got %08x\n", hr);
3102 
3103     hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
3104     ok(hr == E_POINTER, "got %08x\n", hr);
3105 
3106     hr = IMXWriter_get_standalone(writer, NULL);
3107     ok(hr == E_POINTER, "got %08x\n", hr);
3108 
3109     /* set and check */
3110     hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
3111     ok(hr == S_OK, "got %08x\n", hr);
3112 
3113     b = VARIANT_FALSE;
3114     hr = IMXWriter_get_standalone(writer, &b);
3115     ok(hr == S_OK, "got %08x\n", hr);
3116     ok(b == VARIANT_TRUE, "got %d\n", b);
3117 
3118     hr = IMXWriter_get_encoding(writer, NULL);
3119     EXPECT_HR(hr, E_POINTER);
3120 
3121     /* UTF-16 is a default setting apparently */
3122     str = (void*)0xdeadbeef;
3123     hr = IMXWriter_get_encoding(writer, &str);
3124     EXPECT_HR(hr, S_OK);
3125     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
3126 
3127     str2 = (void*)0xdeadbeef;
3128     hr = IMXWriter_get_encoding(writer, &str2);
3129     ok(hr == S_OK, "got %08x\n", hr);
3130     ok(str != str2, "expected newly allocated, got same %p\n", str);
3131 
3132     SysFreeString(str2);
3133     SysFreeString(str);
3134 
3135     /* put empty string */
3136     str = SysAllocString(emptyW);
3137     hr = IMXWriter_put_encoding(writer, str);
3138     ok(hr == E_INVALIDARG, "got %08x\n", hr);
3139     SysFreeString(str);
3140 
3141     str = (void*)0xdeadbeef;
3142     hr = IMXWriter_get_encoding(writer, &str);
3143     EXPECT_HR(hr, S_OK);
3144     ok(!lstrcmpW(str, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(str));
3145     SysFreeString(str);
3146 
3147     /* invalid encoding name */
3148     str = SysAllocString(testW);
3149     hr = IMXWriter_put_encoding(writer, str);
3150     ok(hr == E_INVALIDARG, "got %08x\n", hr);
3151     SysFreeString(str);
3152 
3153     /* test case sensivity */
3154     hr = IMXWriter_put_encoding(writer, _bstr_("utf-8"));
3155     EXPECT_HR(hr, S_OK);
3156     str = (void*)0xdeadbeef;
3157     hr = IMXWriter_get_encoding(writer, &str);
3158     EXPECT_HR(hr, S_OK);
3159     ok(!lstrcmpW(str, _bstr_("utf-8")), "got %s\n", wine_dbgstr_w(str));
3160     SysFreeString(str);
3161 
3162     hr = IMXWriter_put_encoding(writer, _bstr_("uTf-16"));
3163     EXPECT_HR(hr, S_OK);
3164     str = (void*)0xdeadbeef;
3165     hr = IMXWriter_get_encoding(writer, &str);
3166     EXPECT_HR(hr, S_OK);
3167     ok(!lstrcmpW(str, _bstr_("uTf-16")), "got %s\n", wine_dbgstr_w(str));
3168     SysFreeString(str);
3169 
3170     /* how it affects document creation */
3171     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3172     EXPECT_HR(hr, S_OK);
3173 
3174     hr = ISAXContentHandler_startDocument(content);
3175     EXPECT_HR(hr, S_OK);
3176     hr = ISAXContentHandler_endDocument(content);
3177     EXPECT_HR(hr, S_OK);
3178 
3179     V_VT(&dest) = VT_EMPTY;
3180     hr = IMXWriter_get_output(writer, &dest);
3181     EXPECT_HR(hr, S_OK);
3182     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3183     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>\r\n"),
3184         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3185     VariantClear(&dest);
3186     ISAXContentHandler_Release(content);
3187 
3188     hr = IMXWriter_get_version(writer, NULL);
3189     ok(hr == E_POINTER, "got %08x\n", hr);
3190     /* default version is 'surprisingly' 1.0 */
3191     hr = IMXWriter_get_version(writer, &str);
3192     ok(hr == S_OK, "got %08x\n", hr);
3193     ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
3194     SysFreeString(str);
3195 
3196     /* store version string as is */
3197     hr = IMXWriter_put_version(writer, NULL);
3198     ok(hr == E_INVALIDARG, "got %08x\n", hr);
3199 
3200     hr = IMXWriter_put_version(writer, _bstr_("1.0"));
3201     ok(hr == S_OK, "got %08x\n", hr);
3202 
3203     hr = IMXWriter_put_version(writer, _bstr_(""));
3204     ok(hr == S_OK, "got %08x\n", hr);
3205     hr = IMXWriter_get_version(writer, &str);
3206     ok(hr == S_OK, "got %08x\n", hr);
3207     ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
3208     SysFreeString(str);
3209 
3210     hr = IMXWriter_put_version(writer, _bstr_("a.b"));
3211     ok(hr == S_OK, "got %08x\n", hr);
3212     hr = IMXWriter_get_version(writer, &str);
3213     ok(hr == S_OK, "got %08x\n", hr);
3214     ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
3215     SysFreeString(str);
3216 
3217     hr = IMXWriter_put_version(writer, _bstr_("2.0"));
3218     ok(hr == S_OK, "got %08x\n", hr);
3219     hr = IMXWriter_get_version(writer, &str);
3220     ok(hr == S_OK, "got %08x\n", hr);
3221     ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
3222     SysFreeString(str);
3223 
3224     IMXWriter_Release(writer);
3225     free_bstrs();
3226 }
3227 
3228 static void test_mxwriter_flush(void)
3229 {
3230     ISAXContentHandler *content;
3231     IMXWriter *writer;
3232     LARGE_INTEGER pos;
3233     ULARGE_INTEGER pos2;
3234     IStream *stream;
3235     VARIANT dest;
3236     HRESULT hr;
3237     char *buff;
3238     LONG ref;
3239     int len;
3240 
3241     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3242             &IID_IMXWriter, (void**)&writer);
3243     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3244 
3245     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3246     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3247     EXPECT_REF(stream, 1);
3248 
3249     /* detach when nothing was attached */
3250     V_VT(&dest) = VT_EMPTY;
3251     hr = IMXWriter_put_output(writer, dest);
3252     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3253 
3254     /* attach stream */
3255     V_VT(&dest) = VT_UNKNOWN;
3256     V_UNKNOWN(&dest) = (IUnknown*)stream;
3257     hr = IMXWriter_put_output(writer, dest);
3258     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3259     todo_wine EXPECT_REF(stream, 3);
3260 
3261     /* detach setting VT_EMPTY destination */
3262     V_VT(&dest) = VT_EMPTY;
3263     hr = IMXWriter_put_output(writer, dest);
3264     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3265     EXPECT_REF(stream, 1);
3266 
3267     V_VT(&dest) = VT_UNKNOWN;
3268     V_UNKNOWN(&dest) = (IUnknown*)stream;
3269     hr = IMXWriter_put_output(writer, dest);
3270     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3271 
3272     /* flush() doesn't detach a stream */
3273     hr = IMXWriter_flush(writer);
3274     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3275     todo_wine EXPECT_REF(stream, 3);
3276 
3277     pos.QuadPart = 0;
3278     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3279     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3280     ok(pos2.QuadPart == 0, "expected stream beginning\n");
3281 
3282     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3283     ok(hr == S_OK, "got %08x\n", hr);
3284 
3285     hr = ISAXContentHandler_startDocument(content);
3286     ok(hr == S_OK, "got %08x\n", hr);
3287 
3288     pos.QuadPart = 0;
3289     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3290     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3291     ok(pos2.QuadPart != 0, "expected stream beginning\n");
3292 
3293     /* already started */
3294     hr = ISAXContentHandler_startDocument(content);
3295     ok(hr == S_OK, "got %08x\n", hr);
3296 
3297     hr = ISAXContentHandler_endDocument(content);
3298     ok(hr == S_OK, "got %08x\n", hr);
3299 
3300     /* flushed on endDocument() */
3301     pos.QuadPart = 0;
3302     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3303     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3304     ok(pos2.QuadPart != 0, "expected stream position moved\n");
3305 
3306     IStream_Release(stream);
3307 
3308     /* auto-flush feature */
3309     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3310     EXPECT_HR(hr, S_OK);
3311     EXPECT_REF(stream, 1);
3312 
3313     V_VT(&dest) = VT_UNKNOWN;
3314     V_UNKNOWN(&dest) = (IUnknown*)stream;
3315     hr = IMXWriter_put_output(writer, dest);
3316     EXPECT_HR(hr, S_OK);
3317 
3318     hr = IMXWriter_put_byteOrderMark(writer, VARIANT_FALSE);
3319     EXPECT_HR(hr, S_OK);
3320 
3321     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3322     EXPECT_HR(hr, S_OK);
3323 
3324     hr = ISAXContentHandler_startDocument(content);
3325     EXPECT_HR(hr, S_OK);
3326 
3327     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
3328     EXPECT_HR(hr, S_OK);
3329 
3330     /* internal buffer is flushed automatically on certain threshold */
3331     pos.QuadPart = 0;
3332     pos2.QuadPart = 1;
3333     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3334     EXPECT_HR(hr, S_OK);
3335     ok(pos2.QuadPart == 0, "expected stream beginning\n");
3336 
3337     len = 2048;
3338     buff = heap_alloc(len + 1);
3339     memset(buff, 'A', len);
3340     buff[len] = 0;
3341     hr = ISAXContentHandler_characters(content, _bstr_(buff), len);
3342     EXPECT_HR(hr, S_OK);
3343 
3344     pos.QuadPart = 0;
3345     pos2.QuadPart = 0;
3346     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3347     EXPECT_HR(hr, S_OK);
3348     ok(pos2.QuadPart != 0, "unexpected stream beginning\n");
3349 
3350     hr = IMXWriter_get_output(writer, NULL);
3351     EXPECT_HR(hr, E_POINTER);
3352 
3353     ref = get_refcount(stream);
3354     V_VT(&dest) = VT_EMPTY;
3355     hr = IMXWriter_get_output(writer, &dest);
3356     EXPECT_HR(hr, S_OK);
3357     ok(V_VT(&dest) == VT_UNKNOWN, "got vt type %d\n", V_VT(&dest));
3358     ok(V_UNKNOWN(&dest) == (IUnknown*)stream, "got pointer %p\n", V_UNKNOWN(&dest));
3359     ok(ref+1 == get_refcount(stream), "expected increased refcount\n");
3360     VariantClear(&dest);
3361 
3362     hr = ISAXContentHandler_endDocument(content);
3363     EXPECT_HR(hr, S_OK);
3364 
3365     IStream_Release(stream);
3366 
3367     /* test char count lower than threshold */
3368     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3369     EXPECT_HR(hr, S_OK);
3370     EXPECT_REF(stream, 1);
3371 
3372     hr = ISAXContentHandler_startDocument(content);
3373     EXPECT_HR(hr, S_OK);
3374 
3375     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
3376     EXPECT_HR(hr, S_OK);
3377 
3378     pos.QuadPart = 0;
3379     pos2.QuadPart = 1;
3380     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3381     EXPECT_HR(hr, S_OK);
3382     ok(pos2.QuadPart == 0, "expected stream beginning\n");
3383 
3384     memset(buff, 'A', len);
3385     buff[len] = 0;
3386     hr = ISAXContentHandler_characters(content, _bstr_(buff), len - 8);
3387     EXPECT_HR(hr, S_OK);
3388 
3389     pos.QuadPart = 0;
3390     pos2.QuadPart = 1;
3391     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3392     EXPECT_HR(hr, S_OK);
3393     ok(pos2.QuadPart == 0, "expected stream beginning\n");
3394 
3395     hr = ISAXContentHandler_endDocument(content);
3396     EXPECT_HR(hr, S_OK);
3397 
3398     /* test auto-flush function when stream is not set */
3399     V_VT(&dest) = VT_EMPTY;
3400     hr = IMXWriter_put_output(writer, dest);
3401     EXPECT_HR(hr, S_OK);
3402 
3403     hr = ISAXContentHandler_startDocument(content);
3404     EXPECT_HR(hr, S_OK);
3405 
3406     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
3407     EXPECT_HR(hr, S_OK);
3408 
3409     memset(buff, 'A', len);
3410     buff[len] = 0;
3411     hr = ISAXContentHandler_characters(content, _bstr_(buff), len);
3412     EXPECT_HR(hr, S_OK);
3413 
3414     V_VT(&dest) = VT_EMPTY;
3415     hr = IMXWriter_get_output(writer, &dest);
3416     EXPECT_HR(hr, S_OK);
3417     len += strlen("<a>");
3418     ok(SysStringLen(V_BSTR(&dest)) == len, "got len=%d, expected %d\n", SysStringLen(V_BSTR(&dest)), len);
3419     VariantClear(&dest);
3420 
3421     heap_free(buff);
3422     ISAXContentHandler_Release(content);
3423     IStream_Release(stream);
3424     IMXWriter_Release(writer);
3425     free_bstrs();
3426 }
3427 
3428 static void test_mxwriter_startenddocument(void)
3429 {
3430     ISAXContentHandler *content;
3431     IMXWriter *writer;
3432     VARIANT dest;
3433     HRESULT hr;
3434 
3435     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3436             &IID_IMXWriter, (void**)&writer);
3437     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3438 
3439     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3440     ok(hr == S_OK, "got %08x\n", hr);
3441 
3442     hr = ISAXContentHandler_startDocument(content);
3443     ok(hr == S_OK, "got %08x\n", hr);
3444 
3445     hr = ISAXContentHandler_endDocument(content);
3446     ok(hr == S_OK, "got %08x\n", hr);
3447 
3448     V_VT(&dest) = VT_EMPTY;
3449     hr = IMXWriter_get_output(writer, &dest);
3450     ok(hr == S_OK, "got %08x\n", hr);
3451     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3452     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3453         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3454     VariantClear(&dest);
3455 
3456     /* now try another startDocument */
3457     hr = ISAXContentHandler_startDocument(content);
3458     ok(hr == S_OK, "got %08x\n", hr);
3459     /* and get duplicated prolog */
3460     V_VT(&dest) = VT_EMPTY;
3461     hr = IMXWriter_get_output(writer, &dest);
3462     ok(hr == S_OK, "got %08x\n", hr);
3463     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3464     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
3465                         "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3466         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3467     VariantClear(&dest);
3468 
3469     ISAXContentHandler_Release(content);
3470     IMXWriter_Release(writer);
3471 
3472     /* now with omitted declaration */
3473     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3474             &IID_IMXWriter, (void**)&writer);
3475     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3476 
3477     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3478     ok(hr == S_OK, "got %08x\n", hr);
3479 
3480     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3481     ok(hr == S_OK, "got %08x\n", hr);
3482 
3483     hr = ISAXContentHandler_startDocument(content);
3484     ok(hr == S_OK, "got %08x\n", hr);
3485 
3486     hr = ISAXContentHandler_endDocument(content);
3487     ok(hr == S_OK, "got %08x\n", hr);
3488 
3489     V_VT(&dest) = VT_EMPTY;
3490     hr = IMXWriter_get_output(writer, &dest);
3491     ok(hr == S_OK, "got %08x\n", hr);
3492     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3493     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3494     VariantClear(&dest);
3495 
3496     ISAXContentHandler_Release(content);
3497     IMXWriter_Release(writer);
3498 
3499     free_bstrs();
3500 }
3501 
3502 enum startendtype
3503 {
3504     StartElement    = 0x001,
3505     EndElement      = 0x010,
3506     StartEndElement = 0x011,
3507     DisableEscaping = 0x100
3508 };
3509 
3510 struct writer_startendelement_t {
3511     const GUID *clsid;
3512     enum startendtype type;
3513     const char *uri;
3514     const char *local_name;
3515     const char *qname;
3516     const char *output;
3517     HRESULT hr;
3518     ISAXAttributes *attr;
3519 };
3520 
3521 static const char startelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\'\">";
3522 static const char startendelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\'\"/>";
3523 static const char startendelement_noescape_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&\">\'\"/>";
3524 
3525 static const struct writer_startendelement_t writer_startendelement[] = {
3526     /* 0 */
3527     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3528     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3529     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3530     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK },
3531     { &CLSID_MXXMLWriter,   StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3532     /* 5 */
3533     { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3534     { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3535     { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK },
3536     { &CLSID_MXXMLWriter,   StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3537     { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3538     /* 10 */
3539     { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3540     { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK },
3541     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3542     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3543     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3544     /* 15 */
3545     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "<qname>", S_OK },
3546     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "qname", "<qname>", S_OK },
3547     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3548     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3549     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3550     /* 20 */
3551     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3552     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3553     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3554     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK },
3555     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3556     /* 25 */
3557     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3558     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3559     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3560     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3561     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3562     /* 30 */
3563     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3564     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3565     /* endElement tests */
3566     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3567     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3568     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3569     /* 35 */
3570     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "</>", S_OK },
3571     { &CLSID_MXXMLWriter,   EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3572     { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3573     { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3574     { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "</>", S_OK },
3575     /* 40 */
3576     { &CLSID_MXXMLWriter,   EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3577     { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3578     { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3579     { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "</>", S_OK },
3580     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3581     /* 45 */
3582     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3583     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3584     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "</qname>", S_OK },
3585     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "qname", "</qname>", S_OK },
3586     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3587     /* 50 */
3588     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3589     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3590     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3591     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3592     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3593     /* 55 */
3594     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "</>", S_OK },
3595     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3596     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3597     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3598     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3599     /* 60 */
3600     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3601     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3602     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3603     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3604 
3605     /* with attributes */
3606     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3607     /* 65 */
3608     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3609     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3610     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3611     /* empty elements */
3612     { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3613     { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3614     /* 70 */
3615     { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3616     { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3617     { &CLSID_MXXMLWriter,   StartEndElement, "", "", "", "</>", S_OK },
3618     { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
3619     { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
3620     /* 75 */
3621     { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "</>", S_OK },
3622 
3623     /* with disabled output escaping */
3624     { &CLSID_MXXMLWriter,   StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3625     { &CLSID_MXXMLWriter30, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3626     { &CLSID_MXXMLWriter40, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3627     { &CLSID_MXXMLWriter60, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3628 
3629     { NULL }
3630 };
3631 
3632 static void get_class_support_data(struct msxmlsupported_data_t *table, REFIID riid)
3633 {
3634     while (table->clsid)
3635     {
3636         IUnknown *unk;
3637         HRESULT hr;
3638 
3639         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, riid, (void**)&unk);
3640         if (hr == S_OK) IUnknown_Release(unk);
3641 
3642         table->supported = hr == S_OK;
3643         if (hr != S_OK) win_skip("class %s not supported\n", table->name);
3644 
3645         table++;
3646     }
3647 }
3648 
3649 static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table)
3650 {
3651     int i = 0;
3652 
3653     while (table->clsid)
3654     {
3655         ISAXContentHandler *content;
3656         IMXWriter *writer;
3657         HRESULT hr;
3658 
3659         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3660         {
3661             table++;
3662             i++;
3663             continue;
3664         }
3665 
3666         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3667             &IID_IMXWriter, (void**)&writer);
3668         EXPECT_HR(hr, S_OK);
3669 
3670         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3671         EXPECT_HR(hr, S_OK);
3672 
3673         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3674         EXPECT_HR(hr, S_OK);
3675 
3676         hr = ISAXContentHandler_startDocument(content);
3677         EXPECT_HR(hr, S_OK);
3678 
3679         if (table->type & DisableEscaping)
3680         {
3681             hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
3682             EXPECT_HR(hr, S_OK);
3683         }
3684 
3685         if (table->type & StartElement)
3686         {
3687             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), table->uri ? strlen(table->uri) : 0,
3688                 _bstr_(table->local_name), table->local_name ? strlen(table->local_name) : 0, _bstr_(table->qname),
3689                 table->qname ? strlen(table->qname) : 0, table->attr);
3690             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3691         }
3692 
3693         if (table->type & EndElement)
3694         {
3695             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), table->uri ? strlen(table->uri) : 0,
3696                 _bstr_(table->local_name), table->local_name ? strlen(table->local_name) : 0, _bstr_(table->qname),
3697                 table->qname ? strlen(table->qname) : 0);
3698             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3699         }
3700 
3701         /* test output */
3702         if (hr == S_OK)
3703         {
3704             VARIANT dest;
3705 
3706             V_VT(&dest) = VT_EMPTY;
3707             hr = IMXWriter_get_output(writer, &dest);
3708             EXPECT_HR(hr, S_OK);
3709             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3710             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3711                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3712             VariantClear(&dest);
3713         }
3714 
3715         ISAXContentHandler_Release(content);
3716         IMXWriter_Release(writer);
3717 
3718         table++;
3719         i++;
3720     }
3721 
3722     free_bstrs();
3723 }
3724 
3725 /* point of these test is to start/end element with different names and name lengths */
3726 struct writer_startendelement2_t {
3727     const GUID *clsid;
3728     const char *qnamestart;
3729     int qnamestart_len;
3730     const char *qnameend;
3731     int qnameend_len;
3732     const char *output;
3733     HRESULT hr;
3734 };
3735 
3736 static const struct writer_startendelement2_t writer_startendelement2[] = {
3737     { &CLSID_MXXMLWriter,   "a", -1, "b", -1, "<a/>", S_OK },
3738     { &CLSID_MXXMLWriter30, "a", -1, "b", -1, "<a/>", S_OK },
3739     { &CLSID_MXXMLWriter40, "a", -1, "b", -1, "<a/>", S_OK },
3740     /* -1 length is not allowed for version 6 */
3741     { &CLSID_MXXMLWriter60, "a", -1, "b", -1, "<a/>", E_INVALIDARG },
3742 
3743     { &CLSID_MXXMLWriter,   "a", 1, "b", 1, "<a/>", S_OK },
3744     { &CLSID_MXXMLWriter30, "a", 1, "b", 1, "<a/>", S_OK },
3745     { &CLSID_MXXMLWriter40, "a", 1, "b", 1, "<a/>", S_OK },
3746     { &CLSID_MXXMLWriter60, "a", 1, "b", 1, "<a/>", S_OK },
3747     { NULL }
3748 };
3749 
3750 static void test_mxwriter_startendelement_batch2(const struct writer_startendelement2_t *table)
3751 {
3752     int i = 0;
3753 
3754     while (table->clsid)
3755     {
3756         ISAXContentHandler *content;
3757         IMXWriter *writer;
3758         HRESULT hr;
3759 
3760         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3761         {
3762             table++;
3763             i++;
3764             continue;
3765         }
3766 
3767         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3768             &IID_IMXWriter, (void**)&writer);
3769         EXPECT_HR(hr, S_OK);
3770 
3771         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3772         EXPECT_HR(hr, S_OK);
3773 
3774         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3775         EXPECT_HR(hr, S_OK);
3776 
3777         hr = ISAXContentHandler_startDocument(content);
3778         EXPECT_HR(hr, S_OK);
3779 
3780         hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0,
3781             _bstr_(table->qnamestart), table->qnamestart_len, NULL);
3782         ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3783 
3784         hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0,
3785             _bstr_(table->qnameend), table->qnameend_len);
3786         ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3787 
3788         /* test output */
3789         if (hr == S_OK)
3790         {
3791             VARIANT dest;
3792 
3793             V_VT(&dest) = VT_EMPTY;
3794             hr = IMXWriter_get_output(writer, &dest);
3795             EXPECT_HR(hr, S_OK);
3796             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3797             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3798                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3799             VariantClear(&dest);
3800         }
3801 
3802         ISAXContentHandler_Release(content);
3803         IMXWriter_Release(writer);
3804 
3805         table++;
3806         i++;
3807 
3808         free_bstrs();
3809     }
3810 }
3811 
3812 
3813 static void test_mxwriter_startendelement(void)
3814 {
3815     ISAXContentHandler *content;
3816     IMXWriter *writer;
3817     VARIANT dest;
3818     HRESULT hr;
3819 
3820     test_mxwriter_startendelement_batch(writer_startendelement);
3821     test_mxwriter_startendelement_batch2(writer_startendelement2);
3822 
3823     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3824             &IID_IMXWriter, (void**)&writer);
3825     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3826 
3827     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3828     ok(hr == S_OK, "got %08x\n", hr);
3829 
3830     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3831     ok(hr == S_OK, "got %08x\n", hr);
3832 
3833     hr = ISAXContentHandler_startDocument(content);
3834     ok(hr == S_OK, "got %08x\n", hr);
3835 
3836     /* all string pointers should be not null */
3837     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
3838     ok(hr == S_OK, "got %08x\n", hr);
3839 
3840     V_VT(&dest) = VT_EMPTY;
3841     hr = IMXWriter_get_output(writer, &dest);
3842     ok(hr == S_OK, "got %08x\n", hr);
3843     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3844     ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3845     VariantClear(&dest);
3846 
3847     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
3848     ok(hr == S_OK, "got %08x\n", hr);
3849 
3850     V_VT(&dest) = VT_EMPTY;
3851     hr = IMXWriter_get_output(writer, &dest);
3852     ok(hr == S_OK, "got %08x\n", hr);
3853     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3854     ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3855     VariantClear(&dest);
3856 
3857     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
3858     EXPECT_HR(hr, E_INVALIDARG);
3859 
3860     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
3861     EXPECT_HR(hr, E_INVALIDARG);
3862 
3863     /* only local name is an error too */
3864     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
3865     EXPECT_HR(hr, E_INVALIDARG);
3866 
3867     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
3868     EXPECT_HR(hr, S_OK);
3869 
3870     V_VT(&dest) = VT_EMPTY;
3871     hr = IMXWriter_get_output(writer, &dest);
3872     EXPECT_HR(hr, S_OK);
3873     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3874     ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3875     VariantClear(&dest);
3876 
3877     hr = ISAXContentHandler_endDocument(content);
3878     EXPECT_HR(hr, S_OK);
3879 
3880     V_VT(&dest) = VT_EMPTY;
3881     hr = IMXWriter_put_output(writer, dest);
3882     EXPECT_HR(hr, S_OK);
3883 
3884     V_VT(&dest) = VT_EMPTY;
3885     hr = IMXWriter_get_output(writer, &dest);
3886     EXPECT_HR(hr, S_OK);
3887     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3888     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3889     VariantClear(&dest);
3890 
3891     hr = ISAXContentHandler_startDocument(content);
3892     EXPECT_HR(hr, S_OK);
3893 
3894     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL);
3895     EXPECT_HR(hr, S_OK);
3896 
3897     V_VT(&dest) = VT_EMPTY;
3898     hr = IMXWriter_get_output(writer, &dest);
3899     EXPECT_HR(hr, S_OK);
3900     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3901     ok(!lstrcmpW(_bstr_("<abc>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3902     VariantClear(&dest);
3903 
3904     hr = ISAXContentHandler_endDocument(content);
3905     EXPECT_HR(hr, S_OK);
3906     IMXWriter_flush(writer);
3907 
3908     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abdcdef"), 3);
3909     EXPECT_HR(hr, S_OK);
3910     V_VT(&dest) = VT_EMPTY;
3911     hr = IMXWriter_get_output(writer, &dest);
3912     EXPECT_HR(hr, S_OK);
3913     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3914     ok(!lstrcmpW(_bstr_("<abc></abd>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3915     VariantClear(&dest);
3916 
3917     V_VT(&dest) = VT_EMPTY;
3918     hr = IMXWriter_put_output(writer, dest);
3919     EXPECT_HR(hr, S_OK);
3920 
3921     /* length -1 */
3922     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), -1, NULL);
3923     EXPECT_HR(hr, S_OK);
3924     V_VT(&dest) = VT_EMPTY;
3925     hr = IMXWriter_get_output(writer, &dest);
3926     EXPECT_HR(hr, S_OK);
3927     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3928     ok(!lstrcmpW(_bstr_("<a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3929     VariantClear(&dest);
3930 
3931     ISAXContentHandler_Release(content);
3932     IMXWriter_Release(writer);
3933     free_bstrs();
3934 }
3935 
3936 struct writer_characters_t {
3937     const GUID *clsid;
3938     const char *data;
3939     const char *output;
3940 };
3941 
3942 static const struct writer_characters_t writer_characters[] = {
3943     { &CLSID_MXXMLWriter,   "< > & \" \'", "&lt; &gt; &amp; \" \'" },
3944     { &CLSID_MXXMLWriter30, "< > & \" \'", "&lt; &gt; &amp; \" \'" },
3945     { &CLSID_MXXMLWriter40, "< > & \" \'", "&lt; &gt; &amp; \" \'" },
3946     { &CLSID_MXXMLWriter60, "< > & \" \'", "&lt; &gt; &amp; \" \'" },
3947     { NULL }
3948 };
3949 
3950 static void test_mxwriter_characters(void)
3951 {
3952     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
3953     const struct writer_characters_t *table = writer_characters;
3954     ISAXContentHandler *content;
3955     IMXWriter *writer;
3956     VARIANT dest;
3957     HRESULT hr;
3958     int i = 0;
3959 
3960     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3961             &IID_IMXWriter, (void**)&writer);
3962     EXPECT_HR(hr, S_OK);
3963 
3964     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3965     EXPECT_HR(hr, S_OK);
3966 
3967     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3968     EXPECT_HR(hr, S_OK);
3969 
3970     hr = ISAXContentHandler_startDocument(content);
3971     EXPECT_HR(hr, S_OK);
3972 
3973     hr = ISAXContentHandler_characters(content, NULL, 0);
3974     EXPECT_HR(hr, E_INVALIDARG);
3975 
3976     hr = ISAXContentHandler_characters(content, chardataW, 0);
3977     EXPECT_HR(hr, S_OK);
3978 
3979     hr = ISAXContentHandler_characters(content, chardataW, ARRAY_SIZE(chardataW) - 1);
3980     EXPECT_HR(hr, S_OK);
3981 
3982     V_VT(&dest) = VT_EMPTY;
3983     hr = IMXWriter_get_output(writer, &dest);
3984     EXPECT_HR(hr, S_OK);
3985     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3986     ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3987     VariantClear(&dest);
3988 
3989     hr = ISAXContentHandler_endDocument(content);
3990     EXPECT_HR(hr, S_OK);
3991 
3992     ISAXContentHandler_Release(content);
3993     IMXWriter_Release(writer);
3994 
3995     /* try empty characters data to see if element is closed */
3996     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3997             &IID_IMXWriter, (void**)&writer);
3998     EXPECT_HR(hr, S_OK);
3999 
4000     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4001     EXPECT_HR(hr, S_OK);
4002 
4003     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4004     EXPECT_HR(hr, S_OK);
4005 
4006     hr = ISAXContentHandler_startDocument(content);
4007     EXPECT_HR(hr, S_OK);
4008 
4009     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
4010     EXPECT_HR(hr, S_OK);
4011 
4012     hr = ISAXContentHandler_characters(content, chardataW, 0);
4013     EXPECT_HR(hr, S_OK);
4014 
4015     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
4016     EXPECT_HR(hr, S_OK);
4017 
4018     V_VT(&dest) = VT_EMPTY;
4019     hr = IMXWriter_get_output(writer, &dest);
4020     EXPECT_HR(hr, S_OK);
4021     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4022     ok(!lstrcmpW(_bstr_("<a></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4023     VariantClear(&dest);
4024 
4025     ISAXContentHandler_Release(content);
4026     IMXWriter_Release(writer);
4027 
4028     /* batch tests */
4029     while (table->clsid)
4030     {
4031         ISAXContentHandler *content;
4032         IMXWriter *writer;
4033         VARIANT dest;
4034         HRESULT hr;
4035 
4036         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
4037         {
4038             table++;
4039             i++;
4040             continue;
4041         }
4042 
4043         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
4044             &IID_IMXWriter, (void**)&writer);
4045         EXPECT_HR(hr, S_OK);
4046 
4047         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4048         EXPECT_HR(hr, S_OK);
4049 
4050         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4051         EXPECT_HR(hr, S_OK);
4052 
4053         hr = ISAXContentHandler_startDocument(content);
4054         EXPECT_HR(hr, S_OK);
4055 
4056         hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
4057         EXPECT_HR(hr, S_OK);
4058 
4059         /* test output */
4060         if (hr == S_OK)
4061         {
4062             V_VT(&dest) = VT_EMPTY;
4063             hr = IMXWriter_get_output(writer, &dest);
4064             EXPECT_HR(hr, S_OK);
4065             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4066             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
4067                 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
4068             VariantClear(&dest);
4069         }
4070 
4071         /* with disabled escaping */
4072         V_VT(&dest) = VT_EMPTY;
4073         hr = IMXWriter_put_output(writer, dest);
4074         EXPECT_HR(hr, S_OK);
4075 
4076         hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
4077         EXPECT_HR(hr, S_OK);
4078 
4079         hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
4080         EXPECT_HR(hr, S_OK);
4081 
4082         /* test output */
4083         if (hr == S_OK)
4084         {
4085             V_VT(&dest) = VT_EMPTY;
4086             hr = IMXWriter_get_output(writer, &dest);
4087             EXPECT_HR(hr, S_OK);
4088             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4089             ok(!lstrcmpW(_bstr_(table->data), V_BSTR(&dest)),
4090                 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->data);
4091             VariantClear(&dest);
4092         }
4093 
4094         ISAXContentHandler_Release(content);
4095         IMXWriter_Release(writer);
4096 
4097         table++;
4098         i++;
4099     }
4100 
4101     free_bstrs();
4102 }
4103 
4104 static const mxwriter_stream_test mxwriter_stream_tests[] = {
4105     {
4106         VARIANT_TRUE,"UTF-16",
4107         {
4108             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
4109             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
4110             {TRUE}
4111         }
4112     },
4113     {
4114         VARIANT_FALSE,"UTF-16",
4115         {
4116             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
4117             {TRUE}
4118         }
4119     },
4120     {
4121         VARIANT_TRUE,"UTF-8",
4122         {
4123             {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
4124             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
4125              * and the writer is released.
4126              */
4127             {FALSE,NULL,0},
4128             {TRUE}
4129         }
4130     },
4131     {
4132         VARIANT_TRUE,"utf-8",
4133         {
4134             {FALSE,(const BYTE*)utf8xml2,sizeof(utf8xml2)-1},
4135             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
4136              * and the writer is released.
4137              */
4138             {FALSE,NULL,0},
4139             {TRUE}
4140         }
4141     },
4142     {
4143         VARIANT_TRUE,"UTF-16",
4144         {
4145             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
4146             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
4147             {TRUE}
4148         }
4149     },
4150     {
4151         VARIANT_TRUE,"UTF-16",
4152         {
4153             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
4154             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
4155             {TRUE}
4156         }
4157     }
4158 };
4159 
4160 static void test_mxwriter_stream(void)
4161 {
4162     IMXWriter *writer;
4163     ISAXContentHandler *content;
4164     HRESULT hr;
4165     VARIANT dest;
4166     IStream *stream;
4167     LARGE_INTEGER pos;
4168     ULARGE_INTEGER pos2;
4169     DWORD test_count = ARRAY_SIZE(mxwriter_stream_tests);
4170 
4171     for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
4172         const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
4173 
4174         hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4175                 &IID_IMXWriter, (void**)&writer);
4176         ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
4177 
4178         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4179         ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
4180 
4181         hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
4182         ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
4183 
4184         V_VT(&dest) = VT_UNKNOWN;
4185         V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
4186         hr = IMXWriter_put_output(writer, dest);
4187         ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
4188 
4189         hr = IMXWriter_put_byteOrderMark(writer, test->bom);
4190         ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
4191 
4192         current_write_test = test->expected_writes;
4193 
4194         hr = ISAXContentHandler_startDocument(content);
4195         ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
4196 
4197         hr = ISAXContentHandler_endDocument(content);
4198         ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
4199 
4200         ISAXContentHandler_Release(content);
4201         IMXWriter_Release(writer);
4202 
4203         ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
4204             (int)(current_write_test-test->expected_writes), current_stream_test_index);
4205     }
4206 
4207     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4208             &IID_IMXWriter, (void**)&writer);
4209     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
4210 
4211     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
4212     ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
4213 
4214     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4215     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
4216 
4217     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
4218     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
4219 
4220     V_VT(&dest) = VT_UNKNOWN;
4221     V_UNKNOWN(&dest) = (IUnknown*)stream;
4222     hr = IMXWriter_put_output(writer, dest);
4223     ok(hr == S_OK, "put_output failed: %08x\n", hr);
4224 
4225     hr = ISAXContentHandler_startDocument(content);
4226     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
4227 
4228     /* Setting output of the mxwriter causes the current output to be flushed,
4229      * and the writer to start over.
4230      */
4231     V_VT(&dest) = VT_EMPTY;
4232     hr = IMXWriter_put_output(writer, dest);
4233     ok(hr == S_OK, "put_output failed: %08x\n", hr);
4234 
4235     pos.QuadPart = 0;
4236     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
4237     ok(hr == S_OK, "Seek failed: %08x\n", hr);
4238     ok(pos2.QuadPart != 0, "expected stream position moved\n");
4239 
4240     hr = ISAXContentHandler_startDocument(content);
4241     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
4242 
4243     hr = ISAXContentHandler_endDocument(content);
4244     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
4245 
4246     V_VT(&dest) = VT_EMPTY;
4247     hr = IMXWriter_get_output(writer, &dest);
4248     ok(hr == S_OK, "get_output failed: %08x\n", hr);
4249     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
4250     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
4251             "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4252     VariantClear(&dest);
4253 
4254     /* test when BOM is written to output stream */
4255     V_VT(&dest) = VT_EMPTY;
4256     hr = IMXWriter_put_output(writer, dest);
4257     EXPECT_HR(hr, S_OK);
4258 
4259     pos.QuadPart = 0;
4260     hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
4261     EXPECT_HR(hr, S_OK);
4262 
4263     V_VT(&dest) = VT_UNKNOWN;
4264     V_UNKNOWN(&dest) = (IUnknown*)stream;
4265     hr = IMXWriter_put_output(writer, dest);
4266     EXPECT_HR(hr, S_OK);
4267 
4268     hr = IMXWriter_put_byteOrderMark(writer, VARIANT_TRUE);
4269     EXPECT_HR(hr, S_OK);
4270 
4271     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
4272     EXPECT_HR(hr, S_OK);
4273 
4274     hr = ISAXContentHandler_startDocument(content);
4275     EXPECT_HR(hr, S_OK);
4276 
4277     pos.QuadPart = 0;
4278     pos2.QuadPart = 0;
4279     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
4280     EXPECT_HR(hr, S_OK);
4281     ok(pos2.QuadPart == 2, "got wrong position\n");
4282 
4283     IStream_Release(stream);
4284     ISAXContentHandler_Release(content);
4285     IMXWriter_Release(writer);
4286 
4287     free_bstrs();
4288 }
4289 
4290 static const char *encoding_names[] = {
4291     "iso-8859-1",
4292     "iso-8859-2",
4293     "iso-8859-3",
4294     "iso-8859-4",
4295     "iso-8859-5",
4296     "iso-8859-7",
4297     "iso-8859-9",
4298     "iso-8859-13",
4299     "iso-8859-15",
4300     NULL
4301 };
4302 
4303 static void test_mxwriter_encoding(void)
4304 {
4305     ISAXContentHandler *content;
4306     IMXWriter *writer;
4307     IStream *stream;
4308     const char *enc;
4309     VARIANT dest;
4310     HRESULT hr;
4311     HGLOBAL g;
4312     char *ptr;
4313     BSTR s;
4314     int i;
4315 
4316     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4317             &IID_IMXWriter, (void**)&writer);
4318     EXPECT_HR(hr, S_OK);
4319 
4320     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4321     EXPECT_HR(hr, S_OK);
4322 
4323     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
4324     EXPECT_HR(hr, S_OK);
4325 
4326     hr = ISAXContentHandler_startDocument(content);
4327     EXPECT_HR(hr, S_OK);
4328 
4329     hr = ISAXContentHandler_endDocument(content);
4330     EXPECT_HR(hr, S_OK);
4331 
4332     /* The content is always re-encoded to UTF-16 when the output is
4333      * retrieved as a BSTR.
4334      */
4335     V_VT(&dest) = VT_EMPTY;
4336     hr = IMXWriter_get_output(writer, &dest);
4337     EXPECT_HR(hr, S_OK);
4338     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
4339     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
4340             "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4341     VariantClear(&dest);
4342 
4343     /* switch encoding when something is written already */
4344     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
4345     EXPECT_HR(hr, S_OK);
4346 
4347     V_VT(&dest) = VT_UNKNOWN;
4348     V_UNKNOWN(&dest) = (IUnknown*)stream;
4349     hr = IMXWriter_put_output(writer, dest);
4350     EXPECT_HR(hr, S_OK);
4351 
4352     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
4353     EXPECT_HR(hr, S_OK);
4354 
4355     /* write empty element */
4356     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
4357     EXPECT_HR(hr, S_OK);
4358 
4359     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
4360     EXPECT_HR(hr, S_OK);
4361 
4362     /* switch */
4363     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
4364     EXPECT_HR(hr, S_OK);
4365 
4366     hr = IMXWriter_flush(writer);
4367     EXPECT_HR(hr, S_OK);
4368 
4369     hr = GetHGlobalFromStream(stream, &g);
4370     EXPECT_HR(hr, S_OK);
4371 
4372     ptr = GlobalLock(g);
4373     ok(!strncmp(ptr, "<a/>", 4), "got %c%c%c%c\n", ptr[0],ptr[1],ptr[2],ptr[3]);
4374     GlobalUnlock(g);
4375 
4376     /* so output is unaffected, encoding name is stored however */
4377     hr = IMXWriter_get_encoding(writer, &s);
4378     EXPECT_HR(hr, S_OK);
4379     ok(!lstrcmpW(s, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(s));
4380     SysFreeString(s);
4381 
4382     IStream_Release(stream);
4383 
4384     i = 0;
4385     enc = encoding_names[i];
4386     while (enc)
4387     {
4388         char expectedA[200];
4389 
4390         hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
4391         EXPECT_HR(hr, S_OK);
4392 
4393         V_VT(&dest) = VT_UNKNOWN;
4394         V_UNKNOWN(&dest) = (IUnknown*)stream;
4395         hr = IMXWriter_put_output(writer, dest);
4396         EXPECT_HR(hr, S_OK);
4397 
4398         hr = IMXWriter_put_encoding(writer, _bstr_(enc));
4399         ok(hr == S_OK || broken(hr != S_OK) /* old win versions do not support certain encodings */,
4400             "%s: encoding not accepted\n", enc);
4401         if (hr != S_OK)
4402         {
4403             enc = encoding_names[++i];
4404             IStream_Release(stream);
4405             continue;
4406         }
4407 
4408         hr = ISAXContentHandler_startDocument(content);
4409         EXPECT_HR(hr, S_OK);
4410 
4411         hr = ISAXContentHandler_endDocument(content);
4412         EXPECT_HR(hr, S_OK);
4413 
4414         hr = IMXWriter_flush(writer);
4415         EXPECT_HR(hr, S_OK);
4416 
4417         /* prepare expected string */
4418         *expectedA = 0;
4419         strcat(expectedA, "<?xml version=\"1.0\" encoding=\"");
4420         strcat(expectedA, enc);
4421         strcat(expectedA, "\" standalone=\"no\"?>\r\n");
4422 
4423         hr = GetHGlobalFromStream(stream, &g);
4424         EXPECT_HR(hr, S_OK);
4425 
4426         ptr = GlobalLock(g);
4427         ok(!strncmp(ptr, expectedA, strlen(expectedA)), "%s: got %s, expected %.50s\n", enc, ptr, expectedA);
4428         GlobalUnlock(g);
4429 
4430         V_VT(&dest) = VT_EMPTY;
4431         hr = IMXWriter_put_output(writer, dest);
4432         EXPECT_HR(hr, S_OK);
4433 
4434         IStream_Release(stream);
4435 
4436         enc = encoding_names[++i];
4437     }
4438 
4439     ISAXContentHandler_Release(content);
4440     IMXWriter_Release(writer);
4441 
4442     free_bstrs();
4443 }
4444 
4445 static void test_obj_dispex(IUnknown *obj)
4446 {
4447     static const WCHAR testW[] = {'t','e','s','t','p','r','o','p',0};
4448     static const WCHAR starW[] = {'*',0};
4449     DISPID dispid = DISPID_SAX_XMLREADER_GETFEATURE;
4450     IDispatchEx *dispex;
4451     IUnknown *unk;
4452     DWORD props;
4453     UINT ticnt;
4454     HRESULT hr;
4455     BSTR name;
4456     DISPID did;
4457 
4458     hr = IUnknown_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex);
4459     EXPECT_HR(hr, S_OK);
4460     if (FAILED(hr)) return;
4461 
4462     ticnt = 0;
4463     hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt);
4464     EXPECT_HR(hr, S_OK);
4465     ok(ticnt == 1, "ticnt=%u\n", ticnt);
4466 
4467     name = SysAllocString(starW);
4468     hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive);
4469     EXPECT_HR(hr, E_NOTIMPL);
4470     SysFreeString(name);
4471 
4472     hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid);
4473     EXPECT_HR(hr, E_NOTIMPL);
4474 
4475     props = 0;
4476     hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props);
4477     EXPECT_HR(hr, E_NOTIMPL);
4478     ok(props == 0, "expected 0 got %d\n", props);
4479 
4480     hr = IDispatchEx_GetMemberName(dispex, dispid, &name);
4481     EXPECT_HR(hr, E_NOTIMPL);
4482     if (SUCCEEDED(hr)) SysFreeString(name);
4483 
4484     hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_SAX_XMLREADER_GETFEATURE, &dispid);
4485     EXPECT_HR(hr, E_NOTIMPL);
4486 
4487     unk = (IUnknown*)0xdeadbeef;
4488     hr = IDispatchEx_GetNameSpaceParent(dispex, &unk);
4489     EXPECT_HR(hr, E_NOTIMPL);
4490     ok(unk == (IUnknown*)0xdeadbeef, "got %p\n", unk);
4491 
4492     name = SysAllocString(testW);
4493     hr = IDispatchEx_GetDispID(dispex, name, fdexNameEnsure, &did);
4494     ok(hr == DISP_E_UNKNOWNNAME, "got 0x%08x\n", hr);
4495     SysFreeString(name);
4496 
4497     IDispatchEx_Release(dispex);
4498 }
4499 
4500 static void test_saxreader_dispex(void)
4501 {
4502     IVBSAXXMLReader *vbreader;
4503     ISAXXMLReader *reader;
4504     DISPPARAMS dispparams;
4505     DISPID dispid;
4506     IUnknown *unk;
4507     VARIANT arg;
4508     HRESULT hr;
4509 
4510     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
4511                 &IID_ISAXXMLReader, (void**)&reader);
4512     EXPECT_HR(hr, S_OK);
4513 
4514     hr = ISAXXMLReader_QueryInterface(reader, &IID_IUnknown, (void**)&unk);
4515     EXPECT_HR(hr, S_OK);
4516     test_obj_dispex(unk);
4517     IUnknown_Release(unk);
4518 
4519     hr = ISAXXMLReader_QueryInterface(reader, &IID_IVBSAXXMLReader, (void**)&vbreader);
4520     EXPECT_HR(hr, S_OK);
4521     hr = IVBSAXXMLReader_QueryInterface(vbreader, &IID_IUnknown, (void**)&unk);
4522     EXPECT_HR(hr, S_OK);
4523     test_obj_dispex(unk);
4524     IUnknown_Release(unk);
4525 
4526     dispid = DISPID_PROPERTYPUT;
4527     dispparams.cArgs = 1;
4528     dispparams.cNamedArgs = 1;
4529     dispparams.rgdispidNamedArgs = &dispid;
4530     dispparams.rgvarg = &arg;
4531 
4532     V_VT(&arg) = VT_DISPATCH;
4533     V_DISPATCH(&arg) = NULL;
4534 
4535     /* propputref is callable as PROPERTYPUT and PROPERTYPUTREF */
4536     hr = IVBSAXXMLReader_Invoke(vbreader,
4537         DISPID_SAX_XMLREADER_CONTENTHANDLER,
4538        &IID_NULL,
4539         0,
4540         DISPATCH_PROPERTYPUT,
4541        &dispparams,
4542         NULL,
4543         NULL,
4544         NULL);
4545     ok(hr == S_OK, "got 0x%08x\n", hr);
4546 
4547     hr = IVBSAXXMLReader_Invoke(vbreader,
4548         DISPID_SAX_XMLREADER_CONTENTHANDLER,
4549        &IID_NULL,
4550         0,
4551         DISPATCH_PROPERTYPUTREF,
4552        &dispparams,
4553         NULL,
4554         NULL,
4555         NULL);
4556     ok(hr == S_OK, "got 0x%08x\n", hr);
4557 
4558     IVBSAXXMLReader_Release(vbreader);
4559     ISAXXMLReader_Release(reader);
4560 
4561     if (is_clsid_supported(&CLSID_SAXXMLReader60, reader_support_data))
4562     {
4563         hr = CoCreateInstance(&CLSID_SAXXMLReader60, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&unk);
4564         ok(hr == S_OK, "got 0x%08x\n", hr);
4565         test_obj_dispex(unk);
4566         IUnknown_Release(unk);
4567     }
4568 }
4569 
4570 static void test_mxwriter_dispex(void)
4571 {
4572     IDispatchEx *dispex;
4573     IMXWriter *writer;
4574     IUnknown *unk;
4575     HRESULT hr;
4576 
4577     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4578             &IID_IMXWriter, (void**)&writer);
4579     EXPECT_HR(hr, S_OK);
4580 
4581     hr = IMXWriter_QueryInterface(writer, &IID_IDispatchEx, (void**)&dispex);
4582     EXPECT_HR(hr, S_OK);
4583     hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
4584     test_obj_dispex(unk);
4585     IUnknown_Release(unk);
4586     IDispatchEx_Release(dispex);
4587     IMXWriter_Release(writer);
4588 
4589     if (is_clsid_supported(&CLSID_MXXMLWriter60, mxwriter_support_data))
4590     {
4591         hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&unk);
4592         ok(hr == S_OK, "got 0x%08x\n", hr);
4593         test_obj_dispex(unk);
4594         IUnknown_Release(unk);
4595     }
4596 }
4597 
4598 static void test_mxwriter_comment(void)
4599 {
4600     static const WCHAR commentW[] = {'c','o','m','m','e','n','t',0};
4601     IVBSAXLexicalHandler *vblexical;
4602     ISAXContentHandler *content;
4603     ISAXLexicalHandler *lexical;
4604     IMXWriter *writer;
4605     VARIANT dest;
4606     HRESULT hr;
4607 
4608     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4609             &IID_IMXWriter, (void**)&writer);
4610     EXPECT_HR(hr, S_OK);
4611 
4612     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4613     EXPECT_HR(hr, S_OK);
4614 
4615     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4616     EXPECT_HR(hr, S_OK);
4617 
4618     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical);
4619     EXPECT_HR(hr, S_OK);
4620 
4621     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4622     EXPECT_HR(hr, S_OK);
4623 
4624     hr = ISAXContentHandler_startDocument(content);
4625     EXPECT_HR(hr, S_OK);
4626 
4627     hr = ISAXLexicalHandler_comment(lexical, NULL, 0);
4628     EXPECT_HR(hr, E_INVALIDARG);
4629 
4630     hr = IVBSAXLexicalHandler_comment(vblexical, NULL);
4631     EXPECT_HR(hr, E_POINTER);
4632 
4633     hr = ISAXLexicalHandler_comment(lexical, commentW, 0);
4634     EXPECT_HR(hr, S_OK);
4635 
4636     V_VT(&dest) = VT_EMPTY;
4637     hr = IMXWriter_get_output(writer, &dest);
4638     EXPECT_HR(hr, S_OK);
4639     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4640     ok(!lstrcmpW(_bstr_("<!---->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4641     VariantClear(&dest);
4642 
4643     hr = ISAXLexicalHandler_comment(lexical, commentW, ARRAY_SIZE(commentW) - 1);
4644     EXPECT_HR(hr, S_OK);
4645 
4646     V_VT(&dest) = VT_EMPTY;
4647     hr = IMXWriter_get_output(writer, &dest);
4648     EXPECT_HR(hr, S_OK);
4649     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4650     ok(!lstrcmpW(_bstr_("<!---->\r\n<!--comment-->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4651     VariantClear(&dest);
4652 
4653     ISAXContentHandler_Release(content);
4654     ISAXLexicalHandler_Release(lexical);
4655     IVBSAXLexicalHandler_Release(vblexical);
4656     IMXWriter_Release(writer);
4657     free_bstrs();
4658 }
4659 
4660 static void test_mxwriter_cdata(void)
4661 {
4662     IVBSAXLexicalHandler *vblexical;
4663     ISAXContentHandler *content;
4664     ISAXLexicalHandler *lexical;
4665     IMXWriter *writer;
4666     VARIANT dest;
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_ISAXContentHandler, (void**)&content);
4674     EXPECT_HR(hr, S_OK);
4675 
4676     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4677     EXPECT_HR(hr, S_OK);
4678 
4679     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical);
4680     EXPECT_HR(hr, S_OK);
4681 
4682     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4683     EXPECT_HR(hr, S_OK);
4684 
4685     hr = ISAXContentHandler_startDocument(content);
4686     EXPECT_HR(hr, S_OK);
4687 
4688     hr = ISAXLexicalHandler_startCDATA(lexical);
4689     EXPECT_HR(hr, S_OK);
4690 
4691     V_VT(&dest) = VT_EMPTY;
4692     hr = IMXWriter_get_output(writer, &dest);
4693     EXPECT_HR(hr, S_OK);
4694     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4695     ok(!lstrcmpW(_bstr_("<![CDATA["), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4696     VariantClear(&dest);
4697 
4698     hr = IVBSAXLexicalHandler_startCDATA(vblexical);
4699     EXPECT_HR(hr, S_OK);
4700 
4701     /* all these are escaped for text nodes */
4702     hr = ISAXContentHandler_characters(content, _bstr_("< > & \""), 7);
4703     EXPECT_HR(hr, S_OK);
4704 
4705     hr = ISAXLexicalHandler_endCDATA(lexical);
4706     EXPECT_HR(hr, S_OK);
4707 
4708     V_VT(&dest) = VT_EMPTY;
4709     hr = IMXWriter_get_output(writer, &dest);
4710     EXPECT_HR(hr, S_OK);
4711     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4712     ok(!lstrcmpW(_bstr_("<![CDATA[<![CDATA[< > & \"]]>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4713     VariantClear(&dest);
4714 
4715     ISAXContentHandler_Release(content);
4716     ISAXLexicalHandler_Release(lexical);
4717     IVBSAXLexicalHandler_Release(vblexical);
4718     IMXWriter_Release(writer);
4719     free_bstrs();
4720 }
4721 
4722 static void test_mxwriter_pi(void)
4723 {
4724     static const WCHAR targetW[] = {'t','a','r','g','e','t',0};
4725     static const WCHAR dataW[] = {'d','a','t','a',0};
4726     ISAXContentHandler *content;
4727     IMXWriter *writer;
4728     VARIANT dest;
4729     HRESULT hr;
4730 
4731     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4732             &IID_IMXWriter, (void**)&writer);
4733     EXPECT_HR(hr, S_OK);
4734 
4735     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4736     EXPECT_HR(hr, S_OK);
4737 
4738     hr = ISAXContentHandler_processingInstruction(content, NULL, 0, NULL, 0);
4739     EXPECT_HR(hr, E_INVALIDARG);
4740 
4741     hr = ISAXContentHandler_processingInstruction(content, targetW, 0, NULL, 0);
4742     EXPECT_HR(hr, S_OK);
4743 
4744     hr = ISAXContentHandler_processingInstruction(content, targetW, 6, NULL, 0);
4745     EXPECT_HR(hr, S_OK);
4746 
4747     V_VT(&dest) = VT_EMPTY;
4748     hr = IMXWriter_get_output(writer, &dest);
4749     EXPECT_HR(hr, S_OK);
4750     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4751     ok(!lstrcmpW(_bstr_("<?\?>\r\n<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4752     VariantClear(&dest);
4753 
4754     hr = ISAXContentHandler_processingInstruction(content, targetW, 4, dataW, 4);
4755     EXPECT_HR(hr, S_OK);
4756 
4757     V_VT(&dest) = VT_EMPTY;
4758     hr = IMXWriter_get_output(writer, &dest);
4759     EXPECT_HR(hr, S_OK);
4760     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4761     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)));
4762     VariantClear(&dest);
4763 
4764     V_VT(&dest) = VT_EMPTY;
4765     hr = IMXWriter_put_output(writer, dest);
4766     EXPECT_HR(hr, S_OK);
4767 
4768     hr = ISAXContentHandler_processingInstruction(content, targetW, 6, dataW, 0);
4769     EXPECT_HR(hr, S_OK);
4770 
4771     V_VT(&dest) = VT_EMPTY;
4772     hr = IMXWriter_get_output(writer, &dest);
4773     EXPECT_HR(hr, S_OK);
4774     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4775     ok(!lstrcmpW(_bstr_("<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4776     VariantClear(&dest);
4777 
4778 
4779     ISAXContentHandler_Release(content);
4780     IMXWriter_Release(writer);
4781 }
4782 
4783 static void test_mxwriter_ignorablespaces(void)
4784 {
4785     static const WCHAR dataW[] = {'d','a','t','a',0};
4786     ISAXContentHandler *content;
4787     IMXWriter *writer;
4788     VARIANT dest;
4789     HRESULT hr;
4790 
4791     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4792             &IID_IMXWriter, (void**)&writer);
4793     EXPECT_HR(hr, S_OK);
4794 
4795     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4796     EXPECT_HR(hr, S_OK);
4797 
4798     hr = ISAXContentHandler_ignorableWhitespace(content, NULL, 0);
4799     EXPECT_HR(hr, E_INVALIDARG);
4800 
4801     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 0);
4802     EXPECT_HR(hr, S_OK);
4803 
4804     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 4);
4805     EXPECT_HR(hr, S_OK);
4806 
4807     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 1);
4808     EXPECT_HR(hr, S_OK);
4809 
4810     V_VT(&dest) = VT_EMPTY;
4811     hr = IMXWriter_get_output(writer, &dest);
4812     EXPECT_HR(hr, S_OK);
4813     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4814     ok(!lstrcmpW(_bstr_("datad"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4815     VariantClear(&dest);
4816 
4817     ISAXContentHandler_Release(content);
4818     IMXWriter_Release(writer);
4819 }
4820 
4821 static void test_mxwriter_dtd(void)
4822 {
4823     static const WCHAR contentW[] = {'c','o','n','t','e','n','t'};
4824     static const WCHAR nameW[] = {'n','a','m','e'};
4825     static const WCHAR pubW[] = {'p','u','b'};
4826     static const WCHAR sysW[] = {'s','y','s'};
4827     IVBSAXLexicalHandler *vblexical;
4828     ISAXContentHandler *content;
4829     ISAXLexicalHandler *lexical;
4830     IVBSAXDeclHandler *vbdecl;
4831     ISAXDeclHandler *decl;
4832     ISAXDTDHandler *dtd;
4833     IMXWriter *writer;
4834     VARIANT dest;
4835     HRESULT hr;
4836 
4837     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4838             &IID_IMXWriter, (void**)&writer);
4839     EXPECT_HR(hr, S_OK);
4840 
4841     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4842     EXPECT_HR(hr, S_OK);
4843 
4844     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4845     EXPECT_HR(hr, S_OK);
4846 
4847     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl);
4848     EXPECT_HR(hr, S_OK);
4849 
4850     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXDeclHandler, (void**)&vbdecl);
4851     EXPECT_HR(hr, S_OK);
4852 
4853     hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical);
4854     EXPECT_HR(hr, S_OK);
4855 
4856     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4857     EXPECT_HR(hr, S_OK);
4858 
4859     hr = ISAXContentHandler_startDocument(content);
4860     EXPECT_HR(hr, S_OK);
4861 
4862     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0);
4863     EXPECT_HR(hr, E_INVALIDARG);
4864 
4865     hr = IVBSAXLexicalHandler_startDTD(vblexical, NULL, NULL, NULL);
4866     EXPECT_HR(hr, E_POINTER);
4867 
4868     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, ARRAY_SIZE(pubW), NULL, 0);
4869     EXPECT_HR(hr, E_INVALIDARG);
4870 
4871     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, sysW, ARRAY_SIZE(sysW));
4872     EXPECT_HR(hr, E_INVALIDARG);
4873 
4874     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, ARRAY_SIZE(pubW), sysW, ARRAY_SIZE(sysW));
4875     EXPECT_HR(hr, E_INVALIDARG);
4876 
4877     hr = ISAXLexicalHandler_startDTD(lexical, nameW, ARRAY_SIZE(nameW), NULL, 0, NULL, 0);
4878     EXPECT_HR(hr, S_OK);
4879 
4880     V_VT(&dest) = VT_EMPTY;
4881     hr = IMXWriter_get_output(writer, &dest);
4882     EXPECT_HR(hr, S_OK);
4883     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4884     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4885     VariantClear(&dest);
4886 
4887     /* system id is required if public is present */
4888     hr = ISAXLexicalHandler_startDTD(lexical, nameW, ARRAY_SIZE(nameW), pubW, ARRAY_SIZE(pubW), NULL, 0);
4889     EXPECT_HR(hr, E_INVALIDARG);
4890 
4891     hr = ISAXLexicalHandler_startDTD(lexical, nameW, ARRAY_SIZE(nameW),
4892         pubW, ARRAY_SIZE(pubW), sysW, ARRAY_SIZE(sysW));
4893     EXPECT_HR(hr, S_OK);
4894 
4895     V_VT(&dest) = VT_EMPTY;
4896     hr = IMXWriter_get_output(writer, &dest);
4897     EXPECT_HR(hr, S_OK);
4898     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4899     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4900         "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4901     VariantClear(&dest);
4902 
4903     hr = ISAXLexicalHandler_endDTD(lexical);
4904     EXPECT_HR(hr, S_OK);
4905 
4906     hr = IVBSAXLexicalHandler_endDTD(vblexical);
4907     EXPECT_HR(hr, S_OK);
4908 
4909     V_VT(&dest) = VT_EMPTY;
4910     hr = IMXWriter_get_output(writer, &dest);
4911     EXPECT_HR(hr, S_OK);
4912     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4913     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4914          "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\r\n]>\r\n"),
4915         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4916     VariantClear(&dest);
4917 
4918     /* element declaration */
4919     V_VT(&dest) = VT_EMPTY;
4920     hr = IMXWriter_put_output(writer, dest);
4921     EXPECT_HR(hr, S_OK);
4922 
4923     hr = ISAXDeclHandler_elementDecl(decl, NULL, 0, NULL, 0);
4924     EXPECT_HR(hr, E_INVALIDARG);
4925 
4926     hr = IVBSAXDeclHandler_elementDecl(vbdecl, NULL, NULL);
4927     EXPECT_HR(hr, E_POINTER);
4928 
4929     hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), NULL, 0);
4930     EXPECT_HR(hr, E_INVALIDARG);
4931 
4932     hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), contentW, ARRAY_SIZE(contentW));
4933     EXPECT_HR(hr, S_OK);
4934 
4935     V_VT(&dest) = VT_EMPTY;
4936     hr = IMXWriter_get_output(writer, &dest);
4937     EXPECT_HR(hr, S_OK);
4938     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4939     ok(!lstrcmpW(_bstr_("<!ELEMENT name content>\r\n"),
4940         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4941     VariantClear(&dest);
4942 
4943     V_VT(&dest) = VT_EMPTY;
4944     hr = IMXWriter_put_output(writer, dest);
4945     EXPECT_HR(hr, S_OK);
4946 
4947     hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), contentW, 0);
4948     EXPECT_HR(hr, S_OK);
4949 
4950     V_VT(&dest) = VT_EMPTY;
4951     hr = IMXWriter_get_output(writer, &dest);
4952     EXPECT_HR(hr, S_OK);
4953     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4954     ok(!lstrcmpW(_bstr_("<!ELEMENT name >\r\n"),
4955         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4956     VariantClear(&dest);
4957 
4958     /* attribute declaration */
4959     V_VT(&dest) = VT_EMPTY;
4960     hr = IMXWriter_put_output(writer, dest);
4961     EXPECT_HR(hr, S_OK);
4962 
4963     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"),
4964         _bstr_("attribute"), strlen("attribute"), _bstr_("CDATA"), strlen("CDATA"),
4965         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value"), strlen("value"));
4966     EXPECT_HR(hr, S_OK);
4967 
4968     V_VT(&dest) = VT_EMPTY;
4969     hr = IMXWriter_get_output(writer, &dest);
4970     EXPECT_HR(hr, S_OK);
4971     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4972     ok(!lstrcmpW(_bstr_("<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"),
4973         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4974     VariantClear(&dest);
4975 
4976     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"),
4977         _bstr_("attribute2"), strlen("attribute2"), _bstr_("CDATA"), strlen("CDATA"),
4978         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value2"), strlen("value2"));
4979     EXPECT_HR(hr, S_OK);
4980 
4981     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element2"), strlen("element2"),
4982         _bstr_("attribute3"), strlen("attribute3"), _bstr_("CDATA"), strlen("CDATA"),
4983         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value3"), strlen("value3"));
4984     EXPECT_HR(hr, S_OK);
4985 
4986     V_VT(&dest) = VT_EMPTY;
4987     hr = IMXWriter_get_output(writer, &dest);
4988     EXPECT_HR(hr, S_OK);
4989     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4990     ok(!lstrcmpW(_bstr_("<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"
4991                         "<!ATTLIST element attribute2 CDATA #REQUIRED \"value2\">\r\n"
4992                         "<!ATTLIST element2 attribute3 CDATA #REQUIRED \"value3\">\r\n"),
4993         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4994     VariantClear(&dest);
4995 
4996     /* internal entities */
4997     V_VT(&dest) = VT_EMPTY;
4998     hr = IMXWriter_put_output(writer, dest);
4999     EXPECT_HR(hr, S_OK);
5000 
5001     hr = ISAXDeclHandler_internalEntityDecl(decl, NULL, 0, NULL, 0);
5002     EXPECT_HR(hr, E_INVALIDARG);
5003 
5004     hr = IVBSAXDeclHandler_internalEntityDecl(vbdecl, NULL, NULL);
5005     EXPECT_HR(hr, E_POINTER);
5006 
5007     hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), -1, NULL, 0);
5008     EXPECT_HR(hr, E_INVALIDARG);
5009 
5010     hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("value"), strlen("value"));
5011     EXPECT_HR(hr, S_OK);
5012 
5013     V_VT(&dest) = VT_EMPTY;
5014     hr = IMXWriter_get_output(writer, &dest);
5015     EXPECT_HR(hr, S_OK);
5016     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5017     ok(!lstrcmpW(_bstr_("<!ENTITY name \"value\">\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5018     VariantClear(&dest);
5019 
5020     /* external entities */
5021     V_VT(&dest) = VT_EMPTY;
5022     hr = IMXWriter_put_output(writer, dest);
5023     ok(hr == S_OK, "got 0x%08x\n", hr);
5024 
5025     hr = ISAXDeclHandler_externalEntityDecl(decl, NULL, 0, NULL, 0, NULL, 0);
5026     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5027 
5028     hr = IVBSAXDeclHandler_externalEntityDecl(vbdecl, NULL, NULL, NULL);
5029     ok(hr == E_POINTER, "got 0x%08x\n", hr);
5030 
5031     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), 0, NULL, 0, NULL, 0);
5032     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5033 
5034     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), -1, NULL, 0, NULL, 0);
5035     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5036 
5037     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"),
5038         _bstr_("sysid"), strlen("sysid"));
5039     ok(hr == S_OK, "got 0x%08x\n", hr);
5040 
5041     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), NULL, 0, _bstr_("sysid"), strlen("sysid"));
5042     ok(hr == S_OK, "got 0x%08x\n", hr);
5043 
5044     hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"),
5045         NULL, 0);
5046     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5047 
5048     V_VT(&dest) = VT_EMPTY;
5049     hr = IMXWriter_get_output(writer, &dest);
5050     ok(hr == S_OK, "got 0x%08x\n", hr);
5051     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5052     ok(!lstrcmpW(_bstr_(
5053         "<!ENTITY name PUBLIC \"pubid\" \"sysid\">\r\n"
5054         "<!ENTITY name SYSTEM \"sysid\">\r\n"),
5055         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5056 
5057     VariantClear(&dest);
5058 
5059     /* notation declaration */
5060     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDTDHandler, (void**)&dtd);
5061     ok(hr == S_OK, "got 0x%08x\n", hr);
5062 
5063     V_VT(&dest) = VT_EMPTY;
5064     hr = IMXWriter_put_output(writer, dest);
5065     ok(hr == S_OK, "got 0x%08x\n", hr);
5066 
5067     hr = ISAXDTDHandler_notationDecl(dtd, NULL, 0, NULL, 0, NULL, 0);
5068     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5069 
5070     hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), NULL, 0, NULL, 0);
5071     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5072 
5073     hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), NULL, 0);
5074     ok(hr == S_OK, "got 0x%08x\n", hr);
5075 
5076     hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), _bstr_("sysid"), strlen("sysid"));
5077     ok(hr == S_OK, "got 0x%08x\n", hr);
5078 
5079     hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), NULL, 0, _bstr_("sysid"), strlen("sysid"));
5080     ok(hr == S_OK, "got 0x%08x\n", hr);
5081 
5082     hr = IMXWriter_get_output(writer, &dest);
5083     ok(hr == S_OK, "got 0x%08x\n", hr);
5084     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5085     ok(!lstrcmpW(_bstr_(
5086         "<!NOTATION name"
5087         "<!NOTATION name PUBLIC \"pubid\">\r\n"
5088         "<!NOTATION name PUBLIC \"pubid\" \"sysid\">\r\n"
5089         "<!NOTATION name SYSTEM \"sysid\">\r\n"),
5090         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5091 
5092     VariantClear(&dest);
5093 
5094     ISAXDTDHandler_Release(dtd);
5095 
5096     ISAXContentHandler_Release(content);
5097     ISAXLexicalHandler_Release(lexical);
5098     IVBSAXLexicalHandler_Release(vblexical);
5099     IVBSAXDeclHandler_Release(vbdecl);
5100     ISAXDeclHandler_Release(decl);
5101     IMXWriter_Release(writer);
5102     free_bstrs();
5103 }
5104 
5105 typedef struct {
5106     const CLSID *clsid;
5107     const char *uri;
5108     const char *local;
5109     const char *qname;
5110     const char *type;
5111     const char *value;
5112     HRESULT hr;
5113 } addattribute_test_t;
5114 
5115 static const addattribute_test_t addattribute_data[] = {
5116     { &CLSID_SAXAttributes,   NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
5117     { &CLSID_SAXAttributes30, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
5118     { &CLSID_SAXAttributes40, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
5119     { &CLSID_SAXAttributes60, NULL, NULL, "ns:qname", NULL, "value", S_OK },
5120 
5121     { &CLSID_SAXAttributes,   NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5122     { &CLSID_SAXAttributes30, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5123     { &CLSID_SAXAttributes40, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5124     { &CLSID_SAXAttributes60, NULL, "qname", "ns:qname", NULL, "value", S_OK },
5125 
5126     { &CLSID_SAXAttributes,   "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5127     { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5128     { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
5129     { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", NULL, "value", S_OK },
5130 
5131     { &CLSID_SAXAttributes,   "uri", "qname", "ns:qname", "type", "value", S_OK },
5132     { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", "type", "value", S_OK },
5133     { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", "type", "value", S_OK },
5134     { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", "type", "value", S_OK },
5135 
5136     { NULL }
5137 };
5138 
5139 static void test_mxattr_addAttribute(void)
5140 {
5141     const addattribute_test_t *table = addattribute_data;
5142     int i = 0;
5143 
5144     while (table->clsid)
5145     {
5146         ISAXAttributes *saxattr;
5147         IMXAttributes *mxattr;
5148         const WCHAR *value;
5149         int len, index;
5150         HRESULT hr;
5151 
5152         if (!is_clsid_supported(table->clsid, mxattributes_support_data))
5153         {
5154             table++;
5155             i++;
5156             continue;
5157         }
5158 
5159         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
5160             &IID_IMXAttributes, (void**)&mxattr);
5161         EXPECT_HR(hr, S_OK);
5162 
5163         hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5164         EXPECT_HR(hr, S_OK);
5165 
5166         /* SAXAttributes40 and SAXAttributes60 both crash on this test */
5167         if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5168             IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5169         {
5170             hr = ISAXAttributes_getLength(saxattr, NULL);
5171             EXPECT_HR(hr, E_POINTER);
5172         }
5173 
5174         len = -1;
5175         hr = ISAXAttributes_getLength(saxattr, &len);
5176         EXPECT_HR(hr, S_OK);
5177         ok(len == 0, "got %d\n", len);
5178 
5179         hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
5180         EXPECT_HR(hr, E_INVALIDARG);
5181 
5182         hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
5183         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5184 
5185         hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
5186         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5187 
5188         hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
5189         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5190 
5191         hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
5192         EXPECT_HR(hr, E_INVALIDARG);
5193 
5194         hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
5195         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5196 
5197         hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
5198         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5199 
5200         hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
5201         ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5202 
5203         hr = IMXAttributes_addAttribute(mxattr, _bstr_(table->uri), _bstr_(table->local),
5204             _bstr_(table->qname), _bstr_(table->type), _bstr_(table->value));
5205         ok(hr == table->hr, "%d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
5206 
5207         if (hr == S_OK)
5208         {
5209             /* SAXAttributes40 and SAXAttributes60 both crash on this test */
5210             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5211                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5212             {
5213                hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
5214                EXPECT_HR(hr, E_POINTER);
5215 
5216                hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
5217                EXPECT_HR(hr, E_POINTER);
5218 
5219                hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
5220                EXPECT_HR(hr, E_POINTER);
5221 
5222                hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
5223                EXPECT_HR(hr, E_POINTER);
5224 
5225                hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
5226                EXPECT_HR(hr, E_POINTER);
5227 
5228                hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
5229                EXPECT_HR(hr, E_POINTER);
5230             }
5231 
5232             len = -1;
5233             hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
5234             EXPECT_HR(hr, S_OK);
5235             ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
5236                 table->value);
5237             ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
5238 
5239             len = -1;
5240             value = (void*)0xdeadbeef;
5241             hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
5242             EXPECT_HR(hr, S_OK);
5243 
5244             if (table->type)
5245             {
5246                 ok(!lstrcmpW(_bstr_(table->type), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
5247                     table->type);
5248                 ok(lstrlenW(value) == len, "%d: got wrong type value length %d\n", i, len);
5249             }
5250             else
5251             {
5252                 ok(*value == 0, "%d: got type value %s\n", i, wine_dbgstr_w(value));
5253                 ok(len == 0, "%d: got wrong type value length %d\n", i, len);
5254             }
5255 
5256             hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, NULL);
5257             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5258                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5259             {
5260                 EXPECT_HR(hr, E_POINTER);
5261             }
5262             else
5263                 EXPECT_HR(hr, E_INVALIDARG);
5264 
5265             hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, &index);
5266             EXPECT_HR(hr, E_INVALIDARG);
5267 
5268             index = -1;
5269             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_("nonexistent"), 11, &index);
5270             EXPECT_HR(hr, E_INVALIDARG);
5271             ok(index == -1, "%d: got wrong index %d\n", i, index);
5272 
5273             index = -1;
5274             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), 0, &index);
5275             EXPECT_HR(hr, E_INVALIDARG);
5276             ok(index == -1, "%d: got wrong index %d\n", i, index);
5277 
5278             index = -1;
5279             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &index);
5280             EXPECT_HR(hr, S_OK);
5281             ok(index == 0, "%d: got wrong index %d\n", i, index);
5282 
5283             index = -1;
5284             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname)-1, &index);
5285             EXPECT_HR(hr, E_INVALIDARG);
5286             ok(index == -1, "%d: got wrong index %d\n", i, index);
5287 
5288             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes40) ||
5289                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes60))
5290             {
5291                 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
5292                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5293 
5294                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
5295                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5296 
5297                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
5298                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5299 
5300                 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
5301                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5302 
5303                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
5304                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5305 
5306                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
5307                 ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr);
5308             }
5309             else
5310             {
5311                 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
5312                 EXPECT_HR(hr, E_POINTER);
5313 
5314                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
5315                 EXPECT_HR(hr, E_POINTER);
5316 
5317                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
5318                 EXPECT_HR(hr, E_POINTER);
5319 
5320                 /* versions 4 and 6 crash */
5321                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL);
5322                 EXPECT_HR(hr, E_POINTER);
5323 
5324                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len);
5325                 EXPECT_HR(hr, E_POINTER);
5326 
5327                 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
5328                 EXPECT_HR(hr, E_POINTER);
5329 
5330                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
5331                 EXPECT_HR(hr, E_POINTER);
5332 
5333                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
5334                 EXPECT_HR(hr, E_POINTER);
5335 
5336                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL);
5337                 EXPECT_HR(hr, E_POINTER);
5338 
5339                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len);
5340                 EXPECT_HR(hr, E_POINTER);
5341 
5342                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local),
5343                     strlen(table->local), NULL, NULL);
5344                 EXPECT_HR(hr, E_POINTER);
5345             }
5346 
5347             hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len);
5348             EXPECT_HR(hr, S_OK);
5349             ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
5350                 table->value);
5351             ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
5352 
5353             if (table->uri) {
5354                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri),
5355                     _bstr_(table->local), strlen(table->local), &value, &len);
5356                 EXPECT_HR(hr, S_OK);
5357                 ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
5358                     table->value);
5359                 ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
5360             }
5361         }
5362 
5363         len = -1;
5364         hr = ISAXAttributes_getLength(saxattr, &len);
5365         EXPECT_HR(hr, S_OK);
5366         if (table->hr == S_OK)
5367             ok(len == 1, "%d: got %d length, expected 1\n", i, len);
5368         else
5369             ok(len == 0, "%d: got %d length, expected 0\n", i, len);
5370 
5371         ISAXAttributes_Release(saxattr);
5372         IMXAttributes_Release(mxattr);
5373 
5374         table++;
5375         i++;
5376     }
5377 
5378     free_bstrs();
5379 }
5380 
5381 static void test_mxattr_clear(void)
5382 {
5383     ISAXAttributes *saxattr;
5384     IMXAttributes *mxattr;
5385     const WCHAR *ptr;
5386     HRESULT hr;
5387     int len;
5388 
5389     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
5390         &IID_IMXAttributes, (void**)&mxattr);
5391     EXPECT_HR(hr, S_OK);
5392 
5393     hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5394     EXPECT_HR(hr, S_OK);
5395 
5396     hr = ISAXAttributes_getQName(saxattr, 0, NULL, NULL);
5397     EXPECT_HR(hr, E_INVALIDARG);
5398 
5399     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
5400     EXPECT_HR(hr, E_INVALIDARG);
5401 
5402     hr = IMXAttributes_clear(mxattr);
5403     EXPECT_HR(hr, S_OK);
5404 
5405     hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("local"),
5406         _bstr_("qname"), _bstr_("type"), _bstr_("value"));
5407     EXPECT_HR(hr, S_OK);
5408 
5409     len = -1;
5410     hr = ISAXAttributes_getLength(saxattr, &len);
5411     EXPECT_HR(hr, S_OK);
5412     ok(len == 1, "got %d\n", len);
5413 
5414     len = -1;
5415     hr = ISAXAttributes_getQName(saxattr, 0, NULL, &len);
5416     EXPECT_HR(hr, E_POINTER);
5417     ok(len == -1, "got %d\n", len);
5418 
5419     ptr = (void*)0xdeadbeef;
5420     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, NULL);
5421     EXPECT_HR(hr, E_POINTER);
5422     ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
5423 
5424     len = 0;
5425     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
5426     EXPECT_HR(hr, S_OK);
5427     ok(len == 5, "got %d\n", len);
5428     ok(!lstrcmpW(ptr, _bstr_("qname")), "got %s\n", wine_dbgstr_w(ptr));
5429 
5430     hr = IMXAttributes_clear(mxattr);
5431     EXPECT_HR(hr, S_OK);
5432 
5433     len = -1;
5434     hr = ISAXAttributes_getLength(saxattr, &len);
5435     EXPECT_HR(hr, S_OK);
5436     ok(len == 0, "got %d\n", len);
5437 
5438     len = -1;
5439     ptr = (void*)0xdeadbeef;
5440     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
5441     EXPECT_HR(hr, E_INVALIDARG);
5442     ok(len == -1, "got %d\n", len);
5443     ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
5444 
5445     IMXAttributes_Release(mxattr);
5446     ISAXAttributes_Release(saxattr);
5447     free_bstrs();
5448 }
5449 
5450 static void test_mxattr_dispex(void)
5451 {
5452     IMXAttributes *mxattr;
5453     IDispatchEx *dispex;
5454     IUnknown *unk;
5455     HRESULT hr;
5456 
5457     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
5458             &IID_IMXAttributes, (void**)&mxattr);
5459     EXPECT_HR(hr, S_OK);
5460 
5461     hr = IMXAttributes_QueryInterface(mxattr, &IID_IDispatchEx, (void**)&dispex);
5462     EXPECT_HR(hr, S_OK);
5463     hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
5464     test_obj_dispex(unk);
5465     IUnknown_Release(unk);
5466     IDispatchEx_Release(dispex);
5467 
5468     IMXAttributes_Release(mxattr);
5469 }
5470 
5471 static void test_mxattr_qi(void)
5472 {
5473     IVBSAXAttributes *vbsaxattr, *vbsaxattr2;
5474     ISAXAttributes *saxattr;
5475     IMXAttributes *mxattr;
5476     HRESULT hr;
5477 
5478     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
5479             &IID_IMXAttributes, (void**)&mxattr);
5480     EXPECT_HR(hr, S_OK);
5481 
5482     EXPECT_REF(mxattr, 1);
5483     hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5484     EXPECT_HR(hr, S_OK);
5485 
5486     EXPECT_REF(mxattr, 2);
5487     EXPECT_REF(saxattr, 2);
5488 
5489     hr = IMXAttributes_QueryInterface(mxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr);
5490     EXPECT_HR(hr, S_OK);
5491 
5492     EXPECT_REF(vbsaxattr, 3);
5493     EXPECT_REF(mxattr, 3);
5494     EXPECT_REF(saxattr, 3);
5495 
5496     hr = ISAXAttributes_QueryInterface(saxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr2);
5497     EXPECT_HR(hr, S_OK);
5498 
5499     EXPECT_REF(vbsaxattr, 4);
5500     EXPECT_REF(mxattr, 4);
5501     EXPECT_REF(saxattr, 4);
5502 
5503     IMXAttributes_Release(mxattr);
5504     ISAXAttributes_Release(saxattr);
5505     IVBSAXAttributes_Release(vbsaxattr);
5506     IVBSAXAttributes_Release(vbsaxattr2);
5507 }
5508 
5509 static struct msxmlsupported_data_t saxattr_support_data[] =
5510 {
5511     { &CLSID_SAXAttributes,   "SAXAttributes"   },
5512     { &CLSID_SAXAttributes30, "SAXAttributes30" },
5513     { &CLSID_SAXAttributes40, "SAXAttributes40" },
5514     { &CLSID_SAXAttributes60, "SAXAttributes60" },
5515     { NULL }
5516 };
5517 
5518 static void test_mxattr_localname(void)
5519 {
5520     static const WCHAR localname1W[] = {'l','o','c','a','l','n','a','m','e','1',0};
5521     static const WCHAR localnameW[] = {'l','o','c','a','l','n','a','m','e',0};
5522     static const WCHAR uri1W[] = {'u','r','i','1',0};
5523     static const WCHAR uriW[] = {'u','r','i',0};
5524 
5525     const struct msxmlsupported_data_t *table = saxattr_support_data;
5526 
5527     while (table->clsid)
5528     {
5529         ISAXAttributes *saxattr;
5530         IMXAttributes *mxattr;
5531         HRESULT hr;
5532         int index;
5533 
5534         if (!is_clsid_supported(table->clsid, mxattributes_support_data))
5535         {
5536             table++;
5537             continue;
5538         }
5539 
5540         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
5541             &IID_IMXAttributes, (void**)&mxattr);
5542         EXPECT_HR(hr, S_OK);
5543 
5544         hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5545         EXPECT_HR(hr, S_OK);
5546 
5547         hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, &index);
5548         EXPECT_HR(hr, E_INVALIDARG);
5549 
5550         /* add some ambiguos attribute names */
5551         hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
5552             _bstr_("a:localname"), _bstr_(""), _bstr_("value"));
5553         EXPECT_HR(hr, S_OK);
5554         hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
5555             _bstr_("b:localname"), _bstr_(""), _bstr_("value"));
5556         EXPECT_HR(hr, S_OK);
5557 
5558         index = -1;
5559         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localnameW, lstrlenW(localnameW), &index);
5560         EXPECT_HR(hr, S_OK);
5561         ok(index == 0, "%s: got index %d\n", table->name, index);
5562 
5563         index = -1;
5564         hr = ISAXAttributes_getIndexFromName(saxattr, uri1W, lstrlenW(uri1W), localnameW, lstrlenW(localnameW), &index);
5565         EXPECT_HR(hr, E_INVALIDARG);
5566         ok(index == -1, "%s: got index %d\n", table->name, index);
5567 
5568         index = -1;
5569         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), &index);
5570         EXPECT_HR(hr, E_INVALIDARG);
5571         ok(index == -1, "%s: got index %d\n", table->name, index);
5572 
5573         if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5574             IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5575         {
5576             hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
5577             EXPECT_HR(hr, E_POINTER);
5578 
5579             hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
5580             EXPECT_HR(hr, E_POINTER);
5581         }
5582         else
5583         {
5584             hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
5585             EXPECT_HR(hr, E_INVALIDARG);
5586 
5587             hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
5588             EXPECT_HR(hr, E_INVALIDARG);
5589         }
5590 
5591         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), NULL, 0, &index);
5592         EXPECT_HR(hr, E_INVALIDARG);
5593 
5594         hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, localname1W, lstrlenW(localname1W), &index);
5595         EXPECT_HR(hr, E_INVALIDARG);
5596 
5597         table++;
5598 
5599         ISAXAttributes_Release(saxattr);
5600         IMXAttributes_Release(mxattr);
5601         free_bstrs();
5602     }
5603 }
5604 
5605 static void test_mxwriter_indent(void)
5606 {
5607     ISAXContentHandler *content;
5608     IMXWriter *writer;
5609     VARIANT dest;
5610     HRESULT hr;
5611 
5612     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer);
5613     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
5614 
5615     hr = IMXWriter_put_indent(writer, VARIANT_TRUE);
5616     ok(hr == S_OK, "got %08x\n", hr);
5617 
5618     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
5619     ok(hr == S_OK, "got %08x\n", hr);
5620 
5621     hr = ISAXContentHandler_startDocument(content);
5622     ok(hr == S_OK, "got %08x\n", hr);
5623 
5624     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
5625     ok(hr == S_OK, "got %08x\n", hr);
5626 
5627     hr = ISAXContentHandler_characters(content, _bstr_(""), 0);
5628     ok(hr == S_OK, "got %08x\n", hr);
5629 
5630     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("b"), -1, NULL);
5631     ok(hr == S_OK, "got %08x\n", hr);
5632 
5633     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("c"), -1, NULL);
5634     ok(hr == S_OK, "got %08x\n", hr);
5635 
5636     hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("c"), -1);
5637     ok(hr == S_OK, "got %08x\n", hr);
5638 
5639     hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("b"), -1);
5640     ok(hr == S_OK, "got %08x\n", hr);
5641 
5642     hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1);
5643     ok(hr == S_OK, "got %08x\n", hr);
5644 
5645     hr = ISAXContentHandler_endDocument(content);
5646     ok(hr == S_OK, "got %08x\n", hr);
5647 
5648     V_VT(&dest) = VT_EMPTY;
5649     hr = IMXWriter_get_output(writer, &dest);
5650     ok(hr == S_OK, "got %08x\n", hr);
5651     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
5652     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)),
5653         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
5654     VariantClear(&dest);
5655 
5656     ISAXContentHandler_Release(content);
5657     IMXWriter_Release(writer);
5658 
5659     free_bstrs();
5660 }
5661 
5662 START_TEST(saxreader)
5663 {
5664     ISAXXMLReader *reader;
5665     HRESULT hr;
5666 
5667     hr = CoInitialize(NULL);
5668     ok(hr == S_OK, "failed to init com\n");
5669 
5670     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
5671             &IID_ISAXXMLReader, (void**)&reader);
5672 
5673     if(FAILED(hr))
5674     {
5675         win_skip("Failed to create SAXXMLReader instance\n");
5676         CoUninitialize();
5677         return;
5678     }
5679     ISAXXMLReader_Release(reader);
5680 
5681     init_call_sequences(sequences, NUM_CALL_SEQUENCES);
5682 
5683     get_class_support_data(reader_support_data, &IID_ISAXXMLReader);
5684 
5685     test_saxreader();
5686     test_saxreader_properties();
5687     test_saxreader_features();
5688     test_saxreader_encoding();
5689     test_saxreader_dispex();
5690 
5691     /* MXXMLWriter tests */
5692     get_class_support_data(mxwriter_support_data, &IID_IMXWriter);
5693     if (is_clsid_supported(&CLSID_MXXMLWriter, mxwriter_support_data))
5694     {
5695         test_mxwriter_handlers();
5696         test_mxwriter_startenddocument();
5697         test_mxwriter_startendelement();
5698         test_mxwriter_characters();
5699         test_mxwriter_comment();
5700         test_mxwriter_cdata();
5701         test_mxwriter_pi();
5702         test_mxwriter_ignorablespaces();
5703         test_mxwriter_dtd();
5704         test_mxwriter_properties();
5705         test_mxwriter_flush();
5706         test_mxwriter_stream();
5707         test_mxwriter_encoding();
5708         test_mxwriter_dispex();
5709         test_mxwriter_indent();
5710     }
5711     else
5712         win_skip("MXXMLWriter not supported\n");
5713 
5714     /* SAXAttributes tests */
5715     get_class_support_data(mxattributes_support_data, &IID_IMXAttributes);
5716     if (is_clsid_supported(&CLSID_SAXAttributes, mxattributes_support_data))
5717     {
5718         test_mxattr_qi();
5719         test_mxattr_addAttribute();
5720         test_mxattr_clear();
5721         test_mxattr_localname();
5722         test_mxattr_dispex();
5723     }
5724     else
5725         win_skip("SAXAttributes not supported\n");
5726 
5727     CoUninitialize();
5728 }
5729