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