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