domdoc.c 98 KB
Newer Older
1 2 3 4
/*
 *    DOM Document implementation
 *
 * Copyright 2005 Mike McCormack
5
 * Copyright 2010-2011 Adam Martinson for CodeWeavers
6
 *
7
 * This library is free software; you can redistribute it and/or
8 9 10 11
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
12
 * This library is distributed in the hope that it will be useful,
13 14 15 16 17 18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22 23 24 25 26
 */

#define COBJMACROS

#include "config.h"

#include <stdarg.h>
27
#include <assert.h>
28 29 30 31 32 33 34 35 36
#ifdef HAVE_LIBXML2
# include <libxml/parser.h>
# include <libxml/xmlerror.h>
# include <libxml/xpathInternals.h>
# include <libxml/xmlsave.h>
# include <libxml/SAX2.h>
# include <libxml/parserInternals.h>
#endif

37 38 39
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
40
#include "winnls.h"
41
#include "ole2.h"
42
#include "olectl.h"
43
#include "msxml6.h"
44 45 46
#include "wininet.h"
#include "winreg.h"
#include "shlwapi.h"
47
#include "ocidl.h"
48
#include "objsafe.h"
49 50 51 52 53 54 55

#include "wine/debug.h"

#include "msxml_private.h"

#ifdef HAVE_LIBXML2

56 57
WINE_DEFAULT_DEBUG_CHANNEL(msxml);

58 59 60 61 62 63 64 65 66
/* not defined in older versions */
#define XML_SAVE_FORMAT     1
#define XML_SAVE_NO_DECL    2
#define XML_SAVE_NO_EMPTY   4
#define XML_SAVE_NO_XHTML   8
#define XML_SAVE_XHTML     16
#define XML_SAVE_AS_XML    32
#define XML_SAVE_AS_HTML   64

67 68 69
static const WCHAR PropertySelectionLanguageW[] = {'S','e','l','e','c','t','i','o','n','L','a','n','g','u','a','g','e',0};
static const WCHAR PropertySelectionNamespacesW[] = {'S','e','l','e','c','t','i','o','n','N','a','m','e','s','p','a','c','e','s',0};
static const WCHAR PropertyProhibitDTDW[] = {'P','r','o','h','i','b','i','t','D','T','D',0};
70
static const WCHAR PropertyNewParserW[] = {'N','e','w','P','a','r','s','e','r',0};
71 72
static const WCHAR PropValueXPathW[] = {'X','P','a','t','h',0};
static const WCHAR PropValueXSLPatternW[] = {'X','S','L','P','a','t','t','e','r','n',0};
73
static const WCHAR PropertyResolveExternalsW[] = {'R','e','s','o','l','v','e','E','x','t','e','r','n','a','l','s',0};
74 75
static const WCHAR PropertyAllowXsltScriptW[] = {'A','l','l','o','w','X','s','l','t','S','c','r','i','p','t',0};
static const WCHAR PropertyAllowDocumentFunctionW[] = {'A','l','l','o','w','D','o','c','u','m','e','n','t','F','u','n','c','t','i','o','n',0};
76

77 78
/* Anything that passes the test_get_ownerDocument()
 * tests can go here (data shared between all instances).
79 80
 * We need to preserve this when reloading a document,
 * and also need access to it from the libxml backend. */
81
typedef struct {
82
    MSXML_VERSION version;
83
    VARIANT_BOOL preserving;
84
    IXMLDOMSchemaCollection2* schemaCache;
85 86 87 88
    struct list selectNsList;
    xmlChar const* selectNsStr;
    LONG selectNsStr_len;
    BOOL XPath;
89
    WCHAR *url;
90 91
} domdoc_properties;

92 93 94 95 96
typedef struct ConnectionPoint ConnectionPoint;
typedef struct domdoc domdoc;

struct ConnectionPoint
{
97
    IConnectionPoint IConnectionPoint_iface;
98 99 100 101 102
    const IID *iid;

    ConnectionPoint *next;
    IConnectionPointContainer *container;
    domdoc *doc;
103 104 105 106 107 108 109 110

    union
    {
        IUnknown *unk;
        IDispatch *disp;
        IPropertyNotifySink *propnotif;
    } *sinks;
    DWORD sinks_size;
111 112
};

113 114 115 116 117 118 119
typedef enum {
    EVENTID_READYSTATECHANGE = 0,
    EVENTID_DATAAVAILABLE,
    EVENTID_TRANSFORMNODE,
    EVENTID_LAST
} eventid_t;

120
struct domdoc
121
{
122
    xmlnode node;
123 124 125 126 127
    IXMLDOMDocument3          IXMLDOMDocument3_iface;
    IPersistStreamInit        IPersistStreamInit_iface;
    IObjectWithSite           IObjectWithSite_iface;
    IObjectSafety             IObjectSafety_iface;
    IConnectionPointContainer IConnectionPointContainer_iface;
128 129 130 131
    LONG ref;
    VARIANT_BOOL async;
    VARIANT_BOOL validating;
    VARIANT_BOOL resolving;
132
    domdoc_properties* properties;
133 134
    HRESULT error;

135
    /* IObjectWithSite */
136 137 138 139
    IUnknown *site;

    /* IObjectSafety */
    DWORD safeopt;
140 141 142 143 144 145

    /* connection list */
    ConnectionPoint *cp_list;
    ConnectionPoint cp_domdocevents;
    ConnectionPoint cp_propnotif;
    ConnectionPoint cp_dispatch;
146 147 148

    /* events */
    IDispatch *events[EVENTID_LAST];
149 150

    IXMLDOMSchemaCollection2 *namespaces;
151 152
};

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
static HRESULT set_doc_event(domdoc *doc, eventid_t eid, const VARIANT *v)
{
    IDispatch *disp;

    switch (V_VT(v))
    {
    case VT_UNKNOWN:
        if (V_UNKNOWN(v))
            IUnknown_QueryInterface(V_UNKNOWN(v), &IID_IDispatch, (void**)&disp);
        else
            disp = NULL;
        break;
    case VT_DISPATCH:
        disp = V_DISPATCH(v);
        if (disp) IDispatch_AddRef(disp);
        break;
    default:
        return DISP_E_TYPEMISMATCH;
    }

    if (doc->events[eid]) IDispatch_Release(doc->events[eid]);
    doc->events[eid] = disp;

    return S_OK;
}

179 180
static inline ConnectionPoint *impl_from_IConnectionPoint(IConnectionPoint *iface)
{
181
    return CONTAINING_RECORD(iface, ConnectionPoint, IConnectionPoint_iface);
182
}
183

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
/*
  In native windows, the whole lifetime management of XMLDOMNodes is
  managed automatically using reference counts. Wine emulates that by
  maintaining a reference count to the document that is increased for
  each IXMLDOMNode pointer passed out for this document. If all these
  pointers are gone, the document is unreachable and gets freed, that
  is, all nodes in the tree of the document get freed.

  You are able to create nodes that are associated to a document (in
  fact, in msxml's XMLDOM model, all nodes are associated to a document),
  but not in the tree of that document, for example using the createFoo
  functions from IXMLDOMDocument. These nodes do not get cleaned up
  by libxml, so we have to do it ourselves.

  To catch these nodes, a list of "orphan nodes" is introduced.
  It contains pointers to all roots of node trees that are
  associated with the document without being part of the document
  tree. All nodes with parent==NULL (except for the document root nodes)
  should be in the orphan node list of their document. All orphan nodes
  get freed together with the document itself.
 */

206 207
typedef struct _xmldoc_priv {
    LONG refs;
208
    struct list orphans;
209
    domdoc_properties* properties;
210 211
} xmldoc_priv;

212 213 214 215 216
typedef struct _orphan_entry {
    struct list entry;
    xmlNode * node;
} orphan_entry;

217 218 219 220 221 222 223 224
typedef struct _select_ns_entry {
    struct list entry;
    xmlChar const* prefix;
    xmlChar prefix_end;
    xmlChar const* href;
    xmlChar href_end;
} select_ns_entry;

225
static inline xmldoc_priv * priv_from_xmlDocPtr(const xmlDocPtr doc)
226 227 228 229
{
    return doc->_private;
}

230
static inline domdoc_properties * properties_from_xmlDocPtr(xmlDocPtr doc)
231
{
232
    return priv_from_xmlDocPtr(doc)->properties;
233 234
}

235
BOOL is_xpathmode(const xmlDocPtr doc)
236
{
237
    return properties_from_xmlDocPtr(doc)->XPath;
238 239
}

240 241 242 243 244
void set_xpathmode(xmlDocPtr doc, BOOL xpath)
{
    properties_from_xmlDocPtr(doc)->XPath = xpath;
}

245 246 247 248
int registerNamespaces(xmlXPathContextPtr ctxt)
{
    int n = 0;
    const select_ns_entry* ns = NULL;
249
    const struct list* pNsList = &properties_from_xmlDocPtr(ctxt->doc)->selectNsList;
250 251 252 253 254 255 256 257 258 259 260 261

    TRACE("(%p)\n", ctxt);

    LIST_FOR_EACH_ENTRY( ns, pNsList, select_ns_entry, entry )
    {
        xmlXPathRegisterNs(ctxt, ns->prefix, ns->href);
        ++n;
    }

    return n;
}

262 263 264 265 266 267 268 269 270 271
static inline void clear_selectNsList(struct list* pNsList)
{
    select_ns_entry *ns, *ns2;
    LIST_FOR_EACH_ENTRY_SAFE( ns, ns2, pNsList, select_ns_entry, entry )
    {
        heap_free( ns );
    }
    list_init(pNsList);
}

272 273 274
static xmldoc_priv * create_priv(void)
{
    xmldoc_priv *priv;
275
    priv = heap_alloc( sizeof (*priv) );
276

277
    if (priv)
278
    {
279
        priv->refs = 0;
280
        list_init( &priv->orphans );
281
        priv->properties = NULL;
282
    }
283 284 285 286

    return priv;
}

287
static domdoc_properties *create_properties(MSXML_VERSION version)
288 289 290
{
    domdoc_properties *properties = heap_alloc(sizeof(domdoc_properties));

291
    list_init(&properties->selectNsList);
292
    properties->preserving = VARIANT_FALSE;
293
    properties->schemaCache = NULL;
294 295 296 297
    properties->selectNsStr = heap_alloc_zero(sizeof(xmlChar));
    properties->selectNsStr_len = 0;

    /* properties that are dependent on object versions */
298 299
    properties->version = version;
    properties->XPath = (version == MSXML4 || version == MSXML6);
300

301 302 303
    /* document url */
    properties->url = NULL;

304 305 306 307 308 309 310 311 312 313 314 315 316
    return properties;
}

static domdoc_properties* copy_properties(domdoc_properties const* properties)
{
    domdoc_properties* pcopy = heap_alloc(sizeof(domdoc_properties));
    select_ns_entry const* ns = NULL;
    select_ns_entry* new_ns = NULL;
    int len = (properties->selectNsStr_len+1)*sizeof(xmlChar);
    ptrdiff_t offset;

    if (pcopy)
    {
317
        pcopy->version = properties->version;
318
        pcopy->preserving = properties->preserving;
319
        pcopy->schemaCache = properties->schemaCache;
320 321
        if (pcopy->schemaCache)
            IXMLDOMSchemaCollection2_AddRef(pcopy->schemaCache);
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
        pcopy->XPath = properties->XPath;
        pcopy->selectNsStr_len = properties->selectNsStr_len;
        list_init( &pcopy->selectNsList );
        pcopy->selectNsStr = heap_alloc(len);
        memcpy((xmlChar*)pcopy->selectNsStr, properties->selectNsStr, len);
        offset = pcopy->selectNsStr - properties->selectNsStr;

        LIST_FOR_EACH_ENTRY( ns, (&properties->selectNsList), select_ns_entry, entry )
        {
            new_ns = heap_alloc(sizeof(select_ns_entry));
            memcpy(new_ns, ns, sizeof(select_ns_entry));
            new_ns->href += offset;
            new_ns->prefix += offset;
            list_add_tail(&pcopy->selectNsList, &new_ns->entry);
        }

338 339 340 341 342 343 344 345 346 347
        if (properties->url)
        {
            int len = strlenW(properties->url);

            pcopy->url = CoTaskMemAlloc((len+1)*sizeof(WCHAR));
            memcpy(pcopy->url, properties->url, len*sizeof(WCHAR));
            pcopy->url[len] = 0;
        }
        else
            pcopy->url = NULL;
348 349 350 351 352 353 354 355 356
    }

    return pcopy;
}

static void free_properties(domdoc_properties* properties)
{
    if (properties)
    {
357 358
        if (properties->schemaCache)
            IXMLDOMSchemaCollection2_Release(properties->schemaCache);
359 360
        clear_selectNsList(&properties->selectNsList);
        heap_free((xmlChar*)properties->selectNsStr);
361
        CoTaskMemFree(properties->url);
362 363 364 365
        heap_free(properties);
    }
}

366 367 368 369 370 371 372 373 374
static void release_namespaces(domdoc *This)
{
    if (This->namespaces)
    {
        IXMLDOMSchemaCollection2_Release(This->namespaces);
        This->namespaces = NULL;
    }
}

375 376 377 378 379 380 381 382 383 384
/* links a "<?xml" node as a first child */
void xmldoc_link_xmldecl(xmlDocPtr doc, xmlNodePtr node)
{
    assert(doc != NULL);
    if (doc->standalone != -1) xmlAddPrevSibling( doc->children, node );
}

/* unlinks a first "<?xml" child if it was created */
xmlNodePtr xmldoc_unlink_xmldecl(xmlDocPtr doc)
{
385 386
    static const xmlChar xmlA[] = "xml";
    xmlNodePtr node, first_child;
387 388 389

    assert(doc != NULL);

390 391 392 393
    /* xml declaration node could be created automatically after parsing or added
       to a tree later */
    first_child = doc->children;
    if (first_child && first_child->type == XML_PI_NODE && xmlStrEqual(first_child->name, xmlA))
394
    {
395
        node = first_child;
396 397 398 399 400 401 402 403
        xmlUnlinkNode( node );
    }
    else
        node = NULL;

    return node;
}

404 405 406 407 408 409 410 411 412 413
BOOL is_preserving_whitespace(xmlNodePtr node)
{
    domdoc_properties* properties = NULL;
    /* during parsing the xmlDoc._private stuff is not there */
    if (priv_from_xmlDocPtr(node->doc))
        properties = properties_from_xmlDocPtr(node->doc);
    return ((properties && properties->preserving == VARIANT_TRUE) ||
            xmlNodeGetSpacePreserve(node) == 1);
}

414 415 416 417 418 419 420 421 422 423 424
static inline BOOL strn_isspace(xmlChar const* str, int len)
{
    for (; str && len > 0 && *str; ++str, --len)
        if (!isspace(*str))
            break;

    return len == 0;
}

static void sax_characters(void *ctx, const xmlChar *ch, int len)
{
425 426
    xmlParserCtxtPtr ctxt;
    const domdoc *This;
427

428 429
    ctxt = (xmlParserCtxtPtr) ctx;
    This = (const domdoc*) ctxt->_private;
430

431 432
    if (ctxt->node)
    {
433 434
        xmlChar cur = *(ctxt->input->cur);

435 436 437
        /* Characters are reported with multiple calls, for example each charref is reported with a separate
           call and then parser appends it to a single text node or creates a new node if not created.
           It's not possible to tell if it's ignorable data or not just looking at data itself cause it could be
438
           space chars that separate charrefs or similar case. We only need to skip leading and trailing spaces,
439 440
           or whole node if it has nothing but space chars, so to detect leading space node->last is checked that
           contains text node pointer if already created, trailing spaces are detected directly looking at parser input
441 442 443
           for next '<' opening bracket - similar logic is used by libxml2 itself. Basically 'cur' == '<' means the last
           chunk of char data, in case it's not the last chunk we check for previously added node type and if it's not
           a text node it's safe to ignore.
444 445

           Note that during domdoc_loadXML() the xmlDocPtr->_private data is not available. */
446

447 448
        if (!This->properties->preserving &&
            !is_preserving_whitespace(ctxt->node) &&
449 450
            strn_isspace(ch, len) &&
            (!ctxt->node->last ||
451 452
            ((ctxt->node->last && (cur == '<' || ctxt->node->last->type != XML_TEXT_NODE))
           )))
453 454 455 456 457 458
        {
            /* Keep information about ignorable whitespace text node in previous or parent node */
            if (ctxt->node->last)
                *(DWORD*)&ctxt->node->last->_private |= NODE_PRIV_TRAILING_IGNORABLE_WS;
            else if (ctxt->node->type != XML_DOCUMENT_NODE)
                *(DWORD*)&ctxt->node->_private |= NODE_PRIV_CHILD_IGNORABLE_WS;
459
            return;
460
        }
461
    }
462

463
    xmlSAX2Characters(ctxt, ch, len);
464 465
}

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
static void LIBXML2_LOG_CALLBACK sax_error(void* ctx, char const* msg, ...)
{
    va_list ap;
    va_start(ap, msg);
    LIBXML2_CALLBACK_ERR(doparse, msg, ap);
    va_end(ap);
}

static void LIBXML2_LOG_CALLBACK sax_warning(void* ctx, char const* msg, ...)
{
    va_list ap;
    va_start(ap, msg);
    LIBXML2_CALLBACK_WARN(doparse, msg, ap);
    va_end(ap);
}

static void sax_serror(void* ctx, xmlErrorPtr err)
{
    LIBXML2_CALLBACK_SERROR(doparse, err);
}

487
static xmlDocPtr doparse(domdoc* This, char const* ptr, int len, xmlCharEncoding encoding)
488
{
489 490
    xmlDocPtr doc = NULL;
    xmlParserCtxtPtr pctx;
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
    static xmlSAXHandler sax_handler = {
        xmlSAX2InternalSubset,          /* internalSubset */
        xmlSAX2IsStandalone,            /* isStandalone */
        xmlSAX2HasInternalSubset,       /* hasInternalSubset */
        xmlSAX2HasExternalSubset,       /* hasExternalSubset */
        xmlSAX2ResolveEntity,           /* resolveEntity */
        xmlSAX2GetEntity,               /* getEntity */
        xmlSAX2EntityDecl,              /* entityDecl */
        xmlSAX2NotationDecl,            /* notationDecl */
        xmlSAX2AttributeDecl,           /* attributeDecl */
        xmlSAX2ElementDecl,             /* elementDecl */
        xmlSAX2UnparsedEntityDecl,      /* unparsedEntityDecl */
        xmlSAX2SetDocumentLocator,      /* setDocumentLocator */
        xmlSAX2StartDocument,           /* startDocument */
        xmlSAX2EndDocument,             /* endDocument */
        xmlSAX2StartElement,            /* startElement */
        xmlSAX2EndElement,              /* endElement */
        xmlSAX2Reference,               /* reference */
509 510
        sax_characters,                 /* characters */
        sax_characters,                 /* ignorableWhitespace */
511 512
        xmlSAX2ProcessingInstruction,   /* processingInstruction */
        xmlSAX2Comment,                 /* comment */
513 514 515
        sax_warning,                    /* warning */
        sax_error,                      /* error */
        sax_error,                      /* fatalError */
516 517 518 519 520 521 522
        xmlSAX2GetParameterEntity,      /* getParameterEntity */
        xmlSAX2CDataBlock,              /* cdataBlock */
        xmlSAX2ExternalSubset,          /* externalSubset */
        0,                              /* initialized */
        NULL,                           /* _private */
        xmlSAX2StartElementNs,          /* startElementNs */
        xmlSAX2EndElementNs,            /* endElementNs */
523
        sax_serror                      /* serror */
524
    };
525 526 527 528 529 530 531 532

    pctx = xmlCreateMemoryParserCtxt(ptr, len);
    if (!pctx)
    {
        ERR("Failed to create parser context\n");
        return NULL;
    }

533
    if (pctx->sax) xmlFree(pctx->sax);
534
    pctx->sax = &sax_handler;
535
    pctx->_private = This;
536
    pctx->recovery = 0;
537 538 539 540

    if (encoding != XML_CHAR_ENCODING_NONE)
        xmlSwitchEncoding(pctx, encoding);

541
    xmlParseDocument(pctx);
542

543 544 545 546 547 548 549 550 551 552 553
    if (pctx->wellFormed)
    {
        doc = pctx->myDoc;
    }
    else
    {
       xmlFreeDoc(pctx->myDoc);
       pctx->myDoc = NULL;
    }
    pctx->sax = NULL;
    xmlFreeParserCtxt(pctx);
554 555

    /* TODO: put this in one of the SAX callbacks */
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
    /* create first child as a <?xml...?> */
    if (doc && doc->standalone != -1)
    {
        xmlNodePtr node;
        char buff[30];
        xmlChar *xmlbuff = (xmlChar*)buff;

        node = xmlNewDocPI( doc, (xmlChar*)"xml", NULL );

        /* version attribute can't be omitted */
        sprintf(buff, "version=\"%s\"", doc->version ? (char*)doc->version : "1.0");
        xmlNodeAddContent( node, xmlbuff );

        if (doc->encoding)
        {
            sprintf(buff, " encoding=\"%s\"", doc->encoding);
            xmlNodeAddContent( node, xmlbuff );
        }

        if (doc->standalone != -2)
        {
            sprintf(buff, " standalone=\"%s\"", doc->standalone == 0 ? "no" : "yes");
            xmlNodeAddContent( node, xmlbuff );
        }

        xmldoc_link_xmldecl( doc, node );
    }

    return doc;
585 586
}

587
void xmldoc_init(xmlDocPtr doc, MSXML_VERSION version)
588 589
{
    doc->_private = create_priv();
590
    priv_from_xmlDocPtr(doc)->properties = create_properties(version);
591 592
}

593
LONG xmldoc_add_refs(xmlDocPtr doc, LONG refs)
Huw Davies's avatar
Huw Davies committed
594
{
595
    LONG ref = InterlockedExchangeAdd(&priv_from_xmlDocPtr(doc)->refs, refs) + refs;
596
    TRACE("(%p)->(%d)\n", doc, ref);
Huw Davies's avatar
Huw Davies committed
597 598 599
    return ref;
}

600 601 602 603 604 605
LONG xmldoc_add_ref(xmlDocPtr doc)
{
    return xmldoc_add_refs(doc, 1);
}

LONG xmldoc_release_refs(xmlDocPtr doc, LONG refs)
Huw Davies's avatar
Huw Davies committed
606
{
607
    xmldoc_priv *priv = priv_from_xmlDocPtr(doc);
608
    LONG ref = InterlockedExchangeAdd(&priv->refs, -refs) - refs;
609
    TRACE("(%p)->(%d)\n", doc, ref);
610 611 612 613 614

    if (ref < 0)
        WARN("negative refcount, expect troubles\n");

    if (ref == 0)
Huw Davies's avatar
Huw Davies committed
615
    {
616
        orphan_entry *orphan, *orphan2;
Huw Davies's avatar
Huw Davies committed
617
        TRACE("freeing docptr %p\n", doc);
618 619 620 621

        LIST_FOR_EACH_ENTRY_SAFE( orphan, orphan2, &priv->orphans, orphan_entry, entry )
        {
            xmlFreeNode( orphan->node );
622
            heap_free( orphan );
623
        }
624
        free_properties(priv->properties);
625
        heap_free(doc->_private);
626

Huw Davies's avatar
Huw Davies committed
627 628 629 630 631 632
        xmlFreeDoc(doc);
    }

    return ref;
}

633 634 635 636 637
LONG xmldoc_release(xmlDocPtr doc)
{
    return xmldoc_release_refs(doc, 1);
}

638 639 640 641 642
HRESULT xmldoc_add_orphan(xmlDocPtr doc, xmlNodePtr node)
{
    xmldoc_priv *priv = priv_from_xmlDocPtr(doc);
    orphan_entry *entry;

643
    entry = heap_alloc( sizeof (*entry) );
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
    if(!entry)
        return E_OUTOFMEMORY;

    entry->node = node;
    list_add_head( &priv->orphans, &entry->entry );
    return S_OK;
}

HRESULT xmldoc_remove_orphan(xmlDocPtr doc, xmlNodePtr node)
{
    xmldoc_priv *priv = priv_from_xmlDocPtr(doc);
    orphan_entry *entry, *entry2;

    LIST_FOR_EACH_ENTRY_SAFE( entry, entry2, &priv->orphans, orphan_entry, entry )
    {
        if( entry->node == node )
        {
            list_remove( &entry->entry );
662
            heap_free( entry );
663 664 665 666 667 668 669
            return S_OK;
        }
    }

    return S_FALSE;
}

670
static inline xmlDocPtr get_doc( domdoc *This )
671
{
672
    return This->node.node->doc;
673
}
674

675 676
static HRESULT attach_xmldoc(domdoc *This, xmlDocPtr xml )
{
677 678
    release_namespaces(This);

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
    if(This->node.node)
    {
        priv_from_xmlDocPtr(get_doc(This))->properties = NULL;
        if (xmldoc_release(get_doc(This)) != 0)
            priv_from_xmlDocPtr(get_doc(This))->properties =
                copy_properties(This->properties);
    }

    This->node.node = (xmlNodePtr) xml;

    if(This->node.node)
    {
        xmldoc_add_ref(get_doc(This));
        priv_from_xmlDocPtr(get_doc(This))->properties = This->properties;
    }
694

695
    return S_OK;
696 697
}

698
static inline domdoc *impl_from_IXMLDOMDocument3( IXMLDOMDocument3 *iface )
699
{
700
    return CONTAINING_RECORD(iface, domdoc, IXMLDOMDocument3_iface);
701 702
}

703
static inline domdoc *impl_from_IPersistStreamInit(IPersistStreamInit *iface)
704
{
705
    return CONTAINING_RECORD(iface, domdoc, IPersistStreamInit_iface);
706 707
}

708 709
static inline domdoc *impl_from_IObjectWithSite(IObjectWithSite *iface)
{
710
    return CONTAINING_RECORD(iface, domdoc, IObjectWithSite_iface);
711 712
}

713 714
static inline domdoc *impl_from_IObjectSafety(IObjectSafety *iface)
{
715
    return CONTAINING_RECORD(iface, domdoc, IObjectSafety_iface);
716 717
}

718 719
static inline domdoc *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface)
{
720
    return CONTAINING_RECORD(iface, domdoc, IConnectionPointContainer_iface);
721 722
}

723
/************************************************************************
724
 * domdoc implementation of IPersistStream.
725
 */
726
static HRESULT WINAPI PersistStreamInit_QueryInterface(
727
    IPersistStreamInit *iface, REFIID riid, void **ppvObj)
728
{
729
    domdoc* This = impl_from_IPersistStreamInit(iface);
730
    return IXMLDOMDocument3_QueryInterface(&This->IXMLDOMDocument3_iface, riid, ppvObj);
731 732
}

733
static ULONG WINAPI PersistStreamInit_AddRef(
734
    IPersistStreamInit *iface)
735
{
736
    domdoc* This = impl_from_IPersistStreamInit(iface);
737
    return IXMLDOMDocument3_AddRef(&This->IXMLDOMDocument3_iface);
738 739
}

740
static ULONG WINAPI PersistStreamInit_Release(
741
    IPersistStreamInit *iface)
742
{
743
    domdoc* This = impl_from_IPersistStreamInit(iface);
744
    return IXMLDOMDocument3_Release(&This->IXMLDOMDocument3_iface);
745 746
}

747
static HRESULT WINAPI PersistStreamInit_GetClassID(
748
    IPersistStreamInit *iface, CLSID *classid)
749
{
750 751
    domdoc* This = impl_from_IPersistStreamInit(iface);
    TRACE("(%p)->(%p)\n", This, classid);
752 753 754 755

    if(!classid)
        return E_POINTER;

756
    *classid = *DOMDocument_version(This->properties->version);
757 758

    return S_OK;
759 760
}

761
static HRESULT WINAPI PersistStreamInit_IsDirty(
762
    IPersistStreamInit *iface)
763
{
764
    domdoc *This = impl_from_IPersistStreamInit(iface);
765
    FIXME("(%p): stub!\n", This);
766 767 768
    return S_FALSE;
}

769
static HRESULT domdoc_load_from_stream(domdoc *doc, ISequentialStream *stream)
770 771
{
    DWORD read, written, len;
772
    xmlDocPtr xmldoc = NULL;
773
    IStream *hstream;
774
    HGLOBAL hglobal;
775
    BYTE buf[4096];
776
    HRESULT hr;
777 778
    char *ptr;

779 780
    hstream = NULL;
    hr = CreateStreamOnHGlobal(NULL, TRUE, &hstream);
781 782 783 784 785
    if (FAILED(hr))
        return hr;

    do
    {
786
        ISequentialStream_Read(stream, buf, sizeof(buf), &read);
787
        hr = IStream_Write(hstream, buf, read, &written);
788 789 790 791
    } while(SUCCEEDED(hr) && written != 0 && read != 0);

    if (FAILED(hr))
    {
792
        ERR("failed to copy stream 0x%08x\n", hr);
793
        IStream_Release(hstream);
794 795 796
        return hr;
    }

797
    hr = GetHGlobalFromStream(hstream, &hglobal);
798 799 800 801 802
    if (FAILED(hr))
        return hr;

    len = GlobalSize(hglobal);
    ptr = GlobalLock(hglobal);
803 804
    if (len)
        xmldoc = doparse(doc, ptr, len, XML_CHAR_ENCODING_NONE);
805 806 807 808 809 810 811 812
    GlobalUnlock(hglobal);

    if (!xmldoc)
    {
        ERR("Failed to parse xml\n");
        return E_FAIL;
    }

813
    xmldoc->_private = create_priv();
814

815 816 817 818 819 820 821 822 823 824 825 826 827
    return attach_xmldoc(doc, xmldoc);
}

static HRESULT WINAPI PersistStreamInit_Load(IPersistStreamInit *iface, IStream *stream)
{
    domdoc *This = impl_from_IPersistStreamInit(iface);

    TRACE("(%p)->(%p)\n", This, stream);

    if (!stream)
        return E_INVALIDARG;

    return domdoc_load_from_stream(This, (ISequentialStream*)stream);
828 829
}

830
static HRESULT WINAPI PersistStreamInit_Save(
831
    IPersistStreamInit *iface, IStream *stream, BOOL clr_dirty)
832
{
833
    domdoc *This = impl_from_IPersistStreamInit(iface);
834
    BSTR xmlString;
835
    HRESULT hr;
836

837
    TRACE("(%p)->(%p %d)\n", This, stream, clr_dirty);
838

839
    hr = IXMLDOMDocument3_get_xml(&This->IXMLDOMDocument3_iface, &xmlString);
840 841
    if(hr == S_OK)
    {
842
        DWORD len = SysStringLen(xmlString) * sizeof(WCHAR);
843

844
        hr = IStream_Write( stream, xmlString, len, NULL );
845 846 847 848 849 850
        SysFreeString(xmlString);
    }

    TRACE("ret 0x%08x\n", hr);

    return hr;
851 852
}

853
static HRESULT WINAPI PersistStreamInit_GetSizeMax(
854
    IPersistStreamInit *iface, ULARGE_INTEGER *pcbSize)
855
{
856
    domdoc *This = impl_from_IPersistStreamInit(iface);
857
    TRACE("(%p)->(%p)\n", This, pcbSize);
858 859 860
    return E_NOTIMPL;
}

861
static HRESULT WINAPI PersistStreamInit_InitNew(
862
    IPersistStreamInit *iface)
863
{
864 865 866 867 868 869 870
    domdoc *This = impl_from_IPersistStreamInit(iface);
    TRACE("(%p)\n", This);
    return S_OK;
}

static const IPersistStreamInitVtbl xmldoc_IPersistStreamInit_VTable =
{
871 872 873 874 875 876 877 878 879
    PersistStreamInit_QueryInterface,
    PersistStreamInit_AddRef,
    PersistStreamInit_Release,
    PersistStreamInit_GetClassID,
    PersistStreamInit_IsDirty,
    PersistStreamInit_Load,
    PersistStreamInit_Save,
    PersistStreamInit_GetSizeMax,
    PersistStreamInit_InitNew
880 881
};

882
/* IXMLDOMDocument3 interface */
883

884 885 886 887
static const tid_t domdoc_se_tids[] = {
    IXMLDOMNode_tid,
    IXMLDOMDocument_tid,
    IXMLDOMDocument2_tid,
888
    IXMLDOMDocument3_tid,
889
    NULL_tid
890 891
};

892
static HRESULT WINAPI domdoc_QueryInterface( IXMLDOMDocument3 *iface, REFIID riid, void** ppvObject )
893
{
894
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
895

896
    TRACE("(%p)->(%s %p)\n", This, debugstr_guid( riid ), ppvObject );
897

898 899
    *ppvObject = NULL;

900
    if ( IsEqualGUID( riid, &IID_IUnknown ) ||
901
         IsEqualGUID( riid, &IID_IDispatch ) ||
902
         IsEqualGUID( riid, &IID_IXMLDOMNode ) ||
903
         IsEqualGUID( riid, &IID_IXMLDOMDocument ) ||
904 905
         IsEqualGUID( riid, &IID_IXMLDOMDocument2 )||
         IsEqualGUID( riid, &IID_IXMLDOMDocument3 ))
906 907 908
    {
        *ppvObject = iface;
    }
909 910
    else if (IsEqualGUID(&IID_IPersistStream, riid) ||
             IsEqualGUID(&IID_IPersistStreamInit, riid))
911
    {
912
        *ppvObject = &This->IPersistStreamInit_iface;
913
    }
914 915
    else if (IsEqualGUID(&IID_IObjectWithSite, riid))
    {
916
        *ppvObject = &This->IObjectWithSite_iface;
917
    }
918 919
    else if (IsEqualGUID(&IID_IObjectSafety, riid))
    {
920
        *ppvObject = &This->IObjectSafety_iface;
921
    }
922 923
    else if( IsEqualGUID( riid, &IID_ISupportErrorInfo ))
    {
924
        return node_create_supporterrorinfo(domdoc_se_tids, ppvObject);
925
    }
926
    else if(node_query_interface(&This->node, riid, ppvObject))
927 928 929
    {
        return *ppvObject ? S_OK : E_NOINTERFACE;
    }
930 931
    else if (IsEqualGUID( riid, &IID_IConnectionPointContainer ))
    {
932
        *ppvObject = &This->IConnectionPointContainer_iface;
933
    }
934
    else
935
    {
936
        TRACE("interface %s not implemented\n", debugstr_guid(riid));
937
        return E_NOINTERFACE;
938
    }
939

940
    IUnknown_AddRef((IUnknown*)*ppvObject);
941 942 943 944

    return S_OK;
}

945
static ULONG WINAPI domdoc_AddRef( IXMLDOMDocument3 *iface )
946
{
947
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
948 949 950
    ULONG ref = InterlockedIncrement( &This->ref );
    TRACE("(%p)->(%d)\n", This, ref );
    return ref;
951 952
}

953
static ULONG WINAPI domdoc_Release( IXMLDOMDocument3 *iface )
954
{
955
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
956
    LONG ref = InterlockedDecrement( &This->ref );
957

958
    TRACE("(%p)->(%d)\n", This, ref );
959 960 961

    if ( ref == 0 )
    {
962 963
        int eid;

964 965
        if (This->site)
            IUnknown_Release( This->site );
966
        destroy_xmlnode(&This->node);
967 968 969 970

        for (eid = 0; eid < EVENTID_LAST; eid++)
            if (This->events[eid]) IDispatch_Release(This->events[eid]);

971
        release_namespaces(This);
972
        heap_free(This);
973 974 975 976 977
    }

    return ref;
}

978
static HRESULT WINAPI domdoc_GetTypeInfoCount( IXMLDOMDocument3 *iface, UINT* pctinfo )
979
{
980
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
981
    return IDispatchEx_GetTypeInfoCount(&This->node.dispex.IDispatchEx_iface, pctinfo);
982 983 984
}

static HRESULT WINAPI domdoc_GetTypeInfo(
985
    IXMLDOMDocument3 *iface,
986 987
    UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo )
{
988
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
989
    return IDispatchEx_GetTypeInfo(&This->node.dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
990 991 992
}

static HRESULT WINAPI domdoc_GetIDsOfNames(
993
    IXMLDOMDocument3 *iface,
994 995 996 997 998 999
    REFIID riid,
    LPOLESTR* rgszNames,
    UINT cNames,
    LCID lcid,
    DISPID* rgDispId)
{
1000
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1001 1002
    return IDispatchEx_GetIDsOfNames(&This->node.dispex.IDispatchEx_iface,
        riid, rgszNames, cNames, lcid, rgDispId);
1003 1004 1005
}

static HRESULT WINAPI domdoc_Invoke(
1006
    IXMLDOMDocument3 *iface,
1007 1008 1009 1010 1011 1012 1013 1014 1015
    DISPID dispIdMember,
    REFIID riid,
    LCID lcid,
    WORD wFlags,
    DISPPARAMS* pDispParams,
    VARIANT* pVarResult,
    EXCEPINFO* pExcepInfo,
    UINT* puArgErr)
{
1016
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1017 1018
    return IDispatchEx_Invoke(&This->node.dispex.IDispatchEx_iface,
        dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1019 1020 1021
}

static HRESULT WINAPI domdoc_get_nodeName(
1022
    IXMLDOMDocument3 *iface,
1023 1024
    BSTR* name )
{
1025
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1026 1027 1028 1029 1030 1031

    static const WCHAR documentW[] = {'#','d','o','c','u','m','e','n','t',0};

    TRACE("(%p)->(%p)\n", This, name);

    return return_bstr(documentW, name);
1032 1033 1034 1035
}


static HRESULT WINAPI domdoc_get_nodeValue(
1036
    IXMLDOMDocument3 *iface,
1037 1038
    VARIANT* value )
{
1039
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1040 1041 1042 1043 1044 1045 1046 1047 1048

    TRACE("(%p)->(%p)\n", This, value);

    if(!value)
        return E_INVALIDARG;

    V_VT(value) = VT_NULL;
    V_BSTR(value) = NULL; /* tests show that we should do this */
    return S_FALSE;
1049 1050 1051 1052
}


static HRESULT WINAPI domdoc_put_nodeValue(
1053
    IXMLDOMDocument3 *iface,
1054 1055
    VARIANT value)
{
1056
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1057
    TRACE("(%p)->(%s)\n", This, debugstr_variant(&value));
1058
    return E_FAIL;
1059 1060 1061 1062
}


static HRESULT WINAPI domdoc_get_nodeType(
1063
    IXMLDOMDocument3 *iface,
1064 1065
    DOMNodeType* type )
{
1066
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1067 1068 1069 1070 1071

    TRACE("(%p)->(%p)\n", This, type);

    *type = NODE_DOCUMENT;
    return S_OK;
1072 1073 1074 1075
}


static HRESULT WINAPI domdoc_get_parentNode(
1076
    IXMLDOMDocument3 *iface,
1077 1078
    IXMLDOMNode** parent )
{
1079
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1080 1081 1082 1083

    TRACE("(%p)->(%p)\n", This, parent);

    return node_get_parent(&This->node, parent);
1084 1085 1086 1087
}


static HRESULT WINAPI domdoc_get_childNodes(
1088
    IXMLDOMDocument3 *iface,
1089 1090
    IXMLDOMNodeList** childList )
{
1091
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1092 1093 1094 1095

    TRACE("(%p)->(%p)\n", This, childList);

    return node_get_child_nodes(&This->node, childList);
1096 1097 1098 1099
}


static HRESULT WINAPI domdoc_get_firstChild(
1100
    IXMLDOMDocument3 *iface,
1101 1102
    IXMLDOMNode** firstChild )
{
1103
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1104 1105 1106 1107

    TRACE("(%p)->(%p)\n", This, firstChild);

    return node_get_first_child(&This->node, firstChild);
1108 1109 1110 1111
}


static HRESULT WINAPI domdoc_get_lastChild(
1112
    IXMLDOMDocument3 *iface,
1113 1114
    IXMLDOMNode** lastChild )
{
1115
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1116 1117 1118

    TRACE("(%p)->(%p)\n", This, lastChild);

1119
    return node_get_last_child(&This->node, lastChild);
1120 1121 1122 1123
}


static HRESULT WINAPI domdoc_get_previousSibling(
1124
    IXMLDOMDocument3 *iface,
1125 1126
    IXMLDOMNode** previousSibling )
{
1127
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1128 1129 1130 1131

    TRACE("(%p)->(%p)\n", This, previousSibling);

    return return_null_node(previousSibling);
1132 1133 1134 1135
}


static HRESULT WINAPI domdoc_get_nextSibling(
1136
    IXMLDOMDocument3 *iface,
1137 1138
    IXMLDOMNode** nextSibling )
{
1139
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1140 1141 1142 1143

    TRACE("(%p)->(%p)\n", This, nextSibling);

    return return_null_node(nextSibling);
1144 1145 1146 1147
}


static HRESULT WINAPI domdoc_get_attributes(
1148
    IXMLDOMDocument3 *iface,
1149 1150
    IXMLDOMNamedNodeMap** attributeMap )
{
1151
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1152 1153 1154 1155

    TRACE("(%p)->(%p)\n", This, attributeMap);

    return return_null_ptr((void**)attributeMap);
1156 1157 1158 1159
}


static HRESULT WINAPI domdoc_insertBefore(
1160
    IXMLDOMDocument3 *iface,
1161 1162 1163 1164
    IXMLDOMNode* newChild,
    VARIANT refChild,
    IXMLDOMNode** outNewChild )
{
1165
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1166 1167
    DOMNodeType type;
    HRESULT hr;
1168

1169
    TRACE("(%p)->(%p %s %p)\n", This, newChild, debugstr_variant(&refChild), outNewChild);
1170

1171 1172
    hr = IXMLDOMNode_get_nodeType(newChild, &type);
    if (hr != S_OK) return hr;
1173

1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
    TRACE("new node type %d\n", type);
    switch (type)
    {
        case NODE_ATTRIBUTE:
        case NODE_DOCUMENT:
        case NODE_CDATA_SECTION:
            if (outNewChild) *outNewChild = NULL;
            return E_FAIL;
        default:
            return node_insert_before(&This->node, newChild, &refChild, outNewChild);
    }
}
1186 1187

static HRESULT WINAPI domdoc_replaceChild(
1188
    IXMLDOMDocument3 *iface,
1189 1190 1191 1192
    IXMLDOMNode* newChild,
    IXMLDOMNode* oldChild,
    IXMLDOMNode** outOldChild)
{
1193
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1194 1195 1196 1197

    TRACE("(%p)->(%p %p %p)\n", This, newChild, oldChild, outOldChild);

    return node_replace_child(&This->node, newChild, oldChild, outOldChild);
1198 1199 1200 1201
}


static HRESULT WINAPI domdoc_removeChild(
1202
    IXMLDOMDocument3 *iface,
1203 1204
    IXMLDOMNode  *child,
    IXMLDOMNode **oldChild)
1205
{
1206
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1207 1208
    TRACE("(%p)->(%p %p)\n", This, child, oldChild);
    return node_remove_child(&This->node, child, oldChild);
1209 1210 1211 1212
}


static HRESULT WINAPI domdoc_appendChild(
1213
    IXMLDOMDocument3 *iface,
1214 1215
    IXMLDOMNode  *child,
    IXMLDOMNode **outChild)
1216
{
1217
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1218 1219
    TRACE("(%p)->(%p %p)\n", This, child, outChild);
    return node_append_child(&This->node, child, outChild);
1220 1221 1222 1223
}


static HRESULT WINAPI domdoc_hasChildNodes(
1224
    IXMLDOMDocument3 *iface,
1225
    VARIANT_BOOL *ret)
1226
{
1227
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1228 1229
    TRACE("(%p)->(%p)\n", This, ret);
    return node_has_childnodes(&This->node, ret);
1230 1231 1232 1233
}


static HRESULT WINAPI domdoc_get_ownerDocument(
1234
    IXMLDOMDocument3 *iface,
1235
    IXMLDOMDocument **doc)
1236
{
1237
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1238 1239
    TRACE("(%p)->(%p)\n", This, doc);
    return node_get_owner_doc(&This->node, doc);
1240 1241 1242 1243
}


static HRESULT WINAPI domdoc_cloneNode(
1244
    IXMLDOMDocument3 *iface,
1245
    VARIANT_BOOL deep,
1246
    IXMLDOMNode** outNode)
1247
{
1248
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1249 1250
    xmlNodePtr clone;

1251
    TRACE("(%p)->(%d %p)\n", This, deep, outNode);
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273

    if (!outNode)
        return E_INVALIDARG;

    *outNode = NULL;

    clone = xmlCopyNode((xmlNodePtr)get_doc(This), deep ? 1 : 2);
    if (!clone)
        return E_FAIL;

    clone->doc->_private = create_priv();
    xmldoc_add_orphan(clone->doc, clone);
    xmldoc_add_ref(clone->doc);

    priv_from_xmlDocPtr(clone->doc)->properties = copy_properties(This->properties);
    if (!(*outNode = (IXMLDOMNode*)create_domdoc(clone)))
    {
        xmldoc_release(clone->doc);
        return E_FAIL;
    }

    return S_OK;
1274 1275 1276 1277
}


static HRESULT WINAPI domdoc_get_nodeTypeString(
1278
    IXMLDOMDocument3 *iface,
1279
    BSTR *p)
1280
{
1281
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1282 1283 1284 1285 1286
    static const WCHAR documentW[] = {'d','o','c','u','m','e','n','t',0};

    TRACE("(%p)->(%p)\n", This, p);

    return return_bstr(documentW, p);
1287 1288 1289 1290
}


static HRESULT WINAPI domdoc_get_text(
1291
    IXMLDOMDocument3 *iface,
1292
    BSTR *p)
1293
{
1294
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1295 1296
    TRACE("(%p)->(%p)\n", This, p);
    return node_get_text(&This->node, p);
1297 1298 1299 1300
}


static HRESULT WINAPI domdoc_put_text(
1301
    IXMLDOMDocument3 *iface,
1302 1303
    BSTR text )
{
1304
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1305 1306
    TRACE("(%p)->(%s)\n", This, debugstr_w(text));
    return E_FAIL;
1307 1308 1309 1310
}


static HRESULT WINAPI domdoc_get_specified(
1311
    IXMLDOMDocument3 *iface,
1312 1313
    VARIANT_BOOL* isSpecified )
{
1314
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1315 1316 1317
    FIXME("(%p)->(%p) stub!\n", This, isSpecified);
    *isSpecified = VARIANT_TRUE;
    return S_OK;
1318 1319 1320 1321
}


static HRESULT WINAPI domdoc_get_definition(
1322
    IXMLDOMDocument3 *iface,
1323 1324
    IXMLDOMNode** definitionNode )
{
1325
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1326 1327
    FIXME("(%p)->(%p)\n", This, definitionNode);
    return E_NOTIMPL;
1328 1329 1330 1331
}


static HRESULT WINAPI domdoc_get_nodeTypedValue(
1332
    IXMLDOMDocument3 *iface,
1333
    VARIANT* v )
1334
{
1335
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1336 1337
    TRACE("(%p)->(%p)\n", This, v);
    return return_null_var(v);
1338 1339 1340
}

static HRESULT WINAPI domdoc_put_nodeTypedValue(
1341
    IXMLDOMDocument3 *iface,
1342 1343
    VARIANT typedValue )
{
1344
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1345 1346
    FIXME("(%p)->(%s)\n", This, debugstr_variant(&typedValue));
    return E_NOTIMPL;
1347 1348 1349 1350
}


static HRESULT WINAPI domdoc_get_dataType(
1351
    IXMLDOMDocument3 *iface,
1352
    VARIANT* typename )
1353
{
1354
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1355 1356
    TRACE("(%p)->(%p)\n", This, typename);
    return return_null_var( typename );
1357 1358 1359 1360
}


static HRESULT WINAPI domdoc_put_dataType(
1361
    IXMLDOMDocument3 *iface,
1362 1363
    BSTR dataTypeName )
{
1364
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1365 1366 1367 1368 1369 1370 1371

    FIXME("(%p)->(%s)\n", This, debugstr_w(dataTypeName));

    if(!dataTypeName)
        return E_INVALIDARG;

    return E_FAIL;
1372 1373
}

1374 1375 1376 1377
static int XMLCALL domdoc_get_xml_writecallback(void *ctx, const char *data, int len)
{
    return xmlBufferAdd((xmlBufferPtr)ctx, (xmlChar*)data, len) == 0 ? len : 0;
}
1378 1379

static HRESULT WINAPI domdoc_get_xml(
1380
    IXMLDOMDocument3 *iface,
1381
    BSTR* p)
1382
{
1383
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1384 1385 1386 1387
    xmlSaveCtxtPtr ctxt;
    xmlBufferPtr buf;
    int options;
    long ret;
1388 1389 1390

    TRACE("(%p)->(%p)\n", This, p);

1391 1392 1393 1394 1395 1396 1397 1398 1399
    if(!p)
        return E_INVALIDARG;

    *p = NULL;

    buf = xmlBufferCreate();
    if(!buf)
        return E_OUTOFMEMORY;

1400
    options = XML_SAVE_FORMAT | XML_SAVE_NO_DECL;
1401 1402
    ctxt = xmlSaveToIO(domdoc_get_xml_writecallback, NULL, buf, "UTF-8", options);

1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
    if(!ctxt)
    {
        xmlBufferFree(buf);
        return E_OUTOFMEMORY;
    }

    ret = xmlSaveDoc(ctxt, get_doc(This));
    /* flushes on close */
    xmlSaveClose(ctxt);

    TRACE("%ld, len=%d\n", ret, xmlBufferLength(buf));
    if(ret != -1 && xmlBufferLength(buf) > 0)
    {
        BSTR content;

        content = bstr_from_xmlChar(xmlBufferContent(buf));
        content = EnsureCorrectEOL(content);

        *p = content;
    }
    else
    {
        *p = SysAllocStringLen(NULL, 0);
    }

    xmlBufferFree(buf);

    return *p ? S_OK : E_OUTOFMEMORY;
1431 1432 1433 1434
}


static HRESULT WINAPI domdoc_transformNode(
1435
    IXMLDOMDocument3 *iface,
1436 1437
    IXMLDOMNode *node,
    BSTR *p)
1438
{
1439
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1440 1441
    TRACE("(%p)->(%p %p)\n", This, node, p);
    return node_transform_node(&This->node, node, p);
1442 1443 1444 1445
}


static HRESULT WINAPI domdoc_selectNodes(
1446
    IXMLDOMDocument3 *iface,
1447 1448
    BSTR p,
    IXMLDOMNodeList **outList)
1449
{
1450
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1451 1452
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(p), outList);
    return node_select_nodes(&This->node, p, outList);
1453 1454 1455 1456
}


static HRESULT WINAPI domdoc_selectSingleNode(
1457
    IXMLDOMDocument3 *iface,
1458 1459
    BSTR p,
    IXMLDOMNode **outNode)
1460
{
1461
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1462 1463
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(p), outNode);
    return node_select_singlenode(&This->node, p, outNode);
1464 1465 1466 1467
}


static HRESULT WINAPI domdoc_get_parsed(
1468
    IXMLDOMDocument3 *iface,
1469 1470
    VARIANT_BOOL* isParsed )
{
1471
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1472 1473 1474
    FIXME("(%p)->(%p) stub!\n", This, isParsed);
    *isParsed = VARIANT_TRUE;
    return S_OK;
1475 1476 1477
}

static HRESULT WINAPI domdoc_get_namespaceURI(
1478
    IXMLDOMDocument3 *iface,
1479 1480
    BSTR* namespaceURI )
{
1481
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1482
    TRACE("(%p)->(%p)\n", This, namespaceURI);
1483
    return return_null_bstr( namespaceURI );
1484 1485 1486
}

static HRESULT WINAPI domdoc_get_prefix(
1487
    IXMLDOMDocument3 *iface,
1488
    BSTR* prefix )
1489
{
1490
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1491 1492
    TRACE("(%p)->(%p)\n", This, prefix);
    return return_null_bstr( prefix );
1493 1494 1495 1496
}


static HRESULT WINAPI domdoc_get_baseName(
1497
    IXMLDOMDocument3 *iface,
1498
    BSTR* name )
1499
{
1500
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1501 1502
    TRACE("(%p)->(%p)\n", This, name);
    return return_null_bstr( name );
1503 1504 1505 1506
}


static HRESULT WINAPI domdoc_transformNodeToObject(
1507
    IXMLDOMDocument3 *iface,
1508 1509 1510
    IXMLDOMNode* stylesheet,
    VARIANT outputObject)
{
1511
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1512 1513
    FIXME("(%p)->(%p %s)\n", This, stylesheet, debugstr_variant(&outputObject));
    return E_NOTIMPL;
1514 1515 1516 1517
}


static HRESULT WINAPI domdoc_get_doctype(
1518
    IXMLDOMDocument3 *iface,
1519
    IXMLDOMDocumentType** doctype )
1520
{
1521
    domdoc *This = impl_from_IXMLDOMDocument3(iface);
1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541
    IXMLDOMNode *node;
    xmlDtdPtr dtd;
    HRESULT hr;

    TRACE("(%p)->(%p)\n", This, doctype);

    if (!doctype) return E_INVALIDARG;

    *doctype = NULL;

    dtd = xmlGetIntSubset(get_doc(This));
    if (!dtd) return S_FALSE;

    node = create_node((xmlNodePtr)dtd);
    if (!node) return S_FALSE;

    hr = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMDocumentType, (void**)doctype);
    IXMLDOMNode_Release(node);

    return hr;
1542 1543 1544 1545
}


static HRESULT WINAPI domdoc_get_implementation(
1546
    IXMLDOMDocument3 *iface,
1547 1548
    IXMLDOMImplementation** impl )
{
1549
    domdoc *This = impl_from_IXMLDOMDocument3(iface);
1550 1551 1552

    TRACE("(%p)->(%p)\n", This, impl);

1553 1554 1555 1556 1557 1558
    if(!impl)
        return E_INVALIDARG;

    *impl = (IXMLDOMImplementation*)create_doc_Implementation();

    return S_OK;
1559 1560 1561
}

static HRESULT WINAPI domdoc_get_documentElement(
1562
    IXMLDOMDocument3 *iface,
1563 1564
    IXMLDOMElement** DOMElement )
{
1565
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1566
    IXMLDOMNode *element_node;
1567
    xmlNodePtr root;
1568
    HRESULT hr;
1569

1570
    TRACE("(%p)->(%p)\n", This, DOMElement);
1571 1572 1573

    if(!DOMElement)
        return E_INVALIDARG;
1574

1575
    *DOMElement = NULL;
1576

1577
    root = xmlDocGetRootElement( get_doc(This) );
1578 1579 1580
    if ( !root )
        return S_FALSE;

1581 1582 1583
    element_node = create_node( root );
    if(!element_node) return S_FALSE;

1584
    hr = IXMLDOMNode_QueryInterface(element_node, &IID_IXMLDOMElement, (void**)DOMElement);
1585 1586 1587
    IXMLDOMNode_Release(element_node);

    return hr;
1588 1589 1590
}


1591
static HRESULT WINAPI domdoc_put_documentElement(
1592
    IXMLDOMDocument3 *iface,
1593 1594
    IXMLDOMElement* DOMElement )
{
1595
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1596
    IXMLDOMNode *elementNode;
1597
    xmlNodePtr oldRoot;
1598
    xmlDocPtr old_doc;
1599
    xmlnode *xmlNode;
1600
    int refcount = 0;
1601 1602 1603 1604 1605 1606 1607 1608
    HRESULT hr;

    TRACE("(%p)->(%p)\n", This, DOMElement);

    hr = IXMLDOMElement_QueryInterface( DOMElement, &IID_IXMLDOMNode, (void**)&elementNode );
    if(FAILED(hr))
        return hr;

1609
    xmlNode = get_node_obj( elementNode );
1610
    if(!xmlNode) return E_FAIL;
1611 1612 1613 1614 1615

    if(!xmlNode->node->parent)
        if(xmldoc_remove_orphan(xmlNode->node->doc, xmlNode->node) != S_OK)
            WARN("%p is not an orphan of %p\n", xmlNode->node->doc, xmlNode->node);

1616 1617 1618 1619 1620 1621
    old_doc = xmlNode->node->doc;
    if (old_doc != get_doc(This))
        refcount = xmlnode_get_inst_cnt(xmlNode);

    /* old root is still orphaned by its document, update refcount from new root */
    if (refcount) xmldoc_add_refs(get_doc(This), refcount);
1622
    oldRoot = xmlDocSetRootElement( get_doc(This), xmlNode->node);
1623
    if (refcount) xmldoc_release_refs(old_doc, refcount);
1624 1625
    IXMLDOMNode_Release( elementNode );

1626 1627 1628
    if(oldRoot)
        xmldoc_add_orphan(oldRoot->doc, oldRoot);

1629
    return S_OK;
1630 1631 1632 1633
}


static HRESULT WINAPI domdoc_createElement(
1634
    IXMLDOMDocument3 *iface,
1635 1636 1637
    BSTR tagname,
    IXMLDOMElement** element )
{
1638
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1639
    IXMLDOMNode *node;
1640
    VARIANT type;
1641
    HRESULT hr;
1642

1643
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(tagname), element);
1644

1645
    if (!element || !tagname) return E_INVALIDARG;
1646

1647 1648
    V_VT(&type) = VT_I1;
    V_I1(&type) = NODE_ELEMENT;
1649

1650
    hr = IXMLDOMDocument3_createNode(iface, type, tagname, NULL, &node);
1651 1652 1653 1654 1655 1656 1657
    if (hr == S_OK)
    {
        IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)element);
        IXMLDOMNode_Release(node);
    }

    return hr;
1658 1659 1660 1661
}


static HRESULT WINAPI domdoc_createDocumentFragment(
1662
    IXMLDOMDocument3 *iface,
1663
    IXMLDOMDocumentFragment** frag )
1664
{
1665
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1666
    IXMLDOMNode *node;
1667
    VARIANT type;
1668
    HRESULT hr;
1669

1670
    TRACE("(%p)->(%p)\n", This, frag);
1671

1672 1673 1674 1675
    if (!frag) return E_INVALIDARG;

    *frag = NULL;

1676 1677
    V_VT(&type) = VT_I1;
    V_I1(&type) = NODE_DOCUMENT_FRAGMENT;
1678

1679
    hr = IXMLDOMDocument3_createNode(iface, type, NULL, NULL, &node);
1680 1681 1682 1683 1684 1685 1686
    if (hr == S_OK)
    {
        IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMDocumentFragment, (void**)frag);
        IXMLDOMNode_Release(node);
    }

    return hr;
1687 1688 1689 1690
}


static HRESULT WINAPI domdoc_createTextNode(
1691
    IXMLDOMDocument3 *iface,
1692 1693 1694
    BSTR data,
    IXMLDOMText** text )
{
1695
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1696 1697 1698
    IXMLDOMNode *node;
    VARIANT type;
    HRESULT hr;
1699

1700
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(data), text);
1701

1702
    if (!text) return E_INVALIDARG;
1703 1704 1705

    *text = NULL;

1706 1707
    V_VT(&type) = VT_I1;
    V_I1(&type) = NODE_TEXT;
1708

1709
    hr = IXMLDOMDocument3_createNode(iface, type, NULL, NULL, &node);
1710 1711 1712 1713 1714 1715
    if (hr == S_OK)
    {
        IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMText, (void**)text);
        IXMLDOMNode_Release(node);
        hr = IXMLDOMText_put_data(*text, data);
    }
1716

1717
    return hr;
1718 1719 1720 1721
}


static HRESULT WINAPI domdoc_createComment(
1722
    IXMLDOMDocument3 *iface,
1723 1724 1725
    BSTR data,
    IXMLDOMComment** comment )
{
1726
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1727 1728 1729
    VARIANT type;
    HRESULT hr;
    IXMLDOMNode *node;
1730

1731
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(data), comment);
1732

1733
    if (!comment) return E_INVALIDARG;
1734 1735 1736

    *comment = NULL;

1737 1738
    V_VT(&type) = VT_I1;
    V_I1(&type) = NODE_COMMENT;
1739

1740
    hr = IXMLDOMDocument3_createNode(iface, type, NULL, NULL, &node);
1741 1742 1743 1744 1745 1746
    if (hr == S_OK)
    {
        IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMComment, (void**)comment);
        IXMLDOMNode_Release(node);
        hr = IXMLDOMComment_put_data(*comment, data);
    }
1747

1748
    return hr;
1749 1750 1751 1752
}


static HRESULT WINAPI domdoc_createCDATASection(
1753
    IXMLDOMDocument3 *iface,
1754 1755 1756
    BSTR data,
    IXMLDOMCDATASection** cdata )
{
1757
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1758 1759 1760
    IXMLDOMNode *node;
    VARIANT type;
    HRESULT hr;
1761

1762
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(data), cdata);
1763

1764
    if (!cdata) return E_INVALIDARG;
1765 1766 1767

    *cdata = NULL;

1768 1769
    V_VT(&type) = VT_I1;
    V_I1(&type) = NODE_CDATA_SECTION;
1770

1771
    hr = IXMLDOMDocument3_createNode(iface, type, NULL, NULL, &node);
1772 1773 1774 1775 1776 1777
    if (hr == S_OK)
    {
        IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMCDATASection, (void**)cdata);
        IXMLDOMNode_Release(node);
        hr = IXMLDOMCDATASection_put_data(*cdata, data);
    }
1778

1779
    return hr;
1780 1781 1782 1783
}


static HRESULT WINAPI domdoc_createProcessingInstruction(
1784
    IXMLDOMDocument3 *iface,
1785 1786 1787 1788
    BSTR target,
    BSTR data,
    IXMLDOMProcessingInstruction** pi )
{
1789
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1790 1791 1792
    IXMLDOMNode *node;
    VARIANT type;
    HRESULT hr;
1793

1794
    TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(target), debugstr_w(data), pi);
1795

1796
    if (!pi) return E_INVALIDARG;
1797

1798
    *pi = NULL;
1799

1800 1801
    V_VT(&type) = VT_I1;
    V_I1(&type) = NODE_PROCESSING_INSTRUCTION;
1802

1803
    hr = IXMLDOMDocument3_createNode(iface, type, target, NULL, &node);
1804 1805
    if (hr == S_OK)
    {
1806
        xmlnode *node_obj;
1807

1808
        /* this is to bypass check in ::put_data() that blocks "<?xml" PIs */
1809
        node_obj = get_node_obj(node);
1810
        hr = node_set_content(node_obj, data);
1811 1812 1813 1814 1815 1816

        IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMProcessingInstruction, (void**)pi);
        IXMLDOMNode_Release(node);
    }

    return hr;
1817 1818 1819 1820
}


static HRESULT WINAPI domdoc_createAttribute(
1821
    IXMLDOMDocument3 *iface,
1822 1823 1824
    BSTR name,
    IXMLDOMAttribute** attribute )
{
1825
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1826 1827 1828
    IXMLDOMNode *node;
    VARIANT type;
    HRESULT hr;
1829

1830
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(name), attribute);
1831

1832
    if (!attribute || !name) return E_INVALIDARG;
1833

1834 1835
    V_VT(&type) = VT_I1;
    V_I1(&type) = NODE_ATTRIBUTE;
1836

1837
    hr = IXMLDOMDocument3_createNode(iface, type, name, NULL, &node);
1838 1839 1840 1841 1842
    if (hr == S_OK)
    {
        IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMAttribute, (void**)attribute);
        IXMLDOMNode_Release(node);
    }
1843

1844
    return hr;
1845 1846 1847 1848
}


static HRESULT WINAPI domdoc_createEntityReference(
1849
    IXMLDOMDocument3 *iface,
1850
    BSTR name,
1851
    IXMLDOMEntityReference** entityref )
1852
{
1853
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1854 1855 1856
    IXMLDOMNode *node;
    VARIANT type;
    HRESULT hr;
1857

1858
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(name), entityref);
1859

1860
    if (!entityref) return E_INVALIDARG;
1861

1862
    *entityref = NULL;
1863

1864 1865
    V_VT(&type) = VT_I1;
    V_I1(&type) = NODE_ENTITY_REFERENCE;
1866

1867
    hr = IXMLDOMDocument3_createNode(iface, type, name, NULL, &node);
1868 1869 1870 1871 1872
    if (hr == S_OK)
    {
        IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMEntityReference, (void**)entityref);
        IXMLDOMNode_Release(node);
    }
1873

1874
    return hr;
1875 1876
}

1877 1878 1879
xmlChar* tagName_to_XPath(const BSTR tagName)
{
    xmlChar *query, *tmp;
1880
    static const xmlChar everything[] = "/descendant::node()";
1881 1882 1883 1884 1885 1886
    static const xmlChar mod_pre[] = "*[local-name()='";
    static const xmlChar mod_post[] = "']";
    static const xmlChar prefix[] = "descendant::";
    const WCHAR *tokBegin, *tokEnd;
    int len;

1887 1888 1889 1890 1891
    /* Special case - empty tagname - means select all nodes,
       except document itself. */
    if (!*tagName)
        return xmlStrdup(everything);

1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923
    query = xmlStrdup(prefix);

    tokBegin = tagName;
    while (tokBegin && *tokBegin)
    {
        switch (*tokBegin)
        {
        case '/':
            query = xmlStrcat(query, BAD_CAST "/");
            ++tokBegin;
            break;
        case '*':
            query = xmlStrcat(query, BAD_CAST "*");
            ++tokBegin;
            break;
        default:
            query = xmlStrcat(query, mod_pre);
            tokEnd = tokBegin;
            while (*tokEnd && *tokEnd != '/')
                ++tokEnd;
            len = WideCharToMultiByte(CP_UTF8, 0, tokBegin, tokEnd-tokBegin, NULL, 0, NULL, NULL);
            tmp = xmlMalloc(len);
            WideCharToMultiByte(CP_UTF8, 0, tokBegin, tokEnd-tokBegin, (char*)tmp, len, NULL, NULL);
            query = xmlStrncat(query, tmp, len);
            xmlFree(tmp);
            tokBegin = tokEnd;
            query = xmlStrcat(query, mod_post);
        }
    }

    return query;
}
1924 1925

static HRESULT WINAPI domdoc_getElementsByTagName(
1926
    IXMLDOMDocument3 *iface,
1927 1928 1929
    BSTR tagName,
    IXMLDOMNodeList** resultList )
{
1930
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1931
    xmlChar *query;
1932
    HRESULT hr;
1933
    BOOL XPath;
1934

1935
    TRACE("(%p)->(%s, %p)\n", This, debugstr_w(tagName), resultList);
1936

1937 1938
    if (!tagName || !resultList) return E_INVALIDARG;

1939 1940 1941
    XPath = This->properties->XPath;
    This->properties->XPath = TRUE;
    query = tagName_to_XPath(tagName);
1942
    hr = create_selection((xmlNodePtr)get_doc(This), query, resultList);
1943 1944
    xmlFree(query);
    This->properties->XPath = XPath;
1945 1946

    return hr;
1947 1948
}

1949
static HRESULT get_node_type(VARIANT Type, DOMNodeType * type)
1950
{
1951 1952 1953 1954 1955 1956 1957 1958 1959
    VARIANT tmp;
    HRESULT hr;

    VariantInit(&tmp);
    hr = VariantChangeType(&tmp, &Type, 0, VT_I4);
    if(FAILED(hr))
        return E_INVALIDARG;

    *type = V_I4(&tmp);
1960

1961
    return S_OK;
1962
}
1963 1964

static HRESULT WINAPI domdoc_createNode(
1965
    IXMLDOMDocument3 *iface,
1966 1967 1968 1969 1970
    VARIANT Type,
    BSTR name,
    BSTR namespaceURI,
    IXMLDOMNode** node )
{
1971
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
1972
    DOMNodeType node_type;
1973
    xmlNodePtr xmlnode;
1974
    xmlChar *xml_name, *href;
1975
    HRESULT hr;
1976

1977
    TRACE("(%p)->(%s %s %s %p)\n", This, debugstr_variant(&Type), debugstr_w(name), debugstr_w(namespaceURI), node);
1978

1979 1980
    if(!node) return E_INVALIDARG;

1981
    hr = get_node_type(Type, &node_type);
1982
    if(FAILED(hr)) return hr;
1983

1984
    TRACE("node_type %d\n", node_type);
1985

1986 1987 1988 1989 1990
    /* exit earlier for types that need name */
    switch(node_type)
    {
    case NODE_ELEMENT:
    case NODE_ATTRIBUTE:
1991
    case NODE_ENTITY_REFERENCE:
1992
    case NODE_PROCESSING_INSTRUCTION:
1993
        if (!name || *name == 0) return E_FAIL;
1994
        break;
1995 1996 1997
    default:
        break;
    }
1998

1999
    xml_name = xmlchar_from_wchar(name);
2000
    /* prevent empty href from being allocated */
2001
    href = namespaceURI ? xmlchar_from_wchar(namespaceURI) : NULL;
2002

2003 2004 2005
    switch(node_type)
    {
    case NODE_ELEMENT:
2006 2007 2008 2009 2010 2011 2012
    {
        xmlChar *local, *prefix;

        local = xmlSplitQName2(xml_name, &prefix);

        xmlnode = xmlNewDocNode(get_doc(This), NULL, local ? local : xml_name, NULL);

2013
        /* allow creating the default namespace xmlns= */
2014 2015 2016 2017 2018 2019 2020 2021 2022
        if (local || (href && *href))
        {
            xmlNsPtr ns = xmlNewNs(xmlnode, href, prefix);
            xmlSetNs(xmlnode, ns);
        }

        xmlFree(local);
        xmlFree(prefix);

2023
        break;
2024
    }
2025
    case NODE_ATTRIBUTE:
2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043
    {
        xmlChar *local, *prefix;

        local = xmlSplitQName2(xml_name, &prefix);

        xmlnode = (xmlNodePtr)xmlNewDocProp(get_doc(This), local ? local : xml_name, NULL);

        if (local || (href && *href))
        {
            /* we need a floating namespace here, it can't be created linked to attribute from
               a start */
            xmlNsPtr ns = xmlNewNs(NULL, href, prefix);
            xmlSetNs(xmlnode, ns);
        }

        xmlFree(local);
        xmlFree(prefix);

2044
        break;
2045
    }
2046 2047 2048 2049 2050 2051
    case NODE_TEXT:
        xmlnode = (xmlNodePtr)xmlNewDocText(get_doc(This), NULL);
        break;
    case NODE_CDATA_SECTION:
        xmlnode = xmlNewCDataBlock(get_doc(This), NULL, 0);
        break;
2052 2053 2054
    case NODE_ENTITY_REFERENCE:
        xmlnode = xmlNewReference(get_doc(This), xml_name);
        break;
2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075
    case NODE_PROCESSING_INSTRUCTION:
#ifdef HAVE_XMLNEWDOCPI
        xmlnode = xmlNewDocPI(get_doc(This), xml_name, NULL);
#else
        FIXME("xmlNewDocPI() not supported, use libxml2 2.6.15 or greater\n");
        xmlnode = NULL;
#endif
        break;
    case NODE_COMMENT:
        xmlnode = xmlNewDocComment(get_doc(This), NULL);
        break;
    case NODE_DOCUMENT_FRAGMENT:
        xmlnode = xmlNewDocFragment(get_doc(This));
        break;
    /* unsupported types */
    case NODE_DOCUMENT:
    case NODE_DOCUMENT_TYPE:
    case NODE_ENTITY:
    case NODE_NOTATION:
        heap_free(xml_name);
        return E_INVALIDARG;
2076 2077
    default:
        FIXME("unhandled node type %d\n", node_type);
2078
        xmlnode = NULL;
2079 2080 2081
        break;
    }

2082
    *node = create_node(xmlnode);
2083
    heap_free(xml_name);
2084
    heap_free(href);
2085

2086
    if(*node)
2087
    {
2088
        TRACE("created node (%d, %p, %p)\n", node_type, *node, xmlnode);
2089
        xmldoc_add_orphan(xmlnode->doc, xmlnode);
2090
        return S_OK;
2091
    }
2092 2093 2094

    return E_FAIL;
}
2095 2096

static HRESULT WINAPI domdoc_nodeFromID(
2097
    IXMLDOMDocument3 *iface,
2098 2099 2100
    BSTR idString,
    IXMLDOMNode** node )
{
2101
    domdoc *This = impl_from_IXMLDOMDocument3(iface);
2102
    FIXME("(%p)->(%s %p)\n", This, debugstr_w(idString), node);
2103 2104 2105
    return E_NOTIMPL;
}

2106
static HRESULT domdoc_onDataAvailable(void *obj, char *ptr, DWORD len)
2107
{
2108 2109
    domdoc *This = obj;
    xmlDocPtr xmldoc;
2110

2111
    xmldoc = doparse(This, ptr, len, XML_CHAR_ENCODING_NONE);
2112
    if(xmldoc) {
2113
        xmldoc->_private = create_priv();
2114
        return attach_xmldoc(This, xmldoc);
2115 2116
    }

2117
    return E_FAIL;
2118
}
2119

2120
static HRESULT domdoc_load_moniker(domdoc *This, IMoniker *mon)
2121 2122 2123
{
    bsc_t *bsc;
    HRESULT hr;
2124

2125
    hr = bind_url(mon, domdoc_onDataAvailable, This, &bsc);
2126 2127
    if(FAILED(hr))
        return hr;
2128

2129
    return detach_bsc(bsc);
2130 2131 2132
}

static HRESULT WINAPI domdoc_load(
2133
    IXMLDOMDocument3 *iface,
2134
    VARIANT source,
2135 2136
    VARIANT_BOOL* isSuccessful )
{
2137
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2138
    LPWSTR filename = NULL;
2139
    HRESULT hr = S_FALSE;
2140
    xmlDocPtr xmldoc;
2141

2142
    TRACE("(%p)->(%s)\n", This, debugstr_variant(&source));
2143

2144 2145
    if (!isSuccessful)
        return E_POINTER;
2146 2147
    *isSuccessful = VARIANT_FALSE;

2148
    assert( &This->node );
2149

2150
    switch( V_VT(&source) )
2151 2152
    {
    case VT_BSTR:
2153 2154 2155 2156 2157
        filename = V_BSTR(&source);
        break;
    case VT_BSTR|VT_BYREF:
        if (!V_BSTRREF(&source)) return E_INVALIDARG;
        filename = *V_BSTRREF(&source);
2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174
        break;
    case VT_ARRAY|VT_UI1:
        {
            SAFEARRAY *psa = V_ARRAY(&source);
            char *str;
            LONG len;
            UINT dim = SafeArrayGetDim(psa);

            switch (dim)
            {
            case 0:
                ERR("SAFEARRAY == NULL\n");
                hr = This->error = E_INVALIDARG;
                break;
            case 1:
                /* Only takes UTF-8 strings.
                 * NOT NULL-terminated. */
2175 2176 2177 2178 2179 2180 2181
                hr = SafeArrayAccessData(psa, (void**)&str);
                if (FAILED(hr))
                {
                    This->error = hr;
                    WARN("failed to access array data, 0x%08x\n", hr);
                    break;
                }
2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208
                SafeArrayGetUBound(psa, 1, &len);

                if ((xmldoc = doparse(This, str, ++len, XML_CHAR_ENCODING_UTF8)))
                {
                    hr = This->error = S_OK;
                    *isSuccessful = VARIANT_TRUE;
                    TRACE("parsed document %p\n", xmldoc);
                }
                else
                {
                    This->error = E_FAIL;
                    TRACE("failed to parse document\n");
                }

                SafeArrayUnaccessData(psa);

                if(xmldoc)
                {
                    xmldoc->_private = create_priv();
                    return attach_xmldoc(This, xmldoc);
                }
                break;
            default:
                FIXME("unhandled SAFEARRAY dim: %d\n", dim);
                hr = This->error = E_NOTIMPL;
            }
        }
2209 2210
        break;
    case VT_UNKNOWN:
2211 2212 2213 2214
    {
        ISequentialStream *stream = NULL;
        IXMLDOMDocument3 *newdoc = NULL;

2215
        if (!V_UNKNOWN(&source)) return E_INVALIDARG;
2216 2217

        hr = IUnknown_QueryInterface(V_UNKNOWN(&source), &IID_IXMLDOMDocument3, (void**)&newdoc);
2218 2219
        if(hr == S_OK)
        {
2220
            if(newdoc)
2221
            {
2222
                domdoc *newDoc = impl_from_IXMLDOMDocument3( newdoc );
2223

2224
                xmldoc = xmlCopyDoc(get_doc(newDoc), 1);
2225
                xmldoc->_private = create_priv();
2226
                hr = attach_xmldoc(This, xmldoc);
2227

2228 2229
                if(SUCCEEDED(hr))
                    *isSuccessful = VARIANT_TRUE;
2230

2231
                return hr;
2232 2233 2234
            }
        }

2235 2236 2237 2238 2239
        hr = IUnknown_QueryInterface(V_UNKNOWN(&source), &IID_IStream, (void**)&stream);
        if (FAILED(hr))
            hr = IUnknown_QueryInterface(V_UNKNOWN(&source), &IID_ISequentialStream, (void**)&stream);

        if (hr == S_OK)
2240
        {
2241 2242 2243 2244 2245
            hr = domdoc_load_from_stream(This, stream);
            if (hr == S_OK)
                *isSuccessful = VARIANT_TRUE;
            ISequentialStream_Release(stream);
            return hr;
2246
        }
2247 2248

        FIXME("unsupported IUnknown type (0x%08x) (%p)\n", hr, V_UNKNOWN(&source)->lpVtbl);
2249
        break;
2250 2251 2252
    }
    default:
        FIXME("VT type not supported (%d)\n", V_VT(&source));
2253
    }
2254

2255
    if ( filename )
2256
    {
2257 2258
        IMoniker *mon;

2259 2260
        CoTaskMemFree(This->properties->url);
        This->properties->url = NULL;
2261

2262 2263 2264
        hr = create_moniker_from_url( filename, &mon);
        if ( SUCCEEDED(hr) )
        {
2265
            hr = domdoc_load_moniker( This, mon );
2266
            if (hr == S_OK)
2267
                IMoniker_GetDisplayName(mon, NULL, NULL, &This->properties->url);
2268 2269
            IMoniker_Release(mon);
        }
2270

2271
        if ( FAILED(hr) )
2272 2273 2274 2275 2276 2277
            This->error = E_FAIL;
        else
        {
            hr = This->error = S_OK;
            *isSuccessful = VARIANT_TRUE;
        }
2278
    }
2279

2280
    if(!filename || FAILED(hr)) {
2281
        xmldoc = xmlNewDoc(NULL);
2282
        xmldoc->_private = create_priv();
2283
        hr = attach_xmldoc(This, xmldoc);
2284 2285
        if(SUCCEEDED(hr))
            hr = S_FALSE;
2286
    }
2287

2288 2289
    TRACE("ret (%d)\n", hr);

2290
    return hr;
2291 2292 2293 2294
}


static HRESULT WINAPI domdoc_get_readyState(
2295
    IXMLDOMDocument3 *iface,
2296
    LONG *value )
2297
{
2298
    domdoc *This = impl_from_IXMLDOMDocument3(iface);
2299 2300 2301 2302 2303 2304 2305
    FIXME("stub! (%p)->(%p)\n", This, value);

    if (!value)
        return E_INVALIDARG;

    *value = READYSTATE_COMPLETE;
    return S_OK;
2306 2307 2308 2309
}


static HRESULT WINAPI domdoc_get_parseError(
2310
    IXMLDOMDocument3 *iface,
2311 2312
    IXMLDOMParseError** errorObj )
{
2313
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2314
    static const WCHAR err[] = {'e','r','r','o','r',0};
2315
    BSTR error_string = NULL;
2316

Huw Davies's avatar
Huw Davies committed
2317
    FIXME("(%p)->(%p): creating a dummy parseError\n", iface, errorObj);
2318 2319 2320 2321 2322

    if(This->error)
        error_string = SysAllocString(err);

    *errorObj = create_parseError(This->error, NULL, error_string, NULL, 0, 0, 0);
Huw Davies's avatar
Huw Davies committed
2323 2324
    if(!*errorObj) return E_OUTOFMEMORY;
    return S_OK;
2325 2326 2327 2328
}


static HRESULT WINAPI domdoc_get_url(
2329
    IXMLDOMDocument3 *iface,
2330
    BSTR* url )
2331
{
2332
    domdoc *This = impl_from_IXMLDOMDocument3(iface);
2333 2334 2335 2336 2337 2338

    TRACE("(%p)->(%p)\n", This, url);

    if (!url)
        return E_INVALIDARG;

2339
    if (This->properties->url)
2340
    {
2341
        *url = SysAllocString(This->properties->url);
2342 2343 2344 2345 2346 2347 2348
        if (!*url)
            return E_OUTOFMEMORY;

        return S_OK;
    }
    else
        return return_null_bstr(url);
2349 2350 2351 2352
}


static HRESULT WINAPI domdoc_get_async(
2353
    IXMLDOMDocument3 *iface,
2354 2355
    VARIANT_BOOL* isAsync )
{
2356
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2357

2358
    TRACE("(%p)->(%p: %d)\n", This, isAsync, This->async);
2359 2360 2361 2362 2363 2364
    *isAsync = This->async;
    return S_OK;
}


static HRESULT WINAPI domdoc_put_async(
2365
    IXMLDOMDocument3 *iface,
2366 2367
    VARIANT_BOOL isAsync )
{
2368
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2369

2370
    TRACE("(%p)->(%d)\n", This, isAsync);
2371 2372 2373 2374 2375 2376
    This->async = isAsync;
    return S_OK;
}


static HRESULT WINAPI domdoc_abort(
2377
    IXMLDOMDocument3 *iface )
2378
{
2379
    domdoc *This = impl_from_IXMLDOMDocument3(iface);
2380
    FIXME("%p\n", This);
2381 2382 2383
    return E_NOTIMPL;
}

2384
/* don't rely on data to be in BSTR format, treat it as WCHAR string */
2385
static HRESULT WINAPI domdoc_loadXML(
2386
    IXMLDOMDocument3 *iface,
2387
    BSTR data,
2388 2389
    VARIANT_BOOL* isSuccessful )
{
2390
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2391
    xmlDocPtr xmldoc = NULL;
2392
    HRESULT hr = S_FALSE, hr2;
2393

2394
    TRACE("(%p)->(%s %p)\n", This, debugstr_w(data), isSuccessful );
2395

2396
    assert ( &This->node );
2397

2398
    if ( isSuccessful )
2399
    {
2400 2401
        *isSuccessful = VARIANT_FALSE;

2402
        if (data)
2403
        {
2404 2405 2406 2407
            WCHAR *ptr = data;

            /* skip leading spaces if needed */
            if (This->properties->version == MSXML_DEFAULT || This->properties->version == MSXML26)
2408
                while (*ptr && isspaceW(*ptr)) ptr++;
2409 2410

            xmldoc = doparse(This, (char*)ptr, strlenW(ptr)*sizeof(WCHAR), XML_CHAR_ENCODING_UTF16LE);
2411
            if ( !xmldoc )
2412
            {
2413
                This->error = E_FAIL;
2414 2415
                TRACE("failed to parse document\n");
            }
2416 2417 2418 2419
            else
            {
                hr = This->error = S_OK;
                *isSuccessful = VARIANT_TRUE;
2420
                TRACE("parsed document %p\n", xmldoc);
2421 2422
            }
        }
2423
    }
2424

2425 2426
    if(!xmldoc)
        xmldoc = xmlNewDoc(NULL);
2427
    xmldoc->_private = create_priv();
2428
    hr2 = attach_xmldoc(This, xmldoc);
2429 2430
    if( FAILED(hr2) )
        hr = hr2;
2431

2432
    return hr;
2433 2434
}

2435
static int XMLCALL domdoc_save_writecallback(void *ctx, const char *buffer, int len)
2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451
{
    DWORD written = -1;

    if(!WriteFile(ctx, buffer, len, &written, NULL))
    {
        WARN("write error\n");
        return -1;
    }
    else
        return written;
}

static int XMLCALL domdoc_save_closecallback(void *ctx)
{
    return CloseHandle(ctx) ? 0 : -1;
}
2452

2453 2454 2455 2456 2457 2458
static int XMLCALL domdoc_stream_save_writecallback(void *ctx, const char *buffer, int len)
{
    ULONG written = 0;
    HRESULT hr;

    hr = IStream_Write((IStream*)ctx, buffer, len, &written);
2459
    TRACE("0x%08x %p %d %u\n", hr, buffer, len, written);
2460 2461 2462 2463 2464 2465
    if (hr != S_OK)
    {
        WARN("stream write error: 0x%08x\n", hr);
        return -1;
    }
    else
2466
        return len;
2467 2468 2469 2470 2471 2472 2473 2474
}

static int XMLCALL domdoc_stream_save_closecallback(void *ctx)
{
    IStream_Release((IStream*)ctx);
    return 0;
}

2475
static HRESULT WINAPI domdoc_save(
2476
    IXMLDOMDocument3 *iface,
2477 2478
    VARIANT destination )
{
2479
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2480
    xmlSaveCtxtPtr ctx = NULL;
2481
    xmlNodePtr xmldecl;
Huw Davies's avatar
Huw Davies committed
2482 2483
    HRESULT ret = S_OK;

2484
    TRACE("(%p)->(%s)\n", This, debugstr_variant(&destination));
Huw Davies's avatar
Huw Davies committed
2485

2486
    switch (V_VT(&destination))
Huw Davies's avatar
Huw Davies committed
2487
    {
2488
    case VT_UNKNOWN:
2489
        {
2490
            IUnknown *pUnk = V_UNKNOWN(&destination);
2491
            IXMLDOMDocument3 *document;
2492
            IStream *stream;
2493

2494
            ret = IUnknown_QueryInterface(pUnk, &IID_IXMLDOMDocument3, (void**)&document);
2495 2496
            if(ret == S_OK)
            {
2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508
                VARIANT_BOOL success;
                BSTR xml;

                ret = IXMLDOMDocument3_get_xml(iface, &xml);
                if(ret == S_OK)
                {
                    ret = IXMLDOMDocument3_loadXML(document, xml, &success);
                    SysFreeString(xml);
                }

                IXMLDOMDocument3_Release(document);
                return ret;
2509 2510
            }

2511 2512 2513
            ret = IUnknown_QueryInterface(pUnk, &IID_IStream, (void**)&stream);
            if(ret == S_OK)
            {
2514
                int options = get_doc(This)->standalone == -1 ? XML_SAVE_NO_DECL : 0;
2515
                ctx = xmlSaveToIO(domdoc_stream_save_writecallback,
2516
                    domdoc_stream_save_closecallback, stream, NULL, options);
2517 2518 2519 2520 2521 2522 2523

                if(!ctx)
                {
                    IStream_Release(stream);
                    return E_FAIL;
                }
            }
2524
        }
2525
        break;
2526

2527 2528
    case VT_BSTR:
    case VT_BSTR | VT_BYREF:
2529
        {
2530 2531
            int options = get_doc(This)->standalone == -1 ? XML_SAVE_NO_DECL : 0;

2532 2533 2534 2535 2536 2537 2538 2539
            /* save with file path */
            HANDLE handle = CreateFileW( (V_VT(&destination) & VT_BYREF)? *V_BSTRREF(&destination) : V_BSTR(&destination),
                                         GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
            if( handle == INVALID_HANDLE_VALUE )
            {
                WARN("failed to create file\n");
                return E_FAIL;
            }
2540

2541 2542
            /* disable top XML declaration */
            ctx = xmlSaveToIO(domdoc_save_writecallback, domdoc_save_closecallback,
2543
                              handle, NULL, options);
2544
            if (!ctx)
2545
            {
2546
                CloseHandle(handle);
2547 2548 2549
                return E_FAIL;
            }
        }
2550
        break;
Huw Davies's avatar
Huw Davies committed
2551

2552 2553 2554
    default:
        FIXME("Unhandled VARIANT: %s\n", debugstr_variant(&destination));
        return S_FALSE;
Huw Davies's avatar
Huw Davies committed
2555 2556
    }

2557
    xmldecl = xmldoc_unlink_xmldecl(get_doc(This));
2558
    if (xmlSaveDoc(ctx, get_doc(This)) == -1) ret = S_FALSE;
2559 2560
    xmldoc_link_xmldecl(get_doc(This), xmldecl);

2561
    /* will release resources through close callback */
2562 2563
    xmlSaveClose(ctx);

Huw Davies's avatar
Huw Davies committed
2564
    return ret;
2565 2566 2567
}

static HRESULT WINAPI domdoc_get_validateOnParse(
2568
    IXMLDOMDocument3 *iface,
2569 2570
    VARIANT_BOOL* isValidating )
{
2571
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2572
    TRACE("(%p)->(%p: %d)\n", This, isValidating, This->validating);
2573 2574
    *isValidating = This->validating;
    return S_OK;
2575 2576 2577 2578
}


static HRESULT WINAPI domdoc_put_validateOnParse(
2579
    IXMLDOMDocument3 *iface,
2580 2581
    VARIANT_BOOL isValidating )
{
2582
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2583
    TRACE("(%p)->(%d)\n", This, isValidating);
2584 2585
    This->validating = isValidating;
    return S_OK;
2586 2587 2588 2589
}


static HRESULT WINAPI domdoc_get_resolveExternals(
2590
    IXMLDOMDocument3 *iface,
2591 2592
    VARIANT_BOOL* isResolving )
{
2593
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2594
    TRACE("(%p)->(%p: %d)\n", This, isResolving, This->resolving);
2595 2596
    *isResolving = This->resolving;
    return S_OK;
2597 2598 2599 2600
}


static HRESULT WINAPI domdoc_put_resolveExternals(
2601
    IXMLDOMDocument3 *iface,
2602
    VARIANT_BOOL isResolving )
2603
{
2604
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2605
    TRACE("(%p)->(%d)\n", This, isResolving);
2606 2607
    This->resolving = isResolving;
    return S_OK;
2608 2609 2610 2611
}


static HRESULT WINAPI domdoc_get_preserveWhiteSpace(
2612
    IXMLDOMDocument3 *iface,
2613 2614
    VARIANT_BOOL* isPreserving )
{
2615
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2616 2617
    TRACE("(%p)->(%p: %d)\n", This, isPreserving, This->properties->preserving);
    *isPreserving = This->properties->preserving;
2618
    return S_OK;
2619 2620 2621 2622
}


static HRESULT WINAPI domdoc_put_preserveWhiteSpace(
2623
    IXMLDOMDocument3 *iface,
2624 2625
    VARIANT_BOOL isPreserving )
{
2626
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2627
    TRACE("(%p)->(%d)\n", This, isPreserving);
2628
    This->properties->preserving = isPreserving;
2629
    return S_OK;
2630 2631 2632
}


2633
static HRESULT WINAPI domdoc_put_onreadystatechange(
2634
    IXMLDOMDocument3 *iface,
2635
    VARIANT event )
2636
{
2637
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2638 2639 2640

    TRACE("(%p)->(%s)\n", This, debugstr_variant(&event));
    return set_doc_event(This, EVENTID_READYSTATECHANGE, &event);
2641 2642 2643
}


2644
static HRESULT WINAPI domdoc_put_onDataAvailable(IXMLDOMDocument3 *iface, VARIANT sink)
2645
{
2646
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2647
    FIXME("(%p)->(%s): stub\n", This, debugstr_variant(&sink));
2648 2649 2650
    return E_NOTIMPL;
}

2651
static HRESULT WINAPI domdoc_put_onTransformNode(IXMLDOMDocument3 *iface, VARIANT sink )
2652
{
2653
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2654
    FIXME("(%p)->(%s): stub\n", This, debugstr_variant(&sink));
2655 2656 2657
    return E_NOTIMPL;
}

2658
static HRESULT WINAPI domdoc_get_namespaces(
2659
    IXMLDOMDocument3* iface,
2660
    IXMLDOMSchemaCollection** collection )
2661
{
2662
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2663 2664 2665
    HRESULT hr;

    FIXME("(%p)->(%p): semi-stub\n", This, collection);
2666 2667 2668

    if (!collection) return E_POINTER;

2669 2670
    if (!This->namespaces)
    {
2671
        hr = SchemaCache_create(This->properties->version, (void**)&This->namespaces);
2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683
        if (hr != S_OK) return hr;

        hr = cache_from_doc_ns(This->namespaces, &This->node);
        if (hr != S_OK)
            release_namespaces(This);
    }

    if (This->namespaces)
        return IXMLDOMSchemaCollection2_QueryInterface(This->namespaces,
                   &IID_IXMLDOMSchemaCollection, (void**)collection);

    return hr;
2684 2685 2686
}

static HRESULT WINAPI domdoc_get_schemas(
2687
    IXMLDOMDocument3* iface,
2688
    VARIANT* schema )
2689
{
2690
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2691
    IXMLDOMSchemaCollection2* cur_schema = This->properties->schemaCache;
2692
    HRESULT hr = S_FALSE;
2693

2694
    TRACE("(%p)->(%p)\n", This, schema);
2695

2696 2697 2698
    V_VT(schema) = VT_NULL;
    /* just to reset pointer part, cause that's what application is expected to use */
    V_DISPATCH(schema) = NULL;
2699 2700 2701

    if(cur_schema)
    {
2702
        hr = IXMLDOMSchemaCollection2_QueryInterface(cur_schema, &IID_IDispatch, (void**)&V_DISPATCH(schema));
2703
        if(SUCCEEDED(hr))
2704
            V_VT(schema) = VT_DISPATCH;
2705 2706
    }
    return hr;
2707 2708 2709
}

static HRESULT WINAPI domdoc_putref_schemas(
2710
    IXMLDOMDocument3* iface,
2711
    VARIANT schema)
2712
{
2713
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2714
    HRESULT hr = E_FAIL;
2715
    IXMLDOMSchemaCollection2* new_schema = NULL;
2716

2717 2718
    FIXME("(%p)->(%s): semi-stub\n", This, debugstr_variant(&schema));
    switch(V_VT(&schema))
2719 2720
    {
    case VT_UNKNOWN:
2721 2722 2723 2724 2725 2726
        if (V_UNKNOWN(&schema))
        {
            hr = IUnknown_QueryInterface(V_UNKNOWN(&schema), &IID_IXMLDOMSchemaCollection, (void**)&new_schema);
            break;
        }
        /* fallthrough */
2727
    case VT_DISPATCH:
2728 2729 2730 2731 2732 2733
        if (V_DISPATCH(&schema))
        {
            hr = IDispatch_QueryInterface(V_DISPATCH(&schema), &IID_IXMLDOMSchemaCollection, (void**)&new_schema);
            break;
        }
        /* fallthrough */
2734 2735 2736 2737 2738 2739
    case VT_NULL:
    case VT_EMPTY:
        hr = S_OK;
        break;

    default:
2740
        WARN("Can't get schema from vt %x\n", V_VT(&schema));
2741 2742 2743 2744
    }

    if(SUCCEEDED(hr))
    {
2745
        IXMLDOMSchemaCollection2* old_schema = InterlockedExchangePointer((void**)&This->properties->schemaCache, new_schema);
2746
        if(old_schema) IXMLDOMSchemaCollection2_Release(old_schema);
2747 2748 2749
    }

    return hr;
2750 2751
}

2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781
static inline BOOL is_wellformed(xmlDocPtr doc)
{
#ifdef HAVE_XMLDOC_PROPERTIES
    return doc->properties & XML_DOC_WELLFORMED;
#else
    /* Not a full check, but catches the worst violations */
    xmlNodePtr child;
    int root = 0;

    for (child = doc->children; child != NULL; child = child->next)
    {
        switch (child->type)
        {
        case XML_ELEMENT_NODE:
            if (++root > 1)
                return FALSE;
            break;
        case XML_TEXT_NODE:
        case XML_CDATA_SECTION_NODE:
            return FALSE;
            break;
        default:
            break;
        }
    }

    return root == 1;
#endif
}

2782 2783 2784 2785
static void LIBXML2_LOG_CALLBACK validate_error(void* ctx, char const* msg, ...)
{
    va_list ap;
    va_start(ap, msg);
2786
    LIBXML2_CALLBACK_ERR(domdoc_validateNode, msg, ap);
2787 2788 2789 2790 2791 2792 2793
    va_end(ap);
}

static void LIBXML2_LOG_CALLBACK validate_warning(void* ctx, char const* msg, ...)
{
    va_list ap;
    va_start(ap, msg);
2794
    LIBXML2_CALLBACK_WARN(domdoc_validateNode, msg, ap);
2795 2796 2797
    va_end(ap);
}

2798
static HRESULT WINAPI domdoc_validateNode(
2799
    IXMLDOMDocument3* iface,
2800
    IXMLDOMNode* node,
2801 2802
    IXMLDOMParseError** err)
{
2803 2804 2805 2806
    domdoc* This = impl_from_IXMLDOMDocument3(iface);
    LONG state, err_code = 0;
    HRESULT hr = S_OK;
    int validated = 0;
2807

2808
    TRACE("(%p)->(%p, %p)\n", This, node, err);
2809
    IXMLDOMDocument3_get_readyState(iface, &state);
2810 2811 2812
    if (state != READYSTATE_COMPLETE)
    {
        if (err)
2813
           *err = create_parseError(err_code, NULL, NULL, NULL, 0, 0, 0);
2814 2815 2816
        return E_PENDING;
    }

2817 2818 2819 2820 2821 2822
    if (!node)
    {
        if (err)
            *err = create_parseError(err_code, NULL, NULL, NULL, 0, 0, 0);
        return E_POINTER;
    }
2823

2824
    if (!get_node_obj(node)->node || get_node_obj(node)->node->doc != get_doc(This))
2825 2826
    {
        if (err)
2827 2828 2829 2830 2831 2832
            *err = create_parseError(err_code, NULL, NULL, NULL, 0, 0, 0);
        return E_FAIL;
    }

    if (!is_wellformed(get_doc(This)))
    {
2833
        ERR("doc not well-formed\n");
2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855
        if (err)
            *err = create_parseError(E_XML_NOTWF, NULL, NULL, NULL, 0, 0, 0);
        return S_FALSE;
    }

    /* DTD validation */
    if (get_doc(This)->intSubset || get_doc(This)->extSubset)
    {
        xmlValidCtxtPtr vctx = xmlNewValidCtxt();
        vctx->error = validate_error;
        vctx->warning = validate_warning;
        ++validated;

        if (!((node == (IXMLDOMNode*)iface)?
              xmlValidateDocument(vctx, get_doc(This)) :
              xmlValidateElement(vctx, get_doc(This), get_node_obj(node)->node)))
        {
            /* TODO: get a real error code here */
            TRACE("DTD validation failed\n");
            err_code = E_XML_INVALID;
            hr = S_FALSE;
        }
2856 2857 2858
        xmlFreeValidCtxt(vctx);
    }

2859
    /* Schema validation */
2860
    if (hr == S_OK && This->properties->schemaCache != NULL)
2861 2862
    {

2863
        hr = SchemaCache_validate_tree(This->properties->schemaCache, get_node_obj(node)->node);
2864
        if (SUCCEEDED(hr))
2865 2866 2867
        {
            ++validated;
            /* TODO: get a real error code here */
2868 2869 2870 2871 2872 2873 2874
            if (hr == S_OK)
            {
                TRACE("schema validation succeeded\n");
            }
            else
            {
                ERR("schema validation failed\n");
2875
                err_code = E_XML_INVALID;
2876
            }
2877 2878 2879 2880 2881 2882 2883 2884 2885 2886
        }
        else
        {
            /* not really OK, just didn't find a schema for the ns */
            hr = S_OK;
        }
    }

    if (!validated)
    {
2887
        ERR("no DTD or schema found\n");
2888 2889 2890 2891
        err_code = E_XML_NODTD;
        hr = S_FALSE;
    }

2892
    if (err)
2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903
        *err = create_parseError(err_code, NULL, NULL, NULL, 0, 0, 0);

    return hr;
}

static HRESULT WINAPI domdoc_validate(
    IXMLDOMDocument3* iface,
    IXMLDOMParseError** err)
{
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
    TRACE("(%p)->(%p)\n", This, err);
2904
    return IXMLDOMDocument3_validateNode(iface, (IXMLDOMNode*)iface, err);
2905 2906 2907
}

static HRESULT WINAPI domdoc_setProperty(
2908
    IXMLDOMDocument3* iface,
2909
    BSTR p,
2910
    VARIANT value)
2911
{
2912
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
2913

2914
    TRACE("(%p)->(%s %s)\n", This, debugstr_w(p), debugstr_variant(&value));
2915

2916
    if (lstrcmpiW(p, PropertySelectionLanguageW) == 0)
2917 2918 2919 2920 2921 2922
    {
        VARIANT varStr;
        HRESULT hr;
        BSTR bstr;

        V_VT(&varStr) = VT_EMPTY;
2923
        if (V_VT(&value) != VT_BSTR)
2924
        {
2925
            if (FAILED(hr = VariantChangeType(&varStr, &value, 0, VT_BSTR)))
2926 2927 2928 2929
                return hr;
            bstr = V_BSTR(&varStr);
        }
        else
2930
            bstr = V_BSTR(&value);
2931 2932

        hr = S_OK;
2933
        if (lstrcmpiW(bstr, PropValueXPathW) == 0)
2934
            This->properties->XPath = TRUE;
2935
        else if (lstrcmpiW(bstr, PropValueXSLPatternW) == 0)
2936
            This->properties->XPath = FALSE;
2937 2938 2939 2940 2941 2942
        else
            hr = E_FAIL;

        VariantClear(&varStr);
        return hr;
    }
2943 2944
    else if (lstrcmpiW(p, PropertySelectionNamespacesW) == 0)
    {
2945 2946
        xmlChar *nsStr = (xmlChar*)This->properties->selectNsStr;
        struct list *pNsList;
2947 2948 2949 2950 2951
        VARIANT varStr;
        HRESULT hr;
        BSTR bstr;

        V_VT(&varStr) = VT_EMPTY;
2952
        if (V_VT(&value) != VT_BSTR)
2953
        {
2954
            if (FAILED(hr = VariantChangeType(&varStr, &value, 0, VT_BSTR)))
2955 2956 2957 2958
                return hr;
            bstr = V_BSTR(&varStr);
        }
        else
2959
            bstr = V_BSTR(&value);
2960 2961 2962

        hr = S_OK;

2963
        pNsList = &(This->properties->selectNsList);
2964 2965
        clear_selectNsList(pNsList);
        heap_free(nsStr);
2966
        nsStr = xmlchar_from_wchar(bstr);
2967

2968
        TRACE("property value: \"%s\"\n", debugstr_w(bstr));
2969

2970 2971
        This->properties->selectNsStr = nsStr;
        This->properties->selectNsStr_len = xmlStrlen(nsStr);
2972 2973
        if (bstr && *bstr)
        {
2974 2975 2976 2977
            xmlChar *pTokBegin, *pTokEnd, *pTokInner;
            select_ns_entry* ns_entry = NULL;
            xmlXPathContextPtr ctx;

2978 2979
            ctx = xmlXPathNewContext(This->node.node->doc);
            pTokBegin = nsStr;
2980 2981 2982 2983 2984 2985

            /* skip leading spaces */
            while (*pTokBegin == ' '  || *pTokBegin == '\n' ||
                   *pTokBegin == '\t' || *pTokBegin == '\r')
                ++pTokBegin;

2986 2987
            for (; *pTokBegin; pTokBegin = pTokEnd)
            {
2988 2989
                if (ns_entry)
                    memset(ns_entry, 0, sizeof(select_ns_entry));
2990
                else
2991
                    ns_entry = heap_alloc_zero(sizeof(select_ns_entry));
2992 2993 2994 2995 2996 2997 2998

                while (*pTokBegin == ' ')
                    ++pTokBegin;
                pTokEnd = pTokBegin;
                while (*pTokEnd != ' ' && *pTokEnd != 0)
                    ++pTokEnd;

2999 3000 3001
                /* so it failed to advance which means we've got some trailing spaces */
                if (pTokEnd == pTokBegin) break;

3002 3003 3004 3005
                if (xmlStrncmp(pTokBegin, (xmlChar const*)"xmlns", 5) != 0)
                {
                    hr = E_FAIL;
                    WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
3006
                          debugstr_w(bstr), debugstr_an((const char*)pTokBegin, pTokEnd-pTokBegin));
3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018
                    continue;
                }

                pTokBegin += 5;
                if (*pTokBegin == '=')
                {
                    /*valid for XSLPattern?*/
                    FIXME("Setting default xmlns not supported - skipping.\n");
                    continue;
                }
                else if (*pTokBegin == ':')
                {
3019
                    ns_entry->prefix = ++pTokBegin;
3020 3021 3022 3023 3024 3025 3026
                    for (pTokInner = pTokBegin; pTokInner != pTokEnd && *pTokInner != '='; ++pTokInner)
                        ;

                    if (pTokInner == pTokEnd)
                    {
                        hr = E_FAIL;
                        WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
3027
                              debugstr_w(bstr), debugstr_an((const char*)pTokBegin, pTokEnd-pTokBegin));
3028 3029 3030
                        continue;
                    }

3031
                    ns_entry->prefix_end = *pTokInner;
3032 3033 3034 3035 3036 3037 3038
                    *pTokInner = 0;
                    ++pTokInner;

                    if (pTokEnd-pTokInner > 1 &&
                        ((*pTokInner == '\'' && *(pTokEnd-1) == '\'') ||
                         (*pTokInner == '"' && *(pTokEnd-1) == '"')))
                    {
3039 3040
                        ns_entry->href = ++pTokInner;
                        ns_entry->href_end = *(pTokEnd-1);
3041
                        *(pTokEnd-1) = 0;
3042
                        list_add_tail(pNsList, &ns_entry->entry);
3043
                        /*let libxml figure out if they're valid from here ;)*/
3044
                        if (xmlXPathRegisterNs(ctx, ns_entry->prefix, ns_entry->href) != 0)
3045 3046 3047
                        {
                            hr = E_FAIL;
                        }
3048
                        ns_entry = NULL;
3049 3050 3051 3052 3053
                        continue;
                    }
                    else
                    {
                        WARN("Syntax error in xmlns string: %s\n\tat token: %s\n",
3054
                              debugstr_w(bstr), debugstr_an((const char*)pTokInner, pTokEnd-pTokInner));
3055
                        list_add_tail(pNsList, &ns_entry->entry);
3056

3057
                        ns_entry = NULL;
3058 3059 3060 3061 3062 3063 3064 3065 3066 3067
                        hr = E_FAIL;
                        continue;
                    }
                }
                else
                {
                    hr = E_FAIL;
                    continue;
                }
            }
3068
            heap_free(ns_entry);
3069 3070 3071 3072 3073 3074
            xmlXPathFreeContext(ctx);
        }

        VariantClear(&varStr);
        return hr;
    }
3075
    else if (lstrcmpiW(p, PropertyProhibitDTDW) == 0 ||
3076
             lstrcmpiW(p, PropertyNewParserW) == 0 ||
3077 3078 3079
             lstrcmpiW(p, PropertyResolveExternalsW) == 0 ||
             lstrcmpiW(p, PropertyAllowXsltScriptW) == 0 ||
             lstrcmpiW(p, PropertyAllowDocumentFunctionW) == 0)
Paul Vriens's avatar
Paul Vriens committed
3080 3081
    {
        /* Ignore */
3082
        FIXME("Ignoring property %s, value %s\n", debugstr_w(p), debugstr_variant(&value));
Paul Vriens's avatar
Paul Vriens committed
3083 3084
        return S_OK;
    }
3085

3086
    FIXME("Unknown property %s\n", debugstr_w(p));
3087
    return E_FAIL;
3088 3089 3090
}

static HRESULT WINAPI domdoc_getProperty(
3091
    IXMLDOMDocument3* iface,
3092 3093 3094
    BSTR p,
    VARIANT* var)
{
3095
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
3096

3097
    TRACE("(%p)->(%s)\n", This, debugstr_w(p));
3098

3099
    if (!var)
3100
        return E_INVALIDARG;
3101 3102

    if (lstrcmpiW(p, PropertySelectionLanguageW) == 0)
3103 3104
    {
        V_VT(var) = VT_BSTR;
3105
        V_BSTR(var) = This->properties->XPath ?
3106 3107 3108
                      SysAllocString(PropValueXPathW) :
                      SysAllocString(PropValueXSLPatternW);
        return V_BSTR(var) ? S_OK : E_OUTOFMEMORY;
3109
    }
3110 3111 3112 3113 3114 3115 3116 3117 3118
    else if (lstrcmpiW(p, PropertySelectionNamespacesW) == 0)
    {
        int lenA, lenW;
        BSTR rebuiltStr, cur;
        const xmlChar *nsStr;
        struct list *pNsList;
        select_ns_entry* pNsEntry;

        V_VT(var) = VT_BSTR;
3119 3120 3121
        nsStr = This->properties->selectNsStr;
        pNsList = &This->properties->selectNsList;
        lenA = This->properties->selectNsStr_len;
3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144
        lenW = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)nsStr, lenA+1, NULL, 0);
        rebuiltStr = heap_alloc(lenW*sizeof(WCHAR));
        MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)nsStr, lenA+1, rebuiltStr, lenW);
        cur = rebuiltStr;
        /* this is fine because all of the chars that end tokens are ASCII*/
        LIST_FOR_EACH_ENTRY(pNsEntry, pNsList, select_ns_entry, entry)
        {
            while (*cur != 0) ++cur;
            if (pNsEntry->prefix_end)
            {
                *cur = pNsEntry->prefix_end;
                while (*cur != 0) ++cur;
            }

            if (pNsEntry->href_end)
            {
                *cur = pNsEntry->href_end;
            }
        }
        V_BSTR(var) = SysAllocString(rebuiltStr);
        heap_free(rebuiltStr);
        return S_OK;
    }
3145

3146
    FIXME("Unknown property %s\n", debugstr_w(p));
3147
    return E_FAIL;
3148 3149
}

3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160
static HRESULT WINAPI domdoc_importNode(
    IXMLDOMDocument3* iface,
    IXMLDOMNode* node,
    VARIANT_BOOL deep,
    IXMLDOMNode** clone)
{
    domdoc *This = impl_from_IXMLDOMDocument3( iface );
    FIXME("(%p)->(%p %d %p): stub\n", This, node, deep, clone);
    return E_NOTIMPL;
}

3161
static const struct IXMLDOMDocument3Vtbl XMLDOMDocument3Vtbl =
3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208
{
    domdoc_QueryInterface,
    domdoc_AddRef,
    domdoc_Release,
    domdoc_GetTypeInfoCount,
    domdoc_GetTypeInfo,
    domdoc_GetIDsOfNames,
    domdoc_Invoke,
    domdoc_get_nodeName,
    domdoc_get_nodeValue,
    domdoc_put_nodeValue,
    domdoc_get_nodeType,
    domdoc_get_parentNode,
    domdoc_get_childNodes,
    domdoc_get_firstChild,
    domdoc_get_lastChild,
    domdoc_get_previousSibling,
    domdoc_get_nextSibling,
    domdoc_get_attributes,
    domdoc_insertBefore,
    domdoc_replaceChild,
    domdoc_removeChild,
    domdoc_appendChild,
    domdoc_hasChildNodes,
    domdoc_get_ownerDocument,
    domdoc_cloneNode,
    domdoc_get_nodeTypeString,
    domdoc_get_text,
    domdoc_put_text,
    domdoc_get_specified,
    domdoc_get_definition,
    domdoc_get_nodeTypedValue,
    domdoc_put_nodeTypedValue,
    domdoc_get_dataType,
    domdoc_put_dataType,
    domdoc_get_xml,
    domdoc_transformNode,
    domdoc_selectNodes,
    domdoc_selectSingleNode,
    domdoc_get_parsed,
    domdoc_get_namespaceURI,
    domdoc_get_prefix,
    domdoc_get_baseName,
    domdoc_transformNodeToObject,
    domdoc_get_doctype,
    domdoc_get_implementation,
    domdoc_get_documentElement,
3209
    domdoc_put_documentElement,
3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235
    domdoc_createElement,
    domdoc_createDocumentFragment,
    domdoc_createTextNode,
    domdoc_createComment,
    domdoc_createCDATASection,
    domdoc_createProcessingInstruction,
    domdoc_createAttribute,
    domdoc_createEntityReference,
    domdoc_getElementsByTagName,
    domdoc_createNode,
    domdoc_nodeFromID,
    domdoc_load,
    domdoc_get_readyState,
    domdoc_get_parseError,
    domdoc_get_url,
    domdoc_get_async,
    domdoc_put_async,
    domdoc_abort,
    domdoc_loadXML,
    domdoc_save,
    domdoc_get_validateOnParse,
    domdoc_put_validateOnParse,
    domdoc_get_resolveExternals,
    domdoc_put_resolveExternals,
    domdoc_get_preserveWhiteSpace,
    domdoc_put_preserveWhiteSpace,
3236
    domdoc_put_onreadystatechange,
3237 3238
    domdoc_put_onDataAvailable,
    domdoc_put_onTransformNode,
3239 3240 3241 3242 3243
    domdoc_get_namespaces,
    domdoc_get_schemas,
    domdoc_putref_schemas,
    domdoc_validate,
    domdoc_setProperty,
3244 3245 3246
    domdoc_getProperty,
    domdoc_validateNode,
    domdoc_importNode
3247 3248
};

3249 3250 3251 3252 3253
/* IConnectionPointContainer */
static HRESULT WINAPI ConnectionPointContainer_QueryInterface(IConnectionPointContainer *iface,
                                                              REFIID riid, void **ppv)
{
    domdoc *This = impl_from_IConnectionPointContainer(iface);
3254
    return IXMLDOMDocument3_QueryInterface(&This->IXMLDOMDocument3_iface, riid, ppv);
3255 3256 3257 3258 3259
}

static ULONG WINAPI ConnectionPointContainer_AddRef(IConnectionPointContainer *iface)
{
    domdoc *This = impl_from_IConnectionPointContainer(iface);
3260
    return IXMLDOMDocument3_AddRef(&This->IXMLDOMDocument3_iface);
3261 3262 3263 3264 3265
}

static ULONG WINAPI ConnectionPointContainer_Release(IConnectionPointContainer *iface)
{
    domdoc *This = impl_from_IConnectionPointContainer(iface);
3266
    return IXMLDOMDocument3_Release(&This->IXMLDOMDocument3_iface);
3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277
}

static HRESULT WINAPI ConnectionPointContainer_EnumConnectionPoints(IConnectionPointContainer *iface,
        IEnumConnectionPoints **ppEnum)
{
    domdoc *This = impl_from_IConnectionPointContainer(iface);
    FIXME("(%p)->(%p): stub\n", This, ppEnum);
    return E_NOTIMPL;
}

static HRESULT WINAPI ConnectionPointContainer_FindConnectionPoint(IConnectionPointContainer *iface,
3278
        REFIID riid, IConnectionPoint **cp)
3279 3280
{
    domdoc *This = impl_from_IConnectionPointContainer(iface);
3281 3282 3283 3284 3285 3286 3287 3288 3289
    ConnectionPoint *iter;

    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), cp);

    *cp = NULL;

    for(iter = This->cp_list; iter; iter = iter->next)
    {
        if (IsEqualGUID(iter->iid, riid))
3290
            *cp = &iter->IConnectionPoint_iface;
3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301
    }

    if (*cp)
    {
        IConnectionPoint_AddRef(*cp);
        return S_OK;
    }

    FIXME("unsupported riid %s\n", debugstr_guid(riid));
    return CONNECT_E_NOCONNECTION;

3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312
}

static const struct IConnectionPointContainerVtbl ConnectionPointContainerVtbl =
{
    ConnectionPointContainer_QueryInterface,
    ConnectionPointContainer_AddRef,
    ConnectionPointContainer_Release,
    ConnectionPointContainer_EnumConnectionPoints,
    ConnectionPointContainer_FindConnectionPoint
};

3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350
/* IConnectionPoint */
static HRESULT WINAPI ConnectionPoint_QueryInterface(IConnectionPoint *iface,
                                                     REFIID riid, void **ppv)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);

    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv );

    *ppv = NULL;

    if (IsEqualGUID(&IID_IUnknown, riid) ||
        IsEqualGUID(&IID_IConnectionPoint, riid))
    {
        *ppv = iface;
    }

    if (*ppv)
    {
        IConnectionPoint_AddRef(iface);
        return S_OK;
    }

    WARN("Unsupported interface %s\n", debugstr_guid(riid));
    return E_NOINTERFACE;
}

static ULONG WINAPI ConnectionPoint_AddRef(IConnectionPoint *iface)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
    return IConnectionPointContainer_AddRef(This->container);
}

static ULONG WINAPI ConnectionPoint_Release(IConnectionPoint *iface)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
    return IConnectionPointContainer_Release(This->container);
}

3351
static HRESULT WINAPI ConnectionPoint_GetConnectionInterface(IConnectionPoint *iface, IID *iid)
3352 3353
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
3354 3355 3356 3357 3358 3359 3360

    TRACE("(%p)->(%p)\n", This, iid);

    if (!iid) return E_POINTER;

    *iid = *This->iid;
    return S_OK;
3361 3362 3363
}

static HRESULT WINAPI ConnectionPoint_GetConnectionPointContainer(IConnectionPoint *iface,
3364
        IConnectionPointContainer **container)
3365 3366
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
3367 3368 3369 3370 3371 3372 3373 3374

    TRACE("(%p)->(%p)\n", This, container);

    if (!container) return E_POINTER;

    *container = This->container;
    IConnectionPointContainer_AddRef(*container);
    return S_OK;
3375 3376
}

3377 3378
static HRESULT WINAPI ConnectionPoint_Advise(IConnectionPoint *iface, IUnknown *unk_sink,
                                             DWORD *cookie)
3379 3380
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
3381 3382
    IUnknown *sink;
    HRESULT hr;
3383
    DWORD i;
3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413

    TRACE("(%p)->(%p %p)\n", This, unk_sink, cookie);

    hr = IUnknown_QueryInterface(unk_sink, This->iid, (void**)&sink);
    if(FAILED(hr) && !IsEqualGUID(&IID_IPropertyNotifySink, This->iid))
        hr = IUnknown_QueryInterface(unk_sink, &IID_IDispatch, (void**)&sink);
    if(FAILED(hr))
        return CONNECT_E_CANNOTCONNECT;

    if(This->sinks)
    {
        for (i = 0; i < This->sinks_size; i++)
            if (!This->sinks[i].unk)
                break;

        if (i == This->sinks_size)
            This->sinks = heap_realloc(This->sinks,(++This->sinks_size)*sizeof(*This->sinks));
    }
    else
    {
        This->sinks = heap_alloc(sizeof(*This->sinks));
        This->sinks_size = 1;
        i = 0;
    }

    This->sinks[i].unk = sink;
    if (cookie)
        *cookie = i+1;

    return S_OK;
3414 3415
}

3416
static HRESULT WINAPI ConnectionPoint_Unadvise(IConnectionPoint *iface, DWORD cookie)
3417 3418
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
3419 3420 3421 3422 3423 3424 3425 3426 3427 3428

    TRACE("(%p)->(%d)\n", This, cookie);

    if (cookie == 0 || cookie > This->sinks_size || !This->sinks[cookie-1].unk)
        return CONNECT_E_NOCONNECTION;

    IUnknown_Release(This->sinks[cookie-1].unk);
    This->sinks[cookie-1].unk = NULL;

    return S_OK;
3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450
}

static HRESULT WINAPI ConnectionPoint_EnumConnections(IConnectionPoint *iface,
                                                      IEnumConnections **ppEnum)
{
    ConnectionPoint *This = impl_from_IConnectionPoint(iface);
    FIXME("(%p)->(%p): stub\n", This, ppEnum);
    return E_NOTIMPL;
}

static const IConnectionPointVtbl ConnectionPointVtbl =
{
    ConnectionPoint_QueryInterface,
    ConnectionPoint_AddRef,
    ConnectionPoint_Release,
    ConnectionPoint_GetConnectionInterface,
    ConnectionPoint_GetConnectionPointContainer,
    ConnectionPoint_Advise,
    ConnectionPoint_Unadvise,
    ConnectionPoint_EnumConnections
};

3451
static void ConnectionPoint_Init(ConnectionPoint *cp, struct domdoc *doc, REFIID riid)
3452
{
3453
    cp->IConnectionPoint_iface.lpVtbl = &ConnectionPointVtbl;
3454 3455
    cp->doc = doc;
    cp->iid = riid;
3456 3457
    cp->sinks = NULL;
    cp->sinks_size = 0;
3458 3459 3460 3461

    cp->next = doc->cp_list;
    doc->cp_list = cp;

3462
    cp->container = &doc->IConnectionPointContainer_iface;
3463 3464
}

3465
/* domdoc implementation of IObjectWithSite */
3466
static HRESULT WINAPI
3467
domdoc_ObjectWithSite_QueryInterface( IObjectWithSite* iface, REFIID riid, void** ppvObject )
3468 3469
{
    domdoc *This = impl_from_IObjectWithSite(iface);
3470
    return IXMLDOMDocument3_QueryInterface(&This->IXMLDOMDocument3_iface, riid, ppvObject);
3471 3472
}

3473
static ULONG WINAPI domdoc_ObjectWithSite_AddRef( IObjectWithSite* iface )
3474 3475
{
    domdoc *This = impl_from_IObjectWithSite(iface);
3476
    return IXMLDOMDocument3_AddRef(&This->IXMLDOMDocument3_iface);
3477 3478
}

3479
static ULONG WINAPI domdoc_ObjectWithSite_Release( IObjectWithSite* iface )
3480 3481
{
    domdoc *This = impl_from_IObjectWithSite(iface);
3482
    return IXMLDOMDocument3_Release(&This->IXMLDOMDocument3_iface);
3483 3484
}

3485
static HRESULT WINAPI domdoc_ObjectWithSite_GetSite( IObjectWithSite *iface, REFIID iid, void **ppvSite )
3486 3487 3488
{
    domdoc *This = impl_from_IObjectWithSite(iface);

3489
    TRACE("(%p)->(%s %p)\n", This, debugstr_guid( iid ), ppvSite );
3490 3491 3492 3493 3494 3495 3496

    if ( !This->site )
        return E_FAIL;

    return IUnknown_QueryInterface( This->site, iid, ppvSite );
}

3497
static HRESULT WINAPI domdoc_ObjectWithSite_SetSite( IObjectWithSite *iface, IUnknown *punk )
3498 3499 3500
{
    domdoc *This = impl_from_IObjectWithSite(iface);

3501
    TRACE("(%p)->(%p)\n", iface, punk);
3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513

    if(!punk)
    {
        if(This->site)
        {
            IUnknown_Release( This->site );
            This->site = NULL;
        }

        return S_OK;
    }

3514
    IUnknown_AddRef( punk );
3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525

    if(This->site)
        IUnknown_Release( This->site );

    This->site = punk;

    return S_OK;
}

static const IObjectWithSiteVtbl domdocObjectSite =
{
3526 3527 3528 3529 3530
    domdoc_ObjectWithSite_QueryInterface,
    domdoc_ObjectWithSite_AddRef,
    domdoc_ObjectWithSite_Release,
    domdoc_ObjectWithSite_SetSite,
    domdoc_ObjectWithSite_GetSite
3531 3532
};

3533
static HRESULT WINAPI domdoc_Safety_QueryInterface(IObjectSafety *iface, REFIID riid, void **ppv)
3534 3535
{
    domdoc *This = impl_from_IObjectSafety(iface);
3536
    return IXMLDOMDocument3_QueryInterface(&This->IXMLDOMDocument3_iface, riid, ppv);
3537 3538
}

3539
static ULONG WINAPI domdoc_Safety_AddRef(IObjectSafety *iface)
3540 3541
{
    domdoc *This = impl_from_IObjectSafety(iface);
3542
    return IXMLDOMDocument3_AddRef(&This->IXMLDOMDocument3_iface);
3543 3544
}

3545
static ULONG WINAPI domdoc_Safety_Release(IObjectSafety *iface)
3546 3547
{
    domdoc *This = impl_from_IObjectSafety(iface);
3548
    return IXMLDOMDocument3_Release(&This->IXMLDOMDocument3_iface);
3549 3550
}

3551
#define SAFETY_SUPPORTED_OPTIONS (INTERFACESAFE_FOR_UNTRUSTED_CALLER|INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_SECURITY_MANAGER)
3552

3553 3554
static HRESULT WINAPI domdoc_Safety_GetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
        DWORD *supported, DWORD *enabled)
3555 3556 3557
{
    domdoc *This = impl_from_IObjectSafety(iface);

3558
    TRACE("(%p)->(%s %p %p)\n", This, debugstr_guid(riid), supported, enabled);
3559

3560
    if(!supported || !enabled) return E_POINTER;
3561

3562 3563
    *supported = SAFETY_SUPPORTED_OPTIONS;
    *enabled = This->safeopt;
3564 3565 3566 3567

    return S_OK;
}

3568 3569
static HRESULT WINAPI domdoc_Safety_SetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid,
        DWORD mask, DWORD enabled)
3570 3571
{
    domdoc *This = impl_from_IObjectSafety(iface);
3572
    TRACE("(%p)->(%s %x %x)\n", This, debugstr_guid(riid), mask, enabled);
3573

3574
    if ((mask & ~SAFETY_SUPPORTED_OPTIONS) != 0)
3575 3576
        return E_FAIL;

3577 3578
    This->safeopt = (This->safeopt & ~mask) | (mask & enabled);

3579 3580 3581
    return S_OK;
}

3582 3583
#undef SAFETY_SUPPORTED_OPTIONS

3584
static const IObjectSafetyVtbl domdocObjectSafetyVtbl = {
3585 3586 3587 3588 3589
    domdoc_Safety_QueryInterface,
    domdoc_Safety_AddRef,
    domdoc_Safety_Release,
    domdoc_Safety_GetInterfaceSafetyOptions,
    domdoc_Safety_SetInterfaceSafetyOptions
3590 3591
};

3592
static const tid_t domdoc_iface_tids[] = {
3593
    IXMLDOMDocument3_tid,
3594 3595
    0
};
3596

3597 3598
static dispex_static_data_t domdoc_dispex = {
    NULL,
3599
    IXMLDOMDocument3_tid,
3600 3601 3602 3603
    NULL,
    domdoc_iface_tids
};

3604
HRESULT get_domdoc_from_xmldoc(xmlDocPtr xmldoc, IXMLDOMDocument3 **document)
3605 3606 3607
{
    domdoc *doc;

3608
    doc = heap_alloc( sizeof (*doc) );
3609 3610 3611
    if( !doc )
        return E_OUTOFMEMORY;

3612
    doc->IXMLDOMDocument3_iface.lpVtbl = &XMLDOMDocument3Vtbl;
3613 3614 3615 3616
    doc->IPersistStreamInit_iface.lpVtbl = &xmldoc_IPersistStreamInit_VTable;
    doc->IObjectWithSite_iface.lpVtbl = &domdocObjectSite;
    doc->IObjectSafety_iface.lpVtbl = &domdocObjectSafetyVtbl;
    doc->IConnectionPointContainer_iface.lpVtbl = &ConnectionPointContainerVtbl;
3617
    doc->ref = 1;
3618
    doc->async = VARIANT_TRUE;
3619 3620
    doc->validating = 0;
    doc->resolving = 0;
3621
    doc->properties = properties_from_xmlDocPtr(xmldoc);
3622
    doc->error = S_OK;
3623
    doc->site = NULL;
3624
    doc->safeopt = 0;
3625
    doc->cp_list = NULL;
3626
    doc->namespaces = NULL;
3627
    memset(doc->events, 0, sizeof(doc->events));
3628 3629 3630 3631 3632

    /* events connection points */
    ConnectionPoint_Init(&doc->cp_dispatch, doc, &IID_IDispatch);
    ConnectionPoint_Init(&doc->cp_propnotif, doc, &IID_IPropertyNotifySink);
    ConnectionPoint_Init(&doc->cp_domdocevents, doc, &DIID_XMLDOMDocumentEvents);
3633

3634 3635
    init_xmlnode(&doc->node, (xmlNodePtr)xmldoc, (IXMLDOMNode*)&doc->IXMLDOMDocument3_iface,
            &domdoc_dispex);
3636

3637
    *document = &doc->IXMLDOMDocument3_iface;
3638

3639
    TRACE("returning iface %p\n", *document);
3640 3641 3642
    return S_OK;
}

3643
HRESULT DOMDocument_create(MSXML_VERSION version, void **ppObj)
3644 3645 3646 3647
{
    xmlDocPtr xmldoc;
    HRESULT hr;

3648
    TRACE("(%d, %p)\n", version, ppObj);
3649 3650 3651 3652 3653

    xmldoc = xmlNewDoc(NULL);
    if(!xmldoc)
        return E_OUTOFMEMORY;

3654
    xmldoc_init(xmldoc, version);
3655

3656
    hr = get_domdoc_from_xmldoc(xmldoc, (IXMLDOMDocument3**)ppObj);
3657
    if(FAILED(hr))
3658
    {
3659 3660
        free_properties(properties_from_xmlDocPtr(xmldoc));
        heap_free(xmldoc->_private);
3661
        xmlFreeDoc(xmldoc);
3662 3663
        return hr;
    }
3664 3665 3666 3667

    return hr;
}

3668 3669
IUnknown* create_domdoc( xmlNodePtr document )
{
3670
    IUnknown *obj = NULL;
3671 3672 3673 3674
    HRESULT hr;

    TRACE("(%p)\n", document);

3675
    hr = get_domdoc_from_xmldoc((xmlDocPtr)document, (IXMLDOMDocument3**)&obj);
3676 3677 3678
    if (FAILED(hr))
        return NULL;

3679
    return obj;
3680 3681
}

3682 3683
#else

3684
HRESULT DOMDocument_create(MSXML_VERSION version, void **ppObj)
3685 3686 3687 3688 3689 3690 3691
{
    MESSAGE("This program tried to use a DOMDocument object, but\n"
            "libxml2 support was not present at compile time.\n");
    return E_NOTIMPL;
}

#endif