/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications.
 * Portions created by the Initial Developer are Copyright (C) 2001
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Vidur Apparao <vidur@netscape.com> (original author)
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsSchemaPrivate.h"
#include "nsSchemaLoader.h"
#include "nsISVSchemaErrorHandler.h"

// content includes
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOM3Node.h"

// loading includes
#include "nsIXMLHttpRequest.h"
#include "nsIDOMEvent.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMEventTarget.h"
#include "nsNetUtil.h"
#include "nsIParserService.h"

// XPConnect includes
#include "nsIXPConnect.h"
#include "nsIScriptSecurityManager.h"
#include "nsIPrincipal.h"

// XPCOM includes
#include "nsIServiceManager.h"
#include "nsIComponentManager.h"
#include "nsStaticAtom.h"
#include "nsIAtomService.h"

////////////////////////////////////////////////////////////
//
// nsSchemaAtoms implementation
//
////////////////////////////////////////////////////////////

// define storage for all atoms
#define SCHEMA_ATOM(_name, _value) nsIAtom* nsSchemaAtoms::_name;
#include "nsSchemaAtomList.h"
#undef SCHEMA_ATOM

static const nsStaticAtom atomInfo[] = {
#define SCHEMA_ATOM(_name, _value) { _value, &nsSchemaAtoms::_name },
#include "nsSchemaAtomList.h"
#undef SCHEMA_ATOM
};

nsresult
nsSchemaAtoms::AddRefAtoms()
{
  nsresult rv;
  PRUint32 numAtoms = NS_ARRAY_LENGTH(atomInfo);
  nsCOMPtr<nsIAtomService> atomServ = do_GetService(NS_ATOMSERVICE_CONTRACTID,
                                                    &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  for (PRUint32 i = 0; i < numAtoms; ++i) {
    atomServ->GetPermanentAtomUTF8(atomInfo[i].mString,
                                   atomInfo[i].mAtom);
  }

  return NS_OK;
}

////////////////////////////////////////////////////////////
//
// LoadListener implementation
//
////////////////////////////////////////////////////////////

class LoadListener : public nsIDOMEventListener {
public:
  LoadListener(nsSchemaLoader* aLoader,
               nsISVSchemaLoadListener* aListener,
               nsIXMLHttpRequest* aRequest);
  virtual ~LoadListener();

  NS_DECL_ISUPPORTS
  NS_DECL_NSIDOMEVENTLISTENER

private:
  nsSchemaLoader* mLoader;
  nsCOMPtr<nsISVSchemaLoadListener> mListener;
  nsCOMPtr<nsIXMLHttpRequest> mRequest;
  nsString mURI;
};

LoadListener::LoadListener(nsSchemaLoader* aLoader,
                           nsISVSchemaLoadListener* aListener,
                           nsIXMLHttpRequest* aRequest)
{
  mLoader = aLoader;
  NS_ADDREF(mLoader);
  mListener = aListener;
  mRequest = aRequest;
}
  
LoadListener::~LoadListener()
{
  NS_IF_RELEASE(mLoader);
}

NS_IMPL_ISUPPORTS1(LoadListener, nsIDOMEventListener)

/* void handleEvent (in nsIDOMEvent event); */
NS_IMETHODIMP
LoadListener::HandleEvent(nsIDOMEvent *event)
{
  nsresult rv;

  PRUint32 httpStatus;
  mRequest->GetStatus(&httpStatus);

  nsCOMPtr<nsISVSchema> schema;

  nsAutoString eventType;
  event->GetType(eventType);

  PRBool succeeded = (httpStatus / 100 == 2);

  // if we loaded fine, and not http/https, we assume success in loaded the file.
  if (!succeeded && eventType.EqualsLiteral("load")) {
    nsCOMPtr<nsIChannel> channel;
    mRequest->GetChannel(getter_AddRefs(channel));
    if (channel) {
      nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));

      // if qi to httpChannel fails, it isn't a http:// or https:// request
      if (!httpChannel) {
        succeeded = PR_TRUE;
      }
    }
  }

  if (succeeded && eventType.EqualsLiteral("load")) {
    nsCOMPtr<nsIDOMDocument> document;

    rv = mRequest->GetResponseXML(getter_AddRefs(document));
    if (NS_SUCCEEDED(rv)) {
      nsCOMPtr<nsIDOMElement> element;

      if (document)
        document->GetDocumentElement(getter_AddRefs(element));

      //XXXTelemac TODO Use an nsISVSchemaErrorHandler instead of nsnull
      if (element)
        rv = mLoader->ProcessSchemaElement(element, nsnull, getter_AddRefs(schema));
      else
        rv = NS_ERROR_SCHEMA_NOT_SCHEMA_ELEMENT;
    }
  }
  else {
    rv = NS_ERROR_SCHEMA_LOADING_ERROR;
  }

  //XXXTelemac OnError call replace by use of nsISVSchemaErrorHandler 
  //XXXTelemac in sub-processing methods.

  if (mListener) {
    if (NS_SUCCEEDED(rv))
      mListener->OnLoad(schema);
    else
      mListener->OnError(rv, NS_LITERAL_STRING("Failure loading"));
  }

  NS_IF_RELEASE(mLoader);
  mListener = nsnull;
  mRequest = nsnull;

  return NS_OK;
}

////////////////////////////////////////////////////////////
//
// nsBuiltinSchemaCollection implementation
//
////////////////////////////////////////////////////////////
nsBuiltinSchemaCollection::nsBuiltinSchemaCollection()
{
}

nsresult
nsBuiltinSchemaCollection::Init()
{
  return (mBuiltinTypesHash.Init() && mSOAPTypeHash.Init()) ? NS_OK
                                                            : NS_ERROR_FAILURE;
}

NS_IMPL_ISUPPORTS1(nsBuiltinSchemaCollection,
                   nsISVSchemaCollection)

/* nsISVSchema getSchema (in AString targetNamespace); */
NS_IMETHODIMP
nsBuiltinSchemaCollection::GetSchema(const nsAString & targetNamespace,
                                     nsISVSchema **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);
  *_retval = nsnull;
  return NS_ERROR_SCHEMA_UNKNOWN_TARGET_NAMESPACE;
}

/* nsISVSchemaElement getElement (in AString name, in AString namespace); */
NS_IMETHODIMP
nsBuiltinSchemaCollection::GetElement(const nsAString & aName,
                                      const nsAString & aNamespace,
                                      nsISVSchemaElement **_retval)
{
  if (aNamespace.IsEmpty()) {
    NS_WARNING("nsSchemaLoader::GetSchema(nsAString,nsISVSchema): "
               "Invalid |targetNamespace| is empty");
    
    return NS_ERROR_INVALID_ARG;
  }

  NS_ENSURE_ARG_POINTER(_retval);
  *_retval = nsnull;
  return NS_ERROR_FAILURE;
}

/* nsISVSchemaAttribute getAttribute (in AString name, in AString namespace); */
NS_IMETHODIMP
nsBuiltinSchemaCollection::GetAttribute(const nsAString & aName,
                                        const nsAString & aNamespace,
                                        nsISVSchemaAttribute **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);
  *_retval = nsnull;
  return NS_ERROR_FAILURE;
}

static PRBool
IsSchemaNamespace(const nsAString& aNamespace)
{
  if (aNamespace.EqualsLiteral(NS_SCHEMA_2001_NAMESPACE) ||
      aNamespace.EqualsLiteral(NS_SCHEMA_1999_NAMESPACE)) {
    return PR_TRUE;
  }
  else {
    return PR_FALSE;
  }
}

static PRBool
IsSOAPNamespace(const nsAString& aNamespace)
{
  if (aNamespace.EqualsLiteral(NS_SOAP_1_1_ENCODING_NAMESPACE) ||
      aNamespace.EqualsLiteral(NS_SOAP_1_2_ENCODING_NAMESPACE)) {
    return PR_TRUE;
  }
  else {
    return PR_FALSE;
  }
}

/* nsISVSchemaType getType (in AString name, in AString namespace); */
NS_IMETHODIMP
nsBuiltinSchemaCollection::GetType(const nsAString & aName,
                                   const nsAString & aNamespace,
                                   nsISVSchemaType **_retval)
{
  if (IsSchemaNamespace(aNamespace)) {
    return GetBuiltinType(aName, aNamespace, _retval);
  }

  if (IsSOAPNamespace(aNamespace)) {
    return GetSOAPType(aName, aNamespace, _retval);
  }
  
  return NS_ERROR_SCHEMA_UNKNOWN_TYPE;
}

nsresult
nsBuiltinSchemaCollection::GetBuiltinType(const nsAString& aName,
                                          const nsAString& aNamespace,
                                          nsISVSchemaType** aType)
{
  if (!mBuiltinTypesHash.Get(aName, aType)) {
    nsresult rv;
    nsCOMPtr<nsIAtomService> atomServ =
      do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr<nsIAtom> typeName;
    rv = atomServ->GetAtom(aName.BeginReading(), getter_AddRefs(typeName));
    NS_ENSURE_SUCCESS(rv, rv);
    PRUint16 typeVal;
    if (typeName == nsSchemaAtoms::sAnyType_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_ANYTYPE;
    }
    else if (typeName == nsSchemaAtoms::sString_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_STRING;
    }
    else if (typeName == nsSchemaAtoms::sNormalizedString_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING;
    }
    else if (typeName == nsSchemaAtoms::sToken_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_TOKEN;
    }
    else if (typeName == nsSchemaAtoms::sByte_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_BYTE;
    }
    else if (typeName == nsSchemaAtoms::sUnsignedByte_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE;
    }
    else if (typeName == nsSchemaAtoms::sBase64Binary_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_BASE64BINARY;
    }
    else if (typeName == nsSchemaAtoms::sHexBinary_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_HEXBINARY;
    }
    else if (typeName == nsSchemaAtoms::sInteger_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_INTEGER;
    }
    else if (typeName == nsSchemaAtoms::sPositiveInteger_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER;
    }
    else if (typeName == nsSchemaAtoms::sNegativeInteger_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER;
    }
    else if (typeName == nsSchemaAtoms::sNonnegativeInteger_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER;
    }
    else if (typeName == nsSchemaAtoms::sNonpositiveInteger_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER;
    }
    else if (typeName == nsSchemaAtoms::sInt_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_INT;
    }
    else if (typeName == nsSchemaAtoms::sUnsignedInt_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT;
    }
    else if (typeName == nsSchemaAtoms::sLong_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_LONG;
    }
    else if (typeName == nsSchemaAtoms::sUnsignedLong_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG;
    }
    else if (typeName == nsSchemaAtoms::sShort_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_SHORT;
    }
    else if (typeName == nsSchemaAtoms::sUnsignedShort_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT;
    }
    else if (typeName == nsSchemaAtoms::sDecimal_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_DECIMAL;
    }
    else if (typeName == nsSchemaAtoms::sFloat_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_FLOAT;
    }
    else if (typeName == nsSchemaAtoms::sDouble_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_DOUBLE;
    }
    else if (typeName == nsSchemaAtoms::sBoolean_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_BOOLEAN;
    }
    else if (typeName == nsSchemaAtoms::sTime_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_TIME;
    }
    else if (typeName == nsSchemaAtoms::sDateTime_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_DATETIME;
    }
    else if (typeName == nsSchemaAtoms::sDuration_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_DURATION;
    }
    else if (typeName == nsSchemaAtoms::sDate_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_DATE;
    }
    else if (typeName == nsSchemaAtoms::sGMonth_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_GMONTH;
    }
    else if (typeName == nsSchemaAtoms::sGYear_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_GYEAR;
    }
    else if (typeName == nsSchemaAtoms::sGYearMonth_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_GYEARMONTH;
    }
    else if (typeName == nsSchemaAtoms::sGDay_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_GDAY;
    }
    else if (typeName == nsSchemaAtoms::sGMonthDay_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_GMONTHDAY;
    }
    else if (typeName == nsSchemaAtoms::sName_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NAME;
    }
    else if (typeName == nsSchemaAtoms::sQName_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_QNAME;
    }
    else if (typeName == nsSchemaAtoms::sNCName_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NCNAME;
    }
    else if (typeName == nsSchemaAtoms::sAnyURI_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_ANYURI;
    }
    else if (typeName == nsSchemaAtoms::sLanguage_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_LANGUAGE;
    }
    else if (typeName == nsSchemaAtoms::sID_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_ID;
    }
    else if (typeName == nsSchemaAtoms::sIDREF_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_IDREF;
    }
    else if (typeName == nsSchemaAtoms::sIDREFS_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_IDREFS;
    }
    else if (typeName == nsSchemaAtoms::sENTITY_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_ENTITY;
    }
    else if (typeName == nsSchemaAtoms::sENTITIES_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_ENTITIES;
    }
    else if (typeName == nsSchemaAtoms::sNOTATION_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NOTATION;
    }
    else if (typeName == nsSchemaAtoms::sNMTOKEN_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NMTOKEN;
    }
    else if (typeName == nsSchemaAtoms::sNMTOKENS_atom) {
      typeVal = nsISVSchemaBuiltinType::BUILTIN_TYPE_NMTOKENS;
    }
    else {
      NS_ERROR("Unknown builtin type");
      return NS_ERROR_SCHEMA_UNKNOWN_TYPE;
    }

    nsCOMPtr<nsISVSchemaType> builtin = new nsSchemaBuiltinType(typeVal);
    if (!builtin) {
      return NS_ERROR_OUT_OF_MEMORY;
    }

    mBuiltinTypesHash.Put(aName, builtin);
    builtin.swap(*aType);
  }

  return NS_OK;
}

nsresult
nsBuiltinSchemaCollection::GetSOAPType(const nsAString& aName,
                                       const nsAString& aNamespace,
                                       nsISVSchemaType** aType)
{
  nsresult rv = NS_OK;

  if (!mSOAPTypeHash.Get(aName, aType)) {
    if (aName.EqualsLiteral("Array")) {
      nsCOMPtr<nsISVSchemaType> anyType;
      rv = GetBuiltinType(NS_LITERAL_STRING("anyType"),
                          NS_LITERAL_STRING(NS_SCHEMA_2001_NAMESPACE),
                          getter_AddRefs(anyType));
      if (NS_FAILED(rv)) {
        return rv;
      }

      nsSOAPArray* array = new nsSOAPArray(anyType);
      if (!array) {
        return NS_ERROR_OUT_OF_MEMORY;
      }

      mSOAPTypeHash.Put(aName, array);

      *aType = array;
      NS_ADDREF(*aType);
    }
    else if (aName.EqualsLiteral("arrayType")) {
      nsSOAPArrayType* arrayType = new nsSOAPArrayType();
      if (!arrayType) {
        return NS_ERROR_OUT_OF_MEMORY;
      }

      mSOAPTypeHash.Put(aName, arrayType);

      *aType = arrayType;
      NS_ADDREF(*aType);
    }
    else {
      rv = NS_ERROR_SCHEMA_UNKNOWN_TYPE;
    }
  }

  return rv;
}

////////////////////////////////////////////////////////////
//
// nsSchemaLoader implementation
//
////////////////////////////////////////////////////////////

nsSchemaLoader::nsSchemaLoader()
{
  mBuiltinCollection = do_GetService(NS_SVBUILTINSCHEMACOLLECTION_CONTRACTID);
}

nsresult
nsSchemaLoader::Init()
{
  return mSchemas.Init() ? NS_OK : NS_ERROR_FAILURE;
}

NS_IMPL_ISUPPORTS2(nsSchemaLoader,
                   nsISVSchemaLoader,
                   nsISVSchemaCollection)


/* nsISVSchema getSchema (in AString targetNamespace); */
NS_IMETHODIMP
nsSchemaLoader::GetSchema(const nsAString & targetNamespace,
                          nsISVSchema ** aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);

  return mSchemas.Get(targetNamespace, aResult) ? NS_OK :
           NS_ERROR_SCHEMA_UNKNOWN_TARGET_NAMESPACE;
}

/* nsISVSchemaElement getElement (in AString name, in AString namespace); */
NS_IMETHODIMP
nsSchemaLoader::GetElement(const nsAString & aName,
                           const nsAString & aNamespace,
                           nsISVSchemaElement **_retval)
{
  nsCOMPtr<nsISVSchema> schema;
  nsresult rv = GetSchema(aNamespace, getter_AddRefs(schema));
  if (NS_FAILED(rv)) {
    return rv;
  }

  return schema->GetElementByName(aName, _retval);
}

/* nsISVSchemaAttribute getAttribute (in AString name, in AString namespace); */
NS_IMETHODIMP
nsSchemaLoader::GetAttribute(const nsAString & aName,
                             const nsAString & aNamespace,
                             nsISVSchemaAttribute **_retval)
{
  nsCOMPtr<nsISVSchema> schema;
  nsresult rv = GetSchema(aNamespace, getter_AddRefs(schema));
  if (NS_FAILED(rv)) {
    return rv;
  }

  return schema->GetAttributeByName(aName, _retval);
}

/* nsISVSchemaType getType (in AString name, in AString namespace); */
NS_IMETHODIMP
nsSchemaLoader::GetType(const nsAString & aName,
                        const nsAString & aNamespace,
                        nsISVSchemaType **_retval)
{
  nsresult rv = NS_OK;

  if (IsSchemaNamespace(aNamespace) || IsSOAPNamespace(aNamespace)) {
    rv = mBuiltinCollection->GetType(aName, aNamespace, _retval);

    if (NS_FAILED(rv)) {
      nsAutoString errorMsg(NS_LITERAL_STRING("nsSchemaLoader::GetType: "));
      errorMsg.AppendLiteral("Failure processing schema: cannot get schema type \"");
      errorMsg.Append(aName);
      errorMsg.AppendLiteral("\"");
      NS_ERROR(NS_ConvertUTF16toUTF8(errorMsg).get());

      return rv;
    }

    return NS_OK;
  }

  nsCOMPtr<nsISVSchema> schema;
  rv = GetSchema(aNamespace, getter_AddRefs(schema));
  if (NS_FAILED(rv)) {
    return rv;
  }

  rv = schema->GetTypeByName(aName, _retval);

  if (NS_FAILED(rv)) {
    nsAutoString msg(NS_LITERAL_STRING("nsSchemaLoader::GetType: "));
    msg.AppendLiteral("Failure processing schema: ");
    msg.AppendLiteral("cannot get schema type \"");
    msg.Append(aName);
    msg.AppendLiteral("\"");
    NS_ERROR(NS_ConvertUTF16toUTF8(msg).get());

    return rv;
  }

  return NS_OK;
}

nsresult
nsSchemaLoader::GetResolvedURI(const nsAString& aSchemaURI,
                               const char* aMethod,
                               nsIURI** aURI)
{
  nsresult rv;
  nsAXPCNativeCallContext *cc = nsnull;
  nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
  if (NS_SUCCEEDED(rv)) {
    rv = xpc->GetCurrentNativeCallContext(&cc);
  }

  if (NS_SUCCEEDED(rv) && cc) {
    JSContext* cx;
    rv = cc->GetJSContext(&cx);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIScriptSecurityManager> secMan(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIURI> baseURI;
    nsCOMPtr<nsIPrincipal> principal;
    rv = secMan->GetSubjectPrincipal(getter_AddRefs(principal));
    if (NS_SUCCEEDED(rv)) {
      principal->GetURI(getter_AddRefs(baseURI));
    }

    rv = NS_NewURI(aURI, aSchemaURI, nsnull, baseURI);
    if (NS_FAILED(rv)) return rv;

    rv = secMan->CheckLoadURIFromScript(cx, *aURI);
    if (NS_FAILED(rv))
    {
      // Security check failed. The above call set a JS exception. The
      // following lines ensure that the exception is propagated.
      cc->SetExceptionWasThrown(PR_TRUE);
      return rv;
    }
  }
  else {
    rv = NS_NewURI(aURI, aSchemaURI, nsnull);
    if (NS_FAILED(rv)) return rv;
  }

  return NS_OK;
}

/* nsISVSchema load (in AString schemaURI); */
NS_IMETHODIMP
nsSchemaLoader::Load(const nsAString& schemaURI,
                     nsISVSchema **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);

  nsCOMPtr<nsIDOMDocument> document;
  nsresult rv = GetDocumentFromURI(schemaURI, getter_AddRefs(document));
  NS_ENSURE_SUCCESS(rv, rv);

  if (!document)
    return NS_ERROR_SCHEMA_LOADING_ERROR;

  nsCOMPtr<nsIDOMElement> element;
  document->GetDocumentElement(getter_AddRefs(element));
  if (element) {
    //XXXTelemac TODO Have an error handler there instead or nsnull
    rv = ProcessSchemaElement(element, nsnull, _retval);
  }
  else {
    rv = NS_ERROR_SCHEMA_NOT_SCHEMA_ELEMENT;
  }

  return rv;
}

/* [noscript] void loadAsyncWithPrincipal (in AString schemaURI,
 *                                         in nsISVSchemaLoadListener listener,
 *                                         in nsIPrincipal principal);
 */
NS_IMETHODIMP
nsSchemaLoader::LoadAsyncWithPrincipal(const nsAString& schemaURI,
                                       nsISVSchemaLoadListener *aListener,
                                       nsIPrincipal *aPrincipal)
{
  NS_ENSURE_ARG(aListener);
  NS_ENSURE_ARG(aPrincipal);

  nsCOMPtr<nsIURI> resolvedURI;
  nsresult rv = GetResolvedURI(schemaURI, "loadAsync", getter_AddRefs(resolvedURI));
  if (NS_FAILED(rv)) {
    return rv;
  }
  nsCAutoString spec;
  resolvedURI->GetSpec(spec);

  nsCOMPtr<nsIXMLHttpRequest> request(do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv));
  if (!request) {
    return rv;
  }

  rv = request->Init(aPrincipal, nsnull, nsnull);
  NS_ENSURE_SUCCESS(rv, rv);

  const nsAString& empty = EmptyString();
  rv = request->OpenRequest(NS_LITERAL_CSTRING("GET"), spec, PR_TRUE, empty,
                            empty);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // Force the mimetype of the returned stream to be xml.
  rv = request->OverrideMimeType(NS_LITERAL_CSTRING("application/xml"));
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsCOMPtr<nsIDOMEventListener> listener;
  LoadListener* listenerInst = new LoadListener(this, aListener, request);
  if (!listenerInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  listener = listenerInst;

  nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(request));
  if (!target) {
    return NS_ERROR_UNEXPECTED;
  }
  
  rv = target->AddEventListener(NS_LITERAL_STRING("load"),
                                listener, PR_FALSE);
  if (NS_FAILED(rv)) {
    return rv;
  }

  rv = target->AddEventListener(NS_LITERAL_STRING("error"),
                                listener, PR_FALSE);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // The listener keeps the request alive until its complete
  rv = request->Send(nsnull);
  
  return rv;
}

/* void loadAsync(in AString schemaURI, in nsISVSchemaLoadListener listener); */
NS_IMETHODIMP
nsSchemaLoader::LoadAsync(const nsAString& aSchemaURI,
                          nsISVSchemaLoadListener *aListener)
{
  // Get the subject principal from the security manager.  We need it to
  // initialize the nsIXMLHttpRequest inside loadAsyncWithPrincipal.
  nsCOMPtr<nsIScriptSecurityManager> secMan =
    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
  NS_ENSURE_STATE(secMan);

  nsCOMPtr<nsIPrincipal> principal;
  nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(principal));
  NS_ENSURE_SUCCESS(rv, rv);
  NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);

  return LoadAsyncWithPrincipal(aSchemaURI, aListener, principal);
}

static const char* kSchemaNamespaces[] = {NS_SCHEMA_1999_NAMESPACE,
                                          NS_SCHEMA_2001_NAMESPACE};
static PRUint32 kSchemaNamespacesLength = sizeof(kSchemaNamespaces) / sizeof(const char*);

/* nsISVSchema processSchemaElement (in nsIDOMElement element, in nsISVSchemaErrorHandler aErrorHandler); */
NS_IMETHODIMP
nsSchemaLoader::ProcessSchemaElement(nsIDOMElement* aElement,
                                     nsISVSchemaErrorHandler* aErrorHandler,
                                     nsISVSchema **aResult)
{
  NS_ENSURE_ARG(aElement);
  NS_ENSURE_ARG_POINTER(aResult);

  nsRefPtr<nsSchema> schemaInst = new nsSchema(this, aElement);
  if (!schemaInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  nsresult rv = schemaInst->Init();
  NS_ENSURE_SUCCESS(rv, rv);

  nsAutoString targetNamespace;
  schemaInst->GetTargetNamespace(targetNamespace);

  nsISVSchema * os;
  if (mSchemas.Get(targetNamespace, &os)) {
    *aResult = os;
    return NS_OK;
  }

  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces, kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  // For now, ignore the following
  // annotations
  // redefine
  // notation
  // identity-constraint elements

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sElement_atom) {
      nsCOMPtr<nsISVSchemaElement> schemaElement;
      rv = ProcessElement(aErrorHandler, schemaInst, childElement,
                          getter_AddRefs(schemaElement));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddElement(schemaElement);
      }
    }
    else if (tagName == nsSchemaAtoms::sComplexType_atom) {
      nsCOMPtr<nsISVSchemaComplexType> complexType;
      rv = ProcessComplexType(aErrorHandler, schemaInst, childElement,
                              getter_AddRefs(complexType));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddType(complexType);
      }
    }
    else if (tagName == nsSchemaAtoms::sSimpleType_atom) {
      nsCOMPtr<nsISVSchemaSimpleType> simpleType;
      rv = ProcessSimpleType(aErrorHandler, schemaInst, childElement,
                             getter_AddRefs(simpleType));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddType(simpleType);
      }
    }
    else if (tagName == nsSchemaAtoms::sAttribute_atom) {
      nsCOMPtr<nsISVSchemaAttribute> attribute;
      rv = ProcessAttribute(aErrorHandler, schemaInst, childElement,
                            getter_AddRefs(attribute));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddAttribute(attribute);
      }
    }
    else if (tagName == nsSchemaAtoms::sAttributeGroup_atom) {
      nsCOMPtr<nsISVSchemaAttributeGroup> attributeGroup;
      rv = ProcessAttributeGroup(aErrorHandler, schemaInst, childElement,
                                 getter_AddRefs(attributeGroup));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddAttributeGroup(attributeGroup);
      }
    }
    else if (tagName == nsSchemaAtoms::sModelGroup_atom) {
      nsCOMPtr<nsISVSchemaModelGroup> modelGroup;
      rv = ProcessModelGroup(aErrorHandler, schemaInst, childElement,
                             tagName, nsnull, getter_AddRefs(modelGroup));
      if (NS_SUCCEEDED(rv)) {
        rv = schemaInst->AddModelGroup(modelGroup);
      }
    }
    else if (tagName == nsSchemaAtoms::sInclude_atom ||
             tagName == nsSchemaAtoms::sImport_atom) {
      /* Mixing the handling of <include> and <import> as they are very similar,
        other than a few requirements regarding namespaces.
       
        http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/structures.html#element-include
         If we include a schema, it must either
           (a) have the same targetNamespace as the including schema document or
           (b) no targetNamespace at all

        http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/structures.html#element-import
         When importing a schema, it must either
           (a) if namespace is defined, then namespace == imported
               targetNamespace
           (b) if namespace is not defined, then imported schema must NOT
               have a targetNamespace

         If the uri to load doesn't resolve, it isn't a error.  It is if its an
         invalid XML document or not a schema file
       */

      NS_NAMED_LITERAL_STRING(schemaLocationStr, "schemaLocation");

      nsAutoString schemalocation;
      childElement->GetAttribute(schemaLocationStr, schemalocation);

      // if empty, skip it
      if (schemalocation.IsEmpty())
        continue;

      nsCOMPtr<nsIIOService> ios = do_GetIOService();
      NS_ENSURE_STATE(ios);

      nsCOMPtr<nsIDOMDocument> document;
      aElement->GetOwnerDocument(getter_AddRefs(document));

      nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
      NS_ENSURE_STATE(doc);

      nsCOMPtr<nsIURI> uri;

      rv = NS_NewURI(getter_AddRefs(uri),
                     NS_ConvertUTF16toUTF8(schemalocation),
                     doc->GetDocumentCharacterSet().get(),
                     doc->GetDocumentURI());
      NS_ENSURE_SUCCESS(rv, rv);
      NS_ENSURE_STATE(uri);

      // since we could be going cross-domain, make sure we can load it by doing
      // a principal same origin check.

      // get the base document's principal
      nsIPrincipal *basePrincipal = doc->NodePrincipal();
      NS_ENSURE_STATE(basePrincipal);

      // Do a same origin check on the principal
      rv = basePrincipal->CheckMayLoad(uri, PR_TRUE);
      if (NS_FAILED(rv))
        continue;

      // get the url
      nsCAutoString spec;
      uri->GetSpec(spec);

      nsCOMPtr<nsIDOMDocument> includedDocument;
      rv = GetDocumentFromURI(NS_ConvertUTF8toUTF16(spec),
                              getter_AddRefs(includedDocument));
      NS_ENSURE_SUCCESS(rv, rv);

      // if no document, it is an error
      NS_ENSURE_STATE(includedDocument);

      // get the document element - it should be a xsd:schema
      nsCOMPtr<nsIDOMElement> element;
      includedDocument->GetDocumentElement(getter_AddRefs(element));

      nsAutoString localName, nsUri;
      element->GetLocalName(localName);
      element->GetNamespaceURI(nsUri);

      PRBool correctNamespace = PR_FALSE;
      PRUint32 i;
      for (i = 0; i < kSchemaNamespacesLength; i++) {
        if (nsUri.Equals(NS_ConvertASCIItoUTF16(kSchemaNamespaces[i]))) {
          correctNamespace = PR_TRUE;
          break;
        }
      }

      if (!correctNamespace || !localName.EqualsLiteral("schema")) {
        // not a valid schema file
        return NS_ERROR_SCHEMA_NOT_SCHEMA_ELEMENT;
      }

      // XXX: check the target namespace requirements

      // If <import>, simply call self to do all the heavy lifting
      if (tagName == nsSchemaAtoms::sImport_atom) {
        rv = ProcessSchemaElement(element, nsnull, aResult);
        NS_ENSURE_SUCCESS(rv, rv);
        continue;
      }

      // import/append all elements in the included file to our schema element
      nsCOMPtr<nsIDOMDocument> ownerDoc;
      rv = childElement->GetOwnerDocument(getter_AddRefs(ownerDoc));
      NS_ENSURE_SUCCESS(rv, rv);
      NS_ENSURE_STATE(ownerDoc);

      nsCOMPtr<nsIDOMNode> tmpNode, importedNode, dummy;
      element->GetFirstChild(getter_AddRefs(tmpNode));

      // get the child element's next sibling so we have something to insert
      // before while we are appending to the current schema document
      unsigned short nodeType;
      nsCOMPtr<nsIDOMNode> nextSibling;
      childElement->GetNextSibling(getter_AddRefs(nextSibling));

      while (tmpNode) {
        tmpNode->GetNodeType(&nodeType);

        if (nodeType == nsIDOMNode::ELEMENT_NODE) {
          rv = ownerDoc->ImportNode(tmpNode, PR_TRUE,
                                    getter_AddRefs(importedNode));
          NS_ENSURE_SUCCESS(rv, rv);

          // InsertBefore can deal with a null nextSibling (will append)
          rv = aElement->InsertBefore(importedNode, nextSibling,
                                      getter_AddRefs(dummy));
          NS_ENSURE_SUCCESS(rv, rv);
        }

        tmpNode->GetNextSibling(getter_AddRefs(dummy));
        tmpNode = dummy;
      }

      // we twidle the iterator (reset), making sure to point it at the right
      // place.  We do this because the iterator takes a snapshot the DOMList,
      // so we tell it to reinit itself and then reset it to the original index.
      PRUint32 index = iterator.GetCurrentIndex();
      iterator.SetElement(aElement);
      iterator.Reset(index);
    } else if (tagName != nsSchemaAtoms::sAnnotation_atom &&
               tagName != nsSchemaAtoms::sRedefine_atom &&
               tagName != nsSchemaAtoms::sNotation_atom) {
      // if it is none of these, unexpected element.
      nsAutoString elementName;
      nsresult rc = aElement->GetTagName(elementName);
      NS_ENSURE_SUCCESS(rc, rc);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema, unexpected element \"");
      errorMsg.Append(elementName);
      errorMsg.AppendLiteral("\" in <schema .../>");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return NS_ERROR_UNEXPECTED;
    }

    if (NS_FAILED(rv)) {
      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema");
      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return rv;
    }
  }

  // We need to add this schema into the schema collection because resolving
  // forward references may require resolving types with namespace prefixes,
  // which could easily point back at this schema file.
  mSchemas.Put(targetNamespace, schemaInst);

  // Resolve all forward references
  rv = schemaInst->Resolve(aErrorHandler);
  if (NS_FAILED(rv)) {
    return rv;
  }

  NS_ADDREF(*aResult = schemaInst);

  return NS_OK;
}

PRBool
ParseQualifiedName(nsIDOMElement* aContext,
                   const nsAString& aQualifiedName,
                   nsAString& aPrefix,
                   nsAString& aLocalName,
                   nsAString& aNamespaceURI)
{
  const PRUnichar *pos, *begin, *end;
  
  aQualifiedName.BeginReading(&begin, &end);
  pos = begin;

  while (pos < end && *pos != ':') {
    ++pos;
  }

  if (pos < end) {
    aPrefix.Assign(Substring(begin, pos-begin));
    ++pos;
    aLocalName.Assign(Substring(pos, end-pos));
  }
  else {
    aLocalName.Assign(aQualifiedName);
  }
  
  nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(aContext));
  
  return node->LookupNamespaceURI(aPrefix, aNamespaceURI);
}

nsresult
nsSchemaLoader::GetNewOrUsedType(nsSchema* aSchema,
                                 nsIDOMElement* aContext,
                                 const nsAString& aTypeName,
                                 nsISVSchemaType** aType)
{
  nsresult rv = NS_OK;
  nsAutoString prefix, localName, namespaceURI;

  // See if there's a prefix and get the
  // namespace associated with the prefix
  rv = ParseQualifiedName(aContext, aTypeName, prefix,
                          localName, namespaceURI);
  if (!prefix.IsEmpty() && NS_FAILED(rv)) {
    // Unknown prefix
    return NS_ERROR_SCHEMA_UNKNOWN_PREFIX;
  }

  *aType = nsnull;
  nsAutoString targetNamespace;
  aSchema->GetTargetNamespace(targetNamespace);
  if (namespaceURI.IsEmpty() || namespaceURI.Equals(targetNamespace)) {
    // It's a local type
    rv = aSchema->GetTypeByName(localName, aType);
  }
  else {
    rv = GetType(localName, namespaceURI, aType);
    if (!*aType) {
      return NS_ERROR_SCHEMA_UNKNOWN_TARGET_NAMESPACE;
    }
  }

  // If we didn't get a type, we need to create a placeholder
  if (NS_SUCCEEDED(rv) && !*aType) {
    nsSchemaTypePlaceholder* placeholder = new nsSchemaTypePlaceholder(aSchema,
                                                                       localName);
    if (!placeholder) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    *aType = placeholder;
    NS_ADDREF(*aType);
  }

  return rv;
}

/**
 * @param aErrorHandler Error handler (in).
 * @param aSchema Schema in which is the element, not null (in).
 * @param aElement DOM element <element .../>, not null (in).
 * @param aSchemaElement Schema element from processing DOM element (out).
 */
nsresult
nsSchemaLoader::ProcessElement(nsISVSchemaErrorHandler* aErrorHandler,
                               nsSchema* aSchema,
                               nsIDOMElement* aElement,
                               nsISVSchemaElement** aSchemaElement)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsISVSchemaElement> schemaElement;
  PRUint32 minOccurs, maxOccurs;
  GetMinAndMax(aElement, &minOccurs, &maxOccurs);

  // See if it's a reference or an actual element declaration
  nsAutoString ref;
  aElement->GetAttribute(NS_LITERAL_STRING("ref"), ref);
  if (!ref.IsEmpty()) {
    nsSchemaElementRef* elementRef;
    nsAutoString refNS;

    // need to handle ns:type
    rv = ParseNameAndNS(ref, aElement, ref, refNS);
    NS_ENSURE_SUCCESS(rv, rv);

    elementRef = new nsSchemaElementRef(aSchema, ref, refNS);
    if (!elementRef) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    schemaElement = elementRef;

    elementRef->SetMinOccurs(minOccurs);
    elementRef->SetMaxOccurs(maxOccurs);
  }
  else {
    nsAutoString value;
    nsSchemaElement* elementInst;
    const nsAString& empty = EmptyString();

    rv = aElement->GetAttributeNS(empty, NS_LITERAL_STRING("name"), value);
    
    if (NS_FAILED(rv)) {
      nsresult rc = aElement->GetTagName(value);
      NS_ENSURE_SUCCESS(rc, rc);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema element, cannot get ");
      errorMsg.AppendLiteral("attribute \"name\" of element \"");
      errorMsg.Append(value);
      errorMsg.AppendLiteral("\"");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return rv;
    }

    value.Trim(" \r\n\t");
    elementInst = new nsSchemaElement(aSchema, value);
    if (!elementInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    schemaElement = elementInst;

    elementInst->SetMinOccurs(minOccurs);
    elementInst->SetMaxOccurs(maxOccurs);

    nsAutoString defaultValue, fixedValue;
    rv = aElement->GetAttributeNS(empty, NS_LITERAL_STRING("default"),
                                  defaultValue);
    if (NS_FAILED(rv)) {
      nsresult rc = aElement->GetTagName(value);
      NS_ENSURE_SUCCESS(rc, rc);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema element, cannot get ");
      errorMsg.AppendLiteral("attribute \"default\" of element \"");
      errorMsg.Append(value);
      errorMsg.AppendLiteral("\"");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return rv;
    }
    
    rv = aElement->GetAttributeNS(empty, NS_LITERAL_STRING("fixed"),
                                  fixedValue);
    if (NS_FAILED(rv)) {
      nsresult rc = aElement->GetTagName(value);
      NS_ENSURE_SUCCESS(rc, rc);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema element, cannot get ");
      errorMsg.AppendLiteral("attribute \"fixed\" of element \"");
      errorMsg.Append(value);
      errorMsg.AppendLiteral("\"");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return rv;
    }

    elementInst->SetConstraints(defaultValue, fixedValue);

    rv = aElement->GetAttributeNS(empty, NS_LITERAL_STRING("nillable"), value);
    if (NS_FAILED(rv)) {
      nsresult rc = aElement->GetTagName(value);
      NS_ENSURE_SUCCESS(rc, rc);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema element, cannot get ");
      errorMsg.AppendLiteral("attribute \"nillable\" of element \"");
      errorMsg.Append(value);
      errorMsg.AppendLiteral("\"");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return rv;
    }
    value.Trim(" \r\n\t");

    PRInt32 flags = 0;
    if (value.EqualsLiteral("true"))
      flags |= nsSchemaElement::NILLABLE;

    rv = aElement->GetAttributeNS(empty, NS_LITERAL_STRING("abstract"), value);
    if (NS_FAILED(rv)) {
      nsresult rc = aElement->GetTagName(value);
      NS_ENSURE_SUCCESS(rc, rc);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema element, cannot get ");
      errorMsg.AppendLiteral("attribute \"abstract\" of element \"");
      errorMsg.Append(value);
      errorMsg.AppendLiteral("\"");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return rv;
    }
    value.Trim(" \r\n\t");

    if (value.EqualsLiteral("true"))
      flags |= nsSchemaElement::ABSTRACT;

    nsCOMPtr<nsIDOMNode> parent;
    rv = aElement->GetParentNode(getter_AddRefs(parent));
    if (NS_FAILED(rv))
      return rv;
    parent->GetLocalName(value);
    
    // Check if the schema element's targetNamespace applies to <element>.
    // Note: If the <element> element information item has <schema> as its
    // parent,then the actual value of the targetNamespace is that of the
    // parent <schema> element information item, or absent if there is
    // none. Otherwise if the <element> element information item has
    // <schema> element as an ancestor then if "form" is present and its actual
    // value is qualified, or if "form" is absent and the actual value of
    // elementFormDefault on the <schema> ancestor is qualified, then the
    // actual value of the  targetNamespace [attribute] is that of the ancestor
    // <schema> element information item, or absent if there is none.
    if (value.EqualsLiteral("schema")) {
      flags |= nsSchemaElement::FORM_QUALIFIED;
    }
    else {
      rv = aElement->GetAttributeNS(empty, NS_LITERAL_STRING("form"),
                                    value);
      if (NS_FAILED(rv)) {
        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema element, cannot get ");
        errorMsg.AppendLiteral("attribute \"form\" of element \"");
        errorMsg.Append(value);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

        return rv;
      }
      value.Trim(" \r\n\t");
      if (value.IsEmpty()) {
        if (aSchema->IsElementFormQualified()) {
           flags |= nsSchemaElement::FORM_QUALIFIED;
        }
        else {
           flags &= ~nsSchemaElement::FORM_QUALIFIED;
        }
      }
      else if (value.EqualsLiteral("qualified")) {
        flags |= nsSchemaElement::FORM_QUALIFIED;
      }
      else {
         flags &= ~nsSchemaElement::FORM_QUALIFIED;
      }
    }

    elementInst->SetFlags(flags);

    nsCOMPtr<nsISVSchemaType> schemaType;
    nsAutoString typeStr;
    aElement->GetAttribute(NS_LITERAL_STRING("type"), typeStr);
    if (!typeStr.IsEmpty()) {
      rv = GetNewOrUsedType(aSchema, aElement, typeStr,
                            getter_AddRefs(schemaType));
      if (NS_FAILED(rv)) {
        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, unknown type \"");
        errorMsg.Append(typeStr);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

        return rv;
      }
    }
    // Look for the type as a child of the element
    else {
      nsChildElementIterator iterator(aElement,
                                      kSchemaNamespaces,
                                      kSchemaNamespacesLength);
      nsCOMPtr<nsIDOMElement> childElement;
      nsCOMPtr<nsIAtom> tagName;

      while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                                getter_AddRefs(tagName))) &&
             childElement) {
        if (tagName == nsSchemaAtoms::sSimpleType_atom) {
          nsCOMPtr<nsISVSchemaSimpleType> simpleType;
          
          rv = ProcessSimpleType(aErrorHandler, aSchema, childElement,
                                 getter_AddRefs(simpleType));
          if (NS_FAILED(rv)) {
            return rv;
          }
          schemaType = simpleType;
          break;
        }
        else if (tagName == nsSchemaAtoms::sComplexType_atom) {
          nsCOMPtr<nsISVSchemaComplexType> complexType;
          
          rv = ProcessComplexType(aErrorHandler, aSchema, childElement,
                                  getter_AddRefs(complexType));
          if (NS_FAILED(rv)) {
            return rv;
          }
          schemaType = complexType;
          break;
        }
      }
    }

    if (!schemaType) {
      nsAutoString ns;
      aElement->GetNamespaceURI(ns);
      rv = GetType(NS_LITERAL_STRING("anyType"),
                   ns,
                   getter_AddRefs(schemaType));
      if (NS_FAILED(rv)) {
        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, cannot find \'anyType\' ");
        errorMsg.AppendLiteral("placeholder type in namespace \"");
        errorMsg.Append(ns);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

        return rv;
      }
    }

    rv = elementInst->SetType(schemaType);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }

  *aSchemaElement = schemaElement;
  NS_ADDREF(*aSchemaElement);

  return NS_OK;
}

/**
 * Handle <complexType ...>
 *
 * @param aErrorHandler Webservice error handler.
 * @param aSchema Owning schema (in)
 * @param aElement <complexType> element (in)
 * @param aComplexType Schema complex type built from |aElement|
 */
nsresult
nsSchemaLoader::ProcessComplexType(nsISVSchemaErrorHandler* aErrorHandler,
                                   nsSchema* aSchema,
                                   nsIDOMElement* aElement,
                                   nsISVSchemaComplexType** aComplexType)
{
  nsresult rv = NS_OK;
  nsCOMPtr<nsISVSchemaComplexType> complexType;

  nsAutoString abstract, name;
  aElement->GetAttribute(NS_LITERAL_STRING("abstract"), abstract);
  aElement->GetAttribute(NS_LITERAL_STRING("name"), name);

  nsSchemaComplexType* typeInst;
  typeInst = new nsSchemaComplexType(aSchema, name,
                                     abstract.EqualsLiteral("true"));
  if (!typeInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  complexType = typeInst;

  rv = typeInst->Init();
  NS_ENSURE_SUCCESS(rv, rv);

  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  PRUint16 contentModel = nsISVSchemaComplexType::CONTENT_MODEL_EMPTY;
  PRUint16 derivation = nsISVSchemaComplexType::DERIVATION_SELF_CONTAINED;
  nsCOMPtr<nsISVSchemaType> baseType;
  nsCOMPtr<nsISVSchemaModelGroup> modelGroup;

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sSimpleContent_atom) {
      contentModel = nsISVSchemaComplexType::CONTENT_MODEL_SIMPLE;

      rv = ProcessSimpleContent(aErrorHandler, aSchema, childElement, typeInst,
                                &derivation, getter_AddRefs(baseType));
      break;
    }

    if (tagName == nsSchemaAtoms::sComplexContent_atom) {
      rv = ProcessComplexContent(aErrorHandler, aSchema, childElement, typeInst,
                                 &contentModel, &derivation,
                                 getter_AddRefs(baseType));
      break;
    }

    if (tagName == nsSchemaAtoms::sModelGroup_atom ||
        tagName == nsSchemaAtoms::sAll_atom ||
        tagName == nsSchemaAtoms::sChoice_atom ||
        tagName == nsSchemaAtoms::sSequence_atom ||
        tagName == nsSchemaAtoms::sAttribute_atom ||
        tagName == nsSchemaAtoms::sAttributeGroup_atom ||
        tagName == nsSchemaAtoms::sAnyAttribute_atom) {
      rv = ProcessComplexTypeBody(aErrorHandler, aSchema,
                                  aElement, typeInst, nsnull,
                                  &contentModel);
      break;
    }

    if (tagName == nsSchemaAtoms::sAnnotation_atom) {
      // XXX: skipping for now
    }
    else {
      // Unexpected schema element
      nsAutoString elementName;
      rv = aElement->GetTagName(elementName);
      NS_ENSURE_SUCCESS(rv, rv);
      rv = NS_ERROR_UNEXPECTED;

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema, unexpected element \"");
      errorMsg.Append(elementName);
      errorMsg.AppendLiteral("\" in <complexType />, should be <simpleContent .../>");
      errorMsg.AppendLiteral(", <complexContent ../>, <annotation .../>");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      break;
    }
  }

  if (NS_FAILED(rv)) {
    return rv;
  }

  nsAutoString mixed;
  aElement->GetAttribute(NS_LITERAL_STRING("mixed"), mixed);
  if (mixed.EqualsLiteral("true")) {
    contentModel = nsISVSchemaComplexType::CONTENT_MODEL_MIXED;
  }

  typeInst->SetContentModel(contentModel);
  typeInst->SetDerivation(derivation, baseType);

  *aComplexType = complexType;
  NS_ADDREF(*aComplexType);

  return NS_OK;
}

void
nsSchemaLoader::ConstructArrayName(nsISVSchemaType* aType,
                                   nsAString& aName)
{
  nsAutoString typeName;
  
  aType->GetName(typeName);
  aName.AssignLiteral("ArrayOf");
  aName.Append(typeName);
}

nsresult
nsSchemaLoader::ParseDimensions(nsSchema* aSchema,
                                nsIDOMElement* aAttrElement,
                                const nsAString& aStr,
                                nsISVSchemaType* aBaseType,
                                nsISVSchemaType** aArrayType,
                                PRUint32* aDimension)
{
  const PRUnichar *iter, *done_reading;
  aStr.BeginReading(&iter, &done_reading);

  PRUint32 dimension = 1;
  PRUnichar uc = *iter++;
  if (uc != PRUnichar('[')) {
    return NS_ERROR_UNEXPECTED;
  }

  while (iter != done_reading) {
    uc = *iter++;
    if (uc == PRUnichar(',')) {
      dimension++;
    }
    else if (uc == PRUnichar(']')) {
      break;
    }
  }
  *aDimension = dimension;

  while ((iter != done_reading) && (*iter == PRUnichar(' '))) {
    ++iter;
  }

  // If there's still more to go, then create an array type
  // based on the base and continue to parse
  if ((iter != done_reading) && (*iter == PRUnichar('['))) {
    nsAutoString name;
    nsCOMPtr<nsISVSchemaType> myArrayType;
    PRUint32 myDimension;
    
    nsresult rv = ParseDimensions(aSchema, aAttrElement,
                                  nsDependentSubstring(iter, done_reading-iter),
                                  aBaseType, getter_AddRefs(myArrayType),
                                  &myDimension);
    if (NS_FAILED(rv)) {
      return rv;
    }

    ConstructArrayName(myArrayType, name);
    nsSchemaComplexType* typeInst = new nsSchemaComplexType(aSchema,
                                                            name,
                                                            PR_FALSE);
    if (!typeInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    nsCOMPtr<nsISVSchemaComplexType> complexType = typeInst;

    rv = typeInst->Init();
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr<nsISVSchemaType> soapArray;
    rv = GetType(NS_LITERAL_STRING("Array"),
                 NS_LITERAL_STRING(NS_SOAP_1_2_ENCODING_NAMESPACE),
                 getter_AddRefs(soapArray));
    if (NS_FAILED(rv)) {
      return rv;
    }

    typeInst->SetContentModel(nsISVSchemaComplexType::CONTENT_MODEL_ELEMENT_ONLY);
    typeInst->SetDerivation(nsISVSchemaComplexType::DERIVATION_RESTRICTION_COMPLEX,
                            soapArray);
    typeInst->SetArrayInfo(myArrayType, myDimension);

    *aArrayType = typeInst;
  }
  else {
    *aArrayType = aBaseType;
  }
  NS_ADDREF(*aArrayType);

  return NS_OK;
}

nsresult
nsSchemaLoader::ParseArrayType(nsSchema* aSchema,
                               nsIDOMElement* aAttrElement,
                               const nsAString& aStr,
                               nsISVSchemaType** aType,
                               PRUint32* aDimension)
{
  PRInt32 offset;
  
  offset = aStr.FindChar(PRUnichar('['));
  if (offset == -1) {
    return NS_ERROR_SCHEMA_UNKNOWN_TYPE;
  }
  nsDependentSubstring typeStr(aStr, 0, offset);
          
  nsCOMPtr<nsISVSchemaType> type;
  nsresult rv = GetNewOrUsedType(aSchema, aAttrElement, typeStr,
                                 getter_AddRefs(type));
  if (NS_FAILED(rv)) {
    return rv;
  }
  
  nsDependentSubstring dimensionStr(aStr, offset,
                                    aStr.Length() - offset);
  return ParseDimensions(aSchema, aAttrElement, dimensionStr, type,
                         aType, aDimension);
}


nsresult
nsSchemaLoader::ProcessComplexTypeBody(nsISVSchemaErrorHandler* aErrorHandler,
                                       nsSchema* aSchema,
                                       nsIDOMElement* aElement,
                                       nsSchemaComplexType* aComplexType,
                                       nsSchemaModelGroup* aSequence,
                                       PRUint16* aContentModel)
{
  nsresult rv = NS_OK;
  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  *aContentModel = nsISVSchemaComplexType::CONTENT_MODEL_EMPTY;

  nsCOMPtr<nsISVSchemaModelGroup> modelGroup;
  
  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if ((tagName == nsSchemaAtoms::sModelGroup_atom) ||
        (tagName == nsSchemaAtoms::sAll_atom) ||
        (tagName == nsSchemaAtoms::sChoice_atom) ||
        (tagName == nsSchemaAtoms::sSequence_atom)) {

      if (modelGroup) {
        // We shouldn't already have a model group
        nsAutoString elementName;
        nsresult rv = childElement->GetTagName(elementName);
        NS_ENSURE_SUCCESS(rv, rv);

        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, must have ");
        errorMsg.AppendLiteral("model group in <complexType .../>, ");
        errorMsg.AppendLiteral("unexpected element \"");
        errorMsg.Append(elementName);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_INVALID_STRUCTURE, errorMsg);

        return NS_ERROR_SCHEMA_INVALID_STRUCTURE;
      }
      
      rv = ProcessModelGroup(aErrorHandler, aSchema,
                             childElement, tagName,
                             aSequence, getter_AddRefs(modelGroup));
      if (NS_FAILED(rv)) {
        return rv;
      }

      PRUint32 particleCount;
      modelGroup->GetParticleCount(&particleCount);
      if (particleCount) {
        *aContentModel = nsISVSchemaComplexType::CONTENT_MODEL_ELEMENT_ONLY;
      }
      else {
        PRUint16 compositor;
        modelGroup->GetCompositor(&compositor);
        
        PRUint32 minOccurs;
        modelGroup->GetMinOccurs(&minOccurs);
        
        if ((compositor == nsISVSchemaModelGroup::COMPOSITOR_CHOICE) &&
            (minOccurs > 0)) {
          *aContentModel = nsISVSchemaComplexType::CONTENT_MODEL_ELEMENT_ONLY;
        }
      }
      
      if (aSequence) {
        // Check if we were collapsed
        if (modelGroup.get() != static_cast<nsISVSchemaModelGroup *>(aSequence)) {
          rv = aSequence->AddParticle(modelGroup);
        }
      }
      else {
        rv = aComplexType->SetModelGroup(modelGroup);
      }
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
    else if ((tagName == nsSchemaAtoms::sAttribute_atom) ||
             (tagName == nsSchemaAtoms::sAttributeGroup_atom) ||
             (tagName == nsSchemaAtoms::sAnyAttribute_atom)) {
      nsCOMPtr<nsISVSchemaAttributeComponent> attribute;

      rv = ProcessAttributeComponent(aErrorHandler, aSchema,
                                     childElement, tagName,
                                     getter_AddRefs(attribute));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = aComplexType->AddAttribute(attribute);
      if (NS_FAILED(rv)) {
        nsAutoString elementName;
        nsAutoString attributeName;
        nsresult rc = childElement->GetTagName(elementName);
        NS_ENSURE_SUCCESS(rc, rc);

        rc = attribute->GetName(attributeName);
        NS_ENSURE_SUCCESS(rc, rc);

        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, cannot process attribute \"");
        errorMsg.Append(attributeName);
        errorMsg.AppendLiteral("\" of element \"");
        errorMsg.Append(elementName);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

        return rv;
      }

      // XXX WSDL ugliness making itself into schemas. Hopefully this
      // mechanism for specifying an array type in schemas will die
      // when the Schema WG address qualified names in attribute
      // default values.
      if (tagName == nsSchemaAtoms::sAttribute_atom) {
#define NS_WSDL_NAMESPACE "http://schemas.xmlsoap.org/wsdl/"
        nsAutoString arrayType;
        childElement->GetAttributeNS(NS_LITERAL_STRING(NS_WSDL_NAMESPACE),
                                     NS_LITERAL_STRING("arrayType"),
                                     arrayType);
        if (!arrayType.IsEmpty()) {
          nsCOMPtr<nsISVSchemaType> arraySchemaType;
          PRUint32 arrayDimension;
          rv = ParseArrayType(aSchema,
                              childElement,
                              arrayType,
                              getter_AddRefs(arraySchemaType),
                              &arrayDimension);
          if (NS_FAILED(rv)) {
            nsAutoString errorMsg;
            errorMsg.AppendLiteral("Failure processing schema, ");
            errorMsg.AppendLiteral("cannot process array type \"");
            errorMsg.Append(arrayType);
            errorMsg.AppendLiteral("\"");

            NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

            return rv;
          }

          rv = aComplexType->SetArrayInfo(arraySchemaType, arrayDimension);
          if (NS_FAILED(rv)) {
            nsAutoString errorMsg;
            errorMsg.AppendLiteral("Failure processing schema, cannot set ");
            errorMsg.AppendLiteral("array information for array type \"");
            errorMsg.Append(arrayType);
            errorMsg.AppendLiteral("\"");

            NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

            return rv;
          }
        }
      }
    }
  }

  return rv;
}

nsresult
nsSchemaLoader::ProcessSimpleContent(nsISVSchemaErrorHandler* aErrorHandler,
                                     nsSchema* aSchema,
                                     nsIDOMElement* aElement,
                                     nsSchemaComplexType* aComplexType,
                                     PRUint16* aDerivation,
                                     nsISVSchemaType** aBaseType)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsISVSchemaType> baseType;

  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;
  
  // A simpleContent element must have children
  if (!iterator.HasChildNodes()) {
    nsAutoString errorMsg(NS_LITERAL_STRING("Failure processing schema, "));
    errorMsg.AppendLiteral("<simpleContent .../> invalid structure, should contains ");
    errorMsg.AppendLiteral("<restriction .../> or <extension .../>");

    NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_INVALID_STRUCTURE, errorMsg);

    return NS_ERROR_SCHEMA_INVALID_STRUCTURE;
  }

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    nsAutoString baseStr;
    if ((tagName == nsSchemaAtoms::sRestriction_atom) ||
        (tagName == nsSchemaAtoms::sExtension_atom)) {
      childElement->GetAttribute(NS_LITERAL_STRING("base"), baseStr);
      if (baseStr.IsEmpty()) {
        nsAutoString elementName;
        rv = childElement->GetTagName(elementName);
        NS_ENSURE_SUCCESS(rv, rv);

        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, \"");
        errorMsg.Append(elementName);
        errorMsg.AppendLiteral("\" must have a \"base\" attribute in order ");
        errorMsg.AppendLiteral("to specify base type");

        NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_MISSING_TYPE, errorMsg);

        return NS_ERROR_SCHEMA_MISSING_TYPE;
      }

      rv = GetNewOrUsedType(aSchema, childElement, baseStr,
                            getter_AddRefs(baseType));
      if (NS_FAILED(rv)) {
        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, unknown base type \"");
        errorMsg.Append(baseStr);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

        return rv;
      }

      nsCOMPtr<nsISVSchemaSimpleType> simpleBaseType;
      if (tagName == nsSchemaAtoms::sRestriction_atom) {
        *aDerivation = nsISVSchemaComplexType::DERIVATION_RESTRICTION_SIMPLE;
        rv = ProcessSimpleContentRestriction(aErrorHandler, aSchema, childElement,
                                             aComplexType, baseType,
                                             getter_AddRefs(simpleBaseType));
        if (NS_FAILED(rv)) {
          return rv;
        }
      }
      else {
        *aDerivation = nsISVSchemaComplexType::DERIVATION_EXTENSION_SIMPLE;
        
        nsCOMPtr<nsISVSchemaComplexType> complexBaseType(do_QueryInterface(baseType));
        if (complexBaseType) {
          // Copy over the attributes from the base type
          // XXX Should really be cloning
          PRUint32 attrIndex, attrCount;
          complexBaseType->GetAttributeCount(&attrCount);
          
          for (attrIndex = 0; attrIndex < attrCount; attrIndex++) {
            nsCOMPtr<nsISVSchemaAttributeComponent> attribute;
            
            rv = complexBaseType->GetAttributeByIndex(attrIndex,
                                                      getter_AddRefs(attribute));
            if (NS_FAILED(rv)) {
              nsAutoString errorMsg;
              errorMsg.AppendLiteral("Failure processing schema, cannot clone ");
              errorMsg.AppendLiteral("attributes from base type \"");
              errorMsg.Append(baseStr);
              errorMsg.AppendLiteral("\"");

              NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

              return rv;
            }

            rv = aComplexType->AddAttribute(attribute);
            if (NS_FAILED(rv)) {
              nsAutoString errorMsg;
              errorMsg.AppendLiteral("Failure processing schema, cannot clone ");
              errorMsg.AppendLiteral("attributes from base type \"");
              errorMsg.Append(baseStr);
              errorMsg.AppendLiteral("\"");
              
              NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

              return rv;
            }
          }
        }
        
        rv = ProcessSimpleContentExtension(aErrorHandler, aSchema, childElement,
                                           aComplexType, baseType,
                                           getter_AddRefs(simpleBaseType));
        if (NS_FAILED(rv)) {
          return rv;
        }
      }

      if (simpleBaseType) {
        rv = aComplexType->SetSimpleBaseType(simpleBaseType);
        if (NS_FAILED(rv)) {
          return rv;
        }
      }
      break;
    }
  }

  *aBaseType = baseType;
  NS_IF_ADDREF(*aBaseType);

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessSimpleContentRestriction(nsISVSchemaErrorHandler* aErrorHandler,
                                                nsSchema* aSchema,
                                                nsIDOMElement* aElement,
                                                nsSchemaComplexType* aComplexType, 
                                                nsISVSchemaType* aBaseType,
                                                nsISVSchemaSimpleType** aSimpleBaseType)
{
  nsresult rv = NS_OK;

  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  nsSchemaRestrictionType* restrictionInst;
  nsCOMPtr<nsISVSchemaSimpleType> simpleBase;

  restrictionInst = new nsSchemaRestrictionType(aSchema, EmptyString());
  if (!restrictionInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  simpleBase = restrictionInst;

  // The base type must actually be a complex type (which itself must
  // have a simple base type.
  nsCOMPtr<nsISVSchemaComplexType> complexBase = do_QueryInterface(aBaseType);
  if (!complexBase) {
    // if base type is a place holder, this is ok
    PRUint16 schemaType;
    rv = aBaseType->GetSchemaType(&schemaType);

    if (NS_SUCCEEDED(rv) && schemaType == nsISVSchemaType::SCHEMA_TYPE_PLACEHOLDER) {
      simpleBase = do_QueryInterface(aBaseType);
    } else {
      nsAutoString baseStr;
      rv = aBaseType->GetName(baseStr);
      NS_ENSURE_SUCCESS(rv, rv);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema, base type \"");
      errorMsg.Append(baseStr);
      errorMsg.AppendLiteral("\" of restriction must be a complex type ");
      errorMsg.AppendLiteral("which itself must be based on a simple type");

      NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_INVALID_TYPE_USAGE, errorMsg);

      return NS_ERROR_SCHEMA_INVALID_TYPE_USAGE;
    }
  } else {
    nsCOMPtr<nsISVSchemaSimpleType> parentSimpleBase;
    complexBase->GetSimpleBaseType(getter_AddRefs(parentSimpleBase));

    if (parentSimpleBase) {
      rv = restrictionInst->SetBaseType(parentSimpleBase);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sSimpleType_atom) {
      nsCOMPtr<nsISVSchemaSimpleType> simpleType;

      rv = ProcessSimpleType(aErrorHandler, aSchema, childElement,
                             getter_AddRefs(simpleType));
      NS_ENSURE_SUCCESS(rv, rv);

      rv = restrictionInst->SetBaseType(simpleType);
      NS_ENSURE_SUCCESS(rv, rv);
    }
    else if ((tagName == nsSchemaAtoms::sMinExclusive_atom) ||
             (tagName == nsSchemaAtoms::sMinInclusive_atom) ||
             (tagName == nsSchemaAtoms::sMaxExclusive_atom) ||
             (tagName == nsSchemaAtoms::sMaxInclusive_atom) ||
             (tagName == nsSchemaAtoms::sTotalDigits_atom) ||
             (tagName == nsSchemaAtoms::sFractionDigits_atom) ||
             (tagName == nsSchemaAtoms::sLength_atom) ||
             (tagName == nsSchemaAtoms::sMinLength_atom) ||
             (tagName == nsSchemaAtoms::sMaxLength_atom) ||
             (tagName == nsSchemaAtoms::sEnumeration_atom) ||
             (tagName == nsSchemaAtoms::sWhiteSpace_atom) ||
             (tagName == nsSchemaAtoms::sPattern_atom)) {
      nsCOMPtr<nsISVSchemaFacet> facet;

      rv = ProcessFacet(aErrorHandler, aSchema, childElement,
                        tagName, getter_AddRefs(facet));
      NS_ENSURE_SUCCESS(rv, rv);

      rv = restrictionInst->AddFacet(facet);
      NS_ENSURE_SUCCESS(rv, rv);
    }
    else if ((tagName == nsSchemaAtoms::sAttribute_atom) ||
             (tagName == nsSchemaAtoms::sAttributeGroup_atom) ||
             (tagName == nsSchemaAtoms::sAnyAttribute_atom)) {
      nsCOMPtr<nsISVSchemaAttributeComponent> attribute;

      rv = ProcessAttributeComponent(aErrorHandler, aSchema,
                                     childElement, tagName,
                                     getter_AddRefs(attribute));
      NS_ENSURE_SUCCESS(rv, rv);

      rv = aComplexType->AddAttribute(attribute);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  *aSimpleBaseType = simpleBase;
  NS_IF_ADDREF(*aSimpleBaseType);

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessSimpleContentExtension(nsISVSchemaErrorHandler* aErrorHandler,
                                              nsSchema* aSchema,
                                              nsIDOMElement* aElement,
                                              nsSchemaComplexType* aComplexType,
                                              nsISVSchemaType* aBaseType,
                                              nsISVSchemaSimpleType** aSimpleBaseType)
{
  nsresult rv = NS_OK;

  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  // If the base type is a complex type, it must itself have a simple
  // base type
  nsCOMPtr<nsISVSchemaComplexType> complexBase = do_QueryInterface(aBaseType);
  if (complexBase) {
    complexBase->GetSimpleBaseType(aSimpleBaseType);
  }
  else {
    aBaseType->QueryInterface(NS_GET_IID(nsISVSchemaSimpleType),
                              (void**)aSimpleBaseType);
  }

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if ((tagName == nsSchemaAtoms::sAttribute_atom) ||
        (tagName == nsSchemaAtoms::sAttributeGroup_atom) ||
        (tagName == nsSchemaAtoms::sAnyAttribute_atom)) {
      nsCOMPtr<nsISVSchemaAttributeComponent> attribute;
      
      rv = ProcessAttributeComponent(aErrorHandler, aSchema,
                                     childElement, tagName,
                                     getter_AddRefs(attribute));
      if (NS_FAILED(rv)) {
        return rv;
      }
      
      rv = aComplexType->AddAttribute(attribute);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
  }
  
  return NS_OK;
}
 
nsresult
nsSchemaLoader::ProcessComplexContent(nsISVSchemaErrorHandler* aErrorHandler,
                                      nsSchema* aSchema,
                                      nsIDOMElement* aElement,
                                      nsSchemaComplexType* aComplexType,
                                      PRUint16* aContentModel,
                                      PRUint16* aDerivation,
                                      nsISVSchemaType** aBaseType)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsISVSchemaType> baseType;
  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;
  
  // A complexContent element must have children
  if (!iterator.HasChildNodes()) {
    nsAutoString errorMsg(NS_LITERAL_STRING("Failure processing schema, "));
    errorMsg.AppendLiteral("<complexContent .../> must contains ");
    errorMsg.AppendLiteral("<restriction .../> or <extension .../>");
    NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_INVALID_STRUCTURE, errorMsg);

    return NS_ERROR_SCHEMA_INVALID_STRUCTURE;
  }
  
  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    nsAutoString baseStr;
    if ((tagName == nsSchemaAtoms::sRestriction_atom) ||
        (tagName == nsSchemaAtoms::sExtension_atom)) {
      childElement->GetAttribute(NS_LITERAL_STRING("base"), baseStr);
      if (baseStr.IsEmpty()) {
        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, ");
        
        if (tagName == nsSchemaAtoms::sRestriction_atom) {
          errorMsg.AppendLiteral("restriction");
        }
        else {
          errorMsg.AppendLiteral("extension");
        }
        
        errorMsg.AppendLiteral(" must have a \"base\" attribute in order to ");
        errorMsg.AppendLiteral("specify base type");
        
        NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_MISSING_TYPE, errorMsg);
        
        return NS_ERROR_SCHEMA_MISSING_TYPE;
      }
      
      rv = GetNewOrUsedType(aSchema, childElement, baseStr,
                            getter_AddRefs(baseType));
      if (NS_FAILED(rv)) {
        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, unknown base type \"");
        errorMsg.Append(baseStr);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

        return rv;
      }

      nsCOMPtr<nsISVSchemaComplexType> complexBaseType(do_QueryInterface(baseType));

      if (tagName == nsSchemaAtoms::sRestriction_atom) {
        *aDerivation = nsISVSchemaComplexType::DERIVATION_RESTRICTION_COMPLEX;
        rv = ProcessComplexTypeBody(aErrorHandler, aSchema, childElement,
                                    aComplexType, nsnull, aContentModel);
      }
      else {
        *aDerivation = nsISVSchemaComplexType::DERIVATION_EXTENSION_COMPLEX;
        
        nsCOMPtr<nsISVSchemaModelGroup> sequence;
        nsSchemaModelGroup* sequenceInst = nsnull;
        if (complexBaseType) {
          // XXX Should really be cloning
          nsCOMPtr<nsISVSchemaModelGroup> baseGroup;
          rv = complexBaseType->GetModelGroup(getter_AddRefs(baseGroup));
          if (NS_FAILED(rv)) {
            nsAutoString errorMsg;
            errorMsg.AppendLiteral("Failure processing schema, extension for type \"");
            errorMsg.Append(baseStr);
            errorMsg.AppendLiteral("\" does not contains any model group");
            errorMsg.AppendLiteral("such as <all>, <choice>, <sequence>, or <group>");

            NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

            return rv;
          }
          
          if (baseGroup) {
            // Create a new model group that's going to be the a sequence
            // of the base model group and the content below
            sequenceInst = new nsSchemaModelGroup(aSchema, EmptyString());
            if (!sequenceInst) {
              return NS_ERROR_OUT_OF_MEMORY;
            }
            sequence = sequenceInst;

            PRUint16 compositor;
            baseGroup->GetCompositor(&compositor);

            PRUint32 minOccurs, maxOccurs;
            baseGroup->GetMinOccurs(&minOccurs);
            baseGroup->GetMaxOccurs(&maxOccurs);

            // If the base group also a sequence, we can collapse the
            // two sequences.
            if ((compositor == nsISVSchemaModelGroup::COMPOSITOR_SEQUENCE) &&
                (minOccurs == 1) && (maxOccurs == 1)) {
              PRUint32 pIndex, pCount;
              baseGroup->GetParticleCount(&pCount);
              for (pIndex = 0; pIndex < pCount; pIndex++) {
                nsCOMPtr<nsISVSchemaParticle> particle;
                
                rv = baseGroup->GetParticle(pIndex, getter_AddRefs(particle));
                if (NS_FAILED(rv)) {
                  nsAutoString errorMsg;
                  errorMsg.AppendLiteral("Failure processing schema, failure ");
                  errorMsg.AppendLiteral("processing model group for extension ");
                  errorMsg.AppendLiteral("of type \"");
                  errorMsg.Append(baseStr);
                  errorMsg.AppendLiteral("\"");

                  NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

                  return rv;
                }
                
                rv = sequenceInst->AddParticle(particle);
                if (NS_FAILED(rv)) {
                  nsAutoString errorMsg;
                  errorMsg.AppendLiteral("Failure processing schema, failure ");
                  errorMsg.AppendLiteral("processing model group for extension ");
                  errorMsg.AppendLiteral("of type \"");
                  errorMsg.Append(baseStr);
                  errorMsg.AppendLiteral("\"");

                  NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

                  return rv;
                }
              }
            }
            else {
              sequenceInst->AddParticle(baseGroup);
            }
            
            aComplexType->SetModelGroup(sequence);
          }
        }
        
        PRUint16 explicitContent;
        rv = ProcessComplexTypeBody(aErrorHandler, aSchema, childElement,
                                    aComplexType, sequenceInst,
                                    &explicitContent);
        if (NS_FAILED(rv)) {
          return rv;
        }
        // If the explicit content is empty, get the content type
        // from the base
        if ((explicitContent == nsISVSchemaComplexType::CONTENT_MODEL_EMPTY) &&
            complexBaseType) {
          complexBaseType->GetContentModel(aContentModel);
        }
        else {
          *aContentModel = explicitContent;
        }
      }

      // Copy over the attributes from the base type
      // XXX Should really be cloning
      if (complexBaseType) {
        PRUint32 attrIndex, attrCount;
        complexBaseType->GetAttributeCount(&attrCount);

        for (attrIndex = 0; attrIndex < attrCount; attrIndex++) {
          nsCOMPtr<nsISVSchemaAttributeComponent> attribute;

          rv = complexBaseType->GetAttributeByIndex(attrIndex,
                                                    getter_AddRefs(attribute));
          if (NS_FAILED(rv)) {
            nsAutoString errorMsg;
            errorMsg.AppendLiteral("Failure processing schema, cannot clone ");
            errorMsg.AppendLiteral("attributes from base type \"");
            errorMsg.Append(baseStr);
            errorMsg.AppendLiteral("\"");

            NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

            return rv;
          }

          rv = aComplexType->AddAttribute(attribute);
          if (NS_FAILED(rv)) {
            nsAutoString errorMsg;
            errorMsg.AppendLiteral("Failure processing schema, cannot clone ");
            errorMsg.AppendLiteral("attributes from base type \"");
            errorMsg.Append(baseStr);
            errorMsg.AppendLiteral("\"");

            NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

            return rv;
          }
        }
      }


      break;
    }
  }

  nsAutoString mixed;
  aElement->GetAttribute(NS_LITERAL_STRING("mixed"), mixed);
  if (mixed.EqualsLiteral("true")) {
    *aContentModel = nsISVSchemaComplexType::CONTENT_MODEL_MIXED;
  }

  *aBaseType = baseType;
  NS_IF_ADDREF(*aBaseType);

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessSimpleType(nsISVSchemaErrorHandler* aErrorHandler,
                                  nsSchema* aSchema,
                                  nsIDOMElement* aElement,
                                  nsISVSchemaSimpleType** aSimpleType)
{
  nsresult rv = NS_OK;

  nsAutoString name;
  aElement->GetAttribute(NS_LITERAL_STRING("name"), name);

  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sRestriction_atom) {
      rv = ProcessSimpleTypeRestriction(aErrorHandler, aSchema, childElement,
                                        name, aSimpleType);
      break;
    }
    else if (tagName == nsSchemaAtoms::sList_atom) {
      rv = ProcessSimpleTypeList(aErrorHandler, aSchema, childElement,
                                 name, aSimpleType);
      break;
    }
    else if (tagName == nsSchemaAtoms::sUnion_atom) {
      rv = ProcessSimpleTypeUnion(aErrorHandler, aSchema, childElement,
                                  name, aSimpleType);
      break;
    }
  }
  
  return rv;
}

nsresult
nsSchemaLoader::ProcessSimpleTypeRestriction(nsISVSchemaErrorHandler* aErrorHandler,
                                             nsSchema* aSchema,
                                             nsIDOMElement* aElement,
                                             const nsAString& aName,
                                             nsISVSchemaSimpleType** aSimpleType)
{
  nsresult rv = NS_OK;

  nsSchemaRestrictionType* restrictionInst;
  nsCOMPtr<nsISVSchemaSimpleType> restriction;
 
  restrictionInst = new nsSchemaRestrictionType(aSchema, aName);
  if (!restrictionInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  restriction = restrictionInst;

  nsCOMPtr<nsISVSchemaType> baseType;
  nsAutoString baseStr;
  aElement->GetAttribute(NS_LITERAL_STRING("base"), baseStr);
  if (!baseStr.IsEmpty()) {
    rv = GetNewOrUsedType(aSchema, aElement, baseStr,
                          getter_AddRefs(baseType));
    if (NS_FAILED(rv)) {
      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema, unknown base type \"");
      errorMsg.Append(baseStr);
      errorMsg.AppendLiteral("\"");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return rv;
    }

    nsCOMPtr<nsISVSchemaSimpleType> simpleBase(do_QueryInterface(baseType));
    if (!simpleBase) {
      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema, base type \"");
      errorMsg.Append(baseStr);
      errorMsg.AppendLiteral("\" should be a simple type");

      NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_INVALID_TYPE_USAGE, errorMsg);

      return NS_ERROR_SCHEMA_INVALID_TYPE_USAGE;
    }
    rv = restrictionInst->SetBaseType(simpleBase);
  }

  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;

  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if ((tagName == nsSchemaAtoms::sSimpleType_atom) &&
        !baseType) {
      nsCOMPtr<nsISVSchemaSimpleType> simpleType;
      
      rv = ProcessSimpleType(aErrorHandler, aSchema, childElement,
                             getter_AddRefs(simpleType));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = restrictionInst->SetBaseType(simpleType);
      if (NS_FAILED(rv)) {
        return rv;
      }
      baseType = simpleType;
    }
    else if ((tagName == nsSchemaAtoms::sMinExclusive_atom) ||
             (tagName == nsSchemaAtoms::sMinInclusive_atom) ||
             (tagName == nsSchemaAtoms::sMaxExclusive_atom) ||
             (tagName == nsSchemaAtoms::sMaxInclusive_atom) ||
             (tagName == nsSchemaAtoms::sTotalDigits_atom) ||
             (tagName == nsSchemaAtoms::sFractionDigits_atom) ||
             (tagName == nsSchemaAtoms::sLength_atom) ||
             (tagName == nsSchemaAtoms::sMinLength_atom) ||
             (tagName == nsSchemaAtoms::sMaxLength_atom) ||
             (tagName == nsSchemaAtoms::sEnumeration_atom) ||
             (tagName == nsSchemaAtoms::sWhiteSpace_atom) ||
             (tagName == nsSchemaAtoms::sPattern_atom)) {
      nsCOMPtr<nsISVSchemaFacet> facet;
      
      rv = ProcessFacet(aErrorHandler, aSchema, childElement,
                        tagName, getter_AddRefs(facet));
      if (NS_FAILED(rv)) {
        return rv;
      }

      rv = restrictionInst->AddFacet(facet);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
  }

  *aSimpleType = restriction;
  NS_ADDREF(*aSimpleType);

  return NS_OK;
}
 
nsresult
nsSchemaLoader::ProcessSimpleTypeList(nsISVSchemaErrorHandler* aErrorHandler,
                                      nsSchema* aSchema,
                                      nsIDOMElement* aElement,
                                      const nsAString& aName,
                                      nsISVSchemaSimpleType** aSimpleType)
{
  nsresult rv = NS_OK;

  nsSchemaListType* listInst;
  nsCOMPtr<nsISVSchemaSimpleType> list;

  listInst = new nsSchemaListType(aSchema, aName);
  if (!listInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  list = listInst;

  nsAutoString itemTypeStr;
  aElement->GetAttribute(NS_LITERAL_STRING("itemType"), itemTypeStr);

  nsCOMPtr<nsISVSchemaSimpleType> itemType;
  if (!itemTypeStr.IsEmpty()) {
    nsCOMPtr<nsISVSchemaType> type;
    rv = GetNewOrUsedType(aSchema, aElement, itemTypeStr,
                          getter_AddRefs(type));
    if (NS_FAILED(rv)) {
      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema, unknown item type \"");
      errorMsg.Append(itemTypeStr);
      errorMsg.AppendLiteral("\"");

      NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

      return rv;
    }

    itemType = do_QueryInterface(type);
  }
  else {
    nsChildElementIterator iterator(aElement,
                                    kSchemaNamespaces,
                                    kSchemaNamespacesLength);
    nsCOMPtr<nsIDOMElement> childElement;
    nsCOMPtr<nsIAtom> tagName;
    
    while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                              getter_AddRefs(tagName))) &&
           childElement) {
      if (tagName == nsSchemaAtoms::sSimpleType_atom) {
        rv = ProcessSimpleType(aErrorHandler, aSchema, childElement,
                               getter_AddRefs(itemType));
        if (NS_FAILED(rv)) {
          return rv;
        }
        break;
      }
    }
  }

  if (!itemType) {
    nsAutoString errorMsg;
    errorMsg.AppendLiteral("Failure processing schema, no item type ");
    errorMsg.AppendLiteral("for simple type \"");
    errorMsg.Append(aName);
    errorMsg.AppendLiteral("\"");

    NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_MISSING_TYPE, errorMsg);

    return NS_ERROR_SCHEMA_MISSING_TYPE;
  }
  listInst->SetListType(itemType);

  *aSimpleType = list;
  NS_ADDREF(*aSimpleType);

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessSimpleTypeUnion(nsISVSchemaErrorHandler* aErrorHandler,
                                       nsSchema* aSchema,
                                       nsIDOMElement* aElement,
                                       const nsAString& aName,
                                       nsISVSchemaSimpleType** aSimpleType)
{
  nsresult rv = NS_OK;

  nsSchemaUnionType* unionInst;
  nsCOMPtr<nsISVSchemaSimpleType> unionType;

  unionInst = new nsSchemaUnionType(aSchema, aName);
  if (!unionInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  unionType = unionInst;

  nsCOMPtr<nsISVSchemaSimpleType> memberType;
  nsAutoString memberTypes;
  aElement->GetAttribute(NS_LITERAL_STRING("memberTypes"), memberTypes);
  if (!memberTypes.IsEmpty()) {
    const PRUnichar *begin = nsnull, *end, *tokenEnd;

    memberTypes.BeginReading(&tokenEnd, &end);

    while (tokenEnd != end) {
      nsAutoString typeStr;
      begin = tokenEnd;
      while (tokenEnd < end && *tokenEnd != PRUnichar(' '))
        ++tokenEnd;
      if (tokenEnd < end) {
        typeStr.Assign(Substring(begin, tokenEnd-begin));
        ++tokenEnd;
        while (tokenEnd < end && *tokenEnd == PRUnichar(' ')) {
          ++tokenEnd;
        }
      }
      else {
        typeStr.Assign(Substring(begin, end-begin));
      }

      nsCOMPtr<nsISVSchemaType> type;
      rv = GetNewOrUsedType(aSchema, aElement, typeStr,
                            getter_AddRefs(type));
      if (NS_FAILED(rv)) {
        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, unknown type \"");
        errorMsg.Append(typeStr);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

        return rv;
      }
      
      memberType = do_QueryInterface(type);
      if (!memberType) {
        nsAutoString errorMsg;
        errorMsg.AppendLiteral("Failure processing schema, invalid member type \"");
        errorMsg.Append(typeStr);
        errorMsg.AppendLiteral("\" for union about simple type \"");
        errorMsg.Append(aName);
        errorMsg.AppendLiteral("\"");

        NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_INVALID_TYPE_USAGE, errorMsg);

        return NS_ERROR_SCHEMA_INVALID_TYPE_USAGE;
      }

      rv = unionInst->AddUnionType(memberType);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
  }
  
  nsChildElementIterator iterator(aElement,
                                  kSchemaNamespaces,
                                  kSchemaNamespacesLength);
  nsCOMPtr<nsIDOMElement> childElement;
  nsCOMPtr<nsIAtom> tagName;
  
  while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                            getter_AddRefs(tagName))) &&
         childElement) {
    if (tagName == nsSchemaAtoms::sSimpleType_atom) {
      rv = ProcessSimpleType(aErrorHandler, aSchema, childElement,
                             getter_AddRefs(memberType));
      if (NS_FAILED(rv)) {
        return rv;
      }
      
      rv = unionInst->AddUnionType(memberType);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
  }

  *aSimpleType = unionType;
  NS_ADDREF(*aSimpleType);

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessModelGroup(nsISVSchemaErrorHandler* aErrorHandler,
                                  nsSchema* aSchema,
                                  nsIDOMElement* aElement,
                                  nsIAtom* aTagName,
                                  nsSchemaModelGroup* aParentSequence,
                                  nsISVSchemaModelGroup** aModelGroup)
{
  nsresult rv = NS_OK;

  nsCOMPtr<nsISVSchemaModelGroup> modelGroup;
  PRUint32 minOccurs, maxOccurs;
  GetMinAndMax(aElement, &minOccurs, &maxOccurs);

  // Check for a ref attribute
  nsAutoString ref, refNS;
  aElement->GetAttribute(NS_LITERAL_STRING("ref"), ref);
  
  if ((aTagName == nsSchemaAtoms::sModelGroup_atom) &&
      !ref.IsEmpty()) {

    rv = ParseNameAndNS(ref, aElement, ref, refNS);
    NS_ENSURE_SUCCESS(rv, rv);

    nsSchemaModelGroupRef* modelGroupRef = new nsSchemaModelGroupRef(aSchema,
                                                                     ref,
                                                                     refNS);
    if (!modelGroupRef) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    modelGroup = modelGroupRef;
    
    modelGroupRef->SetMinOccurs(minOccurs);
    modelGroupRef->SetMaxOccurs(maxOccurs);
  }
  else {
    nsAutoString name;
    aElement->GetAttribute(NS_LITERAL_STRING("name"), name);

    nsChildElementIterator iterator(aElement,
                                    kSchemaNamespaces,
                                    kSchemaNamespacesLength);
    nsCOMPtr<nsIDOMElement> childElement;
    nsCOMPtr<nsIAtom> tagName = aTagName;

    // If this is a group element, find the first compositor
    // child and continue with that.
    if (aTagName == nsSchemaAtoms::sModelGroup_atom) {
      while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                                getter_AddRefs(tagName))) &&
             childElement) {
        if ((tagName == nsSchemaAtoms::sAll_atom) ||
            (tagName == nsSchemaAtoms::sChoice_atom) ||
            (tagName == nsSchemaAtoms::sSequence_atom)) {
          iterator.SetElement(childElement);
          break;
        }
      }
    }

    nsSchemaModelGroup* modelGroupInst;

    // If we have a parent sequence and we're a sequence that
    // only appears once, then collapse us.
    if (aParentSequence &&
        (tagName == nsSchemaAtoms::sSequence_atom) &&
        (minOccurs == 1) && (maxOccurs == 1)) {
      modelGroupInst = aParentSequence;
      modelGroup = modelGroupInst;
    }
    else {
      modelGroupInst = new nsSchemaModelGroup(aSchema, name);
      if (!modelGroupInst) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      modelGroup = modelGroupInst;
      
      modelGroupInst->SetMinOccurs(minOccurs);
      modelGroupInst->SetMaxOccurs(maxOccurs);


      if (tagName == nsSchemaAtoms::sAll_atom) {
        modelGroupInst->SetCompositor(nsISVSchemaModelGroup::COMPOSITOR_ALL);
      }
      else if (tagName == nsSchemaAtoms::sChoice_atom) {
        modelGroupInst->SetCompositor(nsISVSchemaModelGroup::COMPOSITOR_CHOICE);
      }
      else if (tagName == nsSchemaAtoms::sSequence_atom) {
        modelGroupInst->SetCompositor(nsISVSchemaModelGroup::COMPOSITOR_SEQUENCE);
      }
    }
    
    while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                              getter_AddRefs(tagName))) &&
           childElement) {
      if (tagName != nsSchemaAtoms::sAnnotation_atom) {
        nsCOMPtr<nsISVSchemaParticle> particle;

        rv = ProcessParticle(aErrorHandler, aSchema, childElement,
                             tagName, getter_AddRefs(particle));
        if (NS_FAILED(rv)) {
          return rv;
        }

        rv = modelGroupInst->AddParticle(particle);
        if (NS_FAILED(rv)) {
          return rv;
        }
      }
    }
  }

  *aModelGroup = modelGroup;
  NS_ADDREF(*aModelGroup);

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessParticle(nsISVSchemaErrorHandler* aErrorHandler,
                                nsSchema* aSchema,
                                nsIDOMElement* aElement,
                                nsIAtom* aTagName,
                                nsISVSchemaParticle** aParticle)
{
  nsresult rv;

  if (aTagName == nsSchemaAtoms::sElement_atom) {
    nsCOMPtr<nsISVSchemaElement> element;

    rv = ProcessElement(aErrorHandler, aSchema, aElement, getter_AddRefs(element));
    if (NS_FAILED(rv)) {
      return rv;
    }
    *aParticle = element;
    NS_IF_ADDREF(*aParticle);
  }
  else if ((aTagName == nsSchemaAtoms::sModelGroup_atom) ||
           (aTagName == nsSchemaAtoms::sChoice_atom) ||
           (aTagName == nsSchemaAtoms::sSequence_atom)) {
    nsCOMPtr<nsISVSchemaModelGroup> modelGroup;
    
    rv = ProcessModelGroup(aErrorHandler, aSchema, aElement,
                           aTagName, nsnull, getter_AddRefs(modelGroup));
    if (NS_FAILED(rv)) {
      return rv;
    }
    *aParticle = modelGroup;
    NS_IF_ADDREF(*aParticle);
  }
  else if (aTagName == nsSchemaAtoms::sAny_atom) {

    nsCOMPtr<nsISVSchemaParticle> anyParticle;
    nsSchemaAnyParticle* anyParticleInst = new nsSchemaAnyParticle(aSchema);
    if (!anyParticleInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    anyParticle = anyParticleInst;

    PRUint32 minOccurs, maxOccurs;
    GetMinAndMax(aElement, &minOccurs, &maxOccurs);
    anyParticleInst->SetMinOccurs(minOccurs);
    anyParticleInst->SetMaxOccurs(maxOccurs);

    PRUint16 process;
    GetProcess(aElement, &process);
    anyParticleInst->SetProcess(process);

    nsAutoString namespaceStr;
    aElement->GetAttribute(NS_LITERAL_STRING("namespace"), namespaceStr);
    anyParticleInst->SetNamespace(namespaceStr);

    *aParticle = anyParticle;
    NS_ADDREF(*aParticle);
  
  }

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessAttributeComponent(nsISVSchemaErrorHandler* aErrorHandler,
                                          nsSchema* aSchema,
                                          nsIDOMElement* aElement,
                                          nsIAtom* aTagName,
                                          nsISVSchemaAttributeComponent** aAttribute)
{
  nsresult rv;

  if (aTagName == nsSchemaAtoms::sAttribute_atom) {
    nsCOMPtr<nsISVSchemaAttribute> attribute;

    rv = ProcessAttribute(aErrorHandler, aSchema, aElement,
                          getter_AddRefs(attribute));
    if (NS_FAILED(rv)) {
      return rv;
    }
    *aAttribute = attribute;
    NS_IF_ADDREF(*aAttribute);
  }
  else if (aTagName == nsSchemaAtoms::sAttributeGroup_atom) {
    nsCOMPtr<nsISVSchemaAttributeGroup> attributeGroup;

    rv = ProcessAttributeGroup(aErrorHandler, aSchema, aElement,
                               getter_AddRefs(attributeGroup));
    if (NS_FAILED(rv)) {
      return rv;
    }
    *aAttribute = attributeGroup;
    NS_IF_ADDREF(*aAttribute);
  }
  else if (aTagName == nsSchemaAtoms::sAnyAttribute_atom) {
    nsCOMPtr<nsISVSchemaAttributeComponent> anyAttribute;
    nsSchemaAnyAttribute* anyAttributeInst = new nsSchemaAnyAttribute(aSchema);
    if (!anyAttributeInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    anyAttribute = anyAttributeInst;

    PRUint16 process;
    GetProcess(aElement, &process);
    anyAttributeInst->SetProcess(process);

    nsAutoString namespaceStr;
    aElement->GetAttribute(NS_LITERAL_STRING("namespace"), namespaceStr);
    anyAttributeInst->SetNamespace(namespaceStr);
    
    *aAttribute = anyAttribute;
    NS_ADDREF(*aAttribute);

  }

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessAttribute(nsISVSchemaErrorHandler* aErrorHandler,
                                 nsSchema* aSchema,
                                 nsIDOMElement* aElement,
                                 nsISVSchemaAttribute** aAttribute)
{
  nsresult rv;

  nsCOMPtr<nsISVSchemaAttribute> attribute;

  nsAutoString defaultValue, fixedValue, formValue;
  aElement->GetAttribute(NS_LITERAL_STRING("default"), defaultValue);
  aElement->GetAttribute(NS_LITERAL_STRING("fixed"), fixedValue);
  aElement->GetAttribute(NS_LITERAL_STRING("form"), formValue);

  PRUint16 use;
  GetUse(aElement, &use);

  nsAutoString ref, refNS;
  aElement->GetAttribute(NS_LITERAL_STRING("ref"), ref);
  if (!ref.IsEmpty()) {
    rv = ParseNameAndNS(ref, aElement, ref, refNS);
    NS_ENSURE_SUCCESS(rv, rv);

    nsSchemaAttributeRef* attributeRef = new nsSchemaAttributeRef(aSchema,
                                                                  ref, refNS);
    if (!attributeRef) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    attribute = attributeRef;

    attributeRef->SetConstraints(defaultValue, fixedValue);
    attributeRef->SetUse(use);

    // set the qualified form
    if (formValue.EqualsLiteral("qualified")) {
      attributeRef->SetAttributeFormQualified(PR_TRUE);
    }
    else if (formValue.EqualsLiteral("unqualified")) {
      attributeRef->SetAttributeFormQualified(PR_FALSE);
    }
    else {
      // get default
      PRBool defaultvalue = aSchema->IsAttributeFormDefaultQualified();
      attributeRef->SetAttributeFormQualified(defaultvalue);
    }
  }
  else {
    nsAutoString name;
    aElement->GetAttribute(NS_LITERAL_STRING("name"), name);
    
    nsSchemaAttribute* attributeInst = new nsSchemaAttribute(aSchema,
                                                             name);
    if (!attributeInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    attribute = attributeInst;

    attributeInst->SetConstraints(defaultValue, fixedValue);
    attributeInst->SetUse(use);

    // set the qualified form
    if (formValue.EqualsLiteral("qualified")) {
      attributeInst->SetAttributeFormQualified(PR_TRUE);
    }
    else if (formValue.EqualsLiteral("unqualified")) {
      attributeInst->SetAttributeFormQualified(PR_FALSE);
    }
    else {
      // get default
      PRBool defaultvalue = aSchema->IsAttributeFormDefaultQualified();
      attributeInst->SetAttributeFormQualified(defaultvalue);
    }

    nsCOMPtr<nsISVSchemaSimpleType> simpleType;

    nsChildElementIterator iterator(aElement,
                                    kSchemaNamespaces,
                                    kSchemaNamespacesLength);
    nsCOMPtr<nsIDOMElement> childElement;
    nsCOMPtr<nsIAtom> tagName;

    while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                              getter_AddRefs(tagName))) &&
           childElement) {
      if (tagName == nsSchemaAtoms::sSimpleType_atom) {
        rv = ProcessSimpleType(aErrorHandler, aSchema, childElement,
                               getter_AddRefs(simpleType));
        if (NS_FAILED(rv)) {
          return rv;
        }
        break;
      }
    }

    if (!simpleType) {
      nsAutoString typeStr;
      aElement->GetAttribute(NS_LITERAL_STRING("type"), typeStr);

      if (!typeStr.IsEmpty()) {
        nsCOMPtr<nsISVSchemaType> schemaType;
        rv = GetNewOrUsedType(aSchema, aElement, typeStr,
                              getter_AddRefs(schemaType));
        if (NS_FAILED(rv)) {
          nsAutoString errorMsg;
          errorMsg.AppendLiteral("Failure processing schema, unknown type \"");
          errorMsg.Append(typeStr);
          errorMsg.AppendLiteral("\"");

          NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

          return rv;
        }

        simpleType = do_QueryInterface(schemaType);
        if (!simpleType) {
          nsAutoString errorMsg;
          errorMsg.AppendLiteral("Failure processing schema, invalid type \"");
          errorMsg.Append(typeStr);
          errorMsg.AppendLiteral("\" for attribute \"");
          errorMsg.Append(name);
          errorMsg.AppendLiteral("\"");

          NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_INVALID_TYPE_USAGE, errorMsg);

          return NS_ERROR_SCHEMA_INVALID_TYPE_USAGE;
        }
      }
    }

    attributeInst->SetType(simpleType);
  }

  *aAttribute = attribute;
  NS_ADDREF(*aAttribute);

  return NS_OK;
}

nsresult
nsSchemaLoader::ProcessAttributeGroup(nsISVSchemaErrorHandler* aErrorHandler,
                                      nsSchema* aSchema,
                                      nsIDOMElement* aElement,
                                      nsISVSchemaAttributeGroup** aAttributeGroup)
{
  nsresult rv;

  nsCOMPtr<nsISVSchemaAttributeGroup> attributeGroup;

  nsAutoString ref, refNS;
  aElement->GetAttribute(NS_LITERAL_STRING("ref"), ref);

  if (!ref.IsEmpty()) {
    // need to handle ns:type
    rv = ParseNameAndNS(ref, aElement, ref, refNS);
    NS_ENSURE_SUCCESS(rv, rv);

    nsSchemaAttributeGroupRef* attrRef = new nsSchemaAttributeGroupRef(aSchema,
                                                                       ref,
                                                                       refNS);
    if (!attrRef) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    attributeGroup = attrRef;
  }
  else {
    nsAutoString name;
    aElement->GetAttribute(NS_LITERAL_STRING("name"), name);
    
    nsSchemaAttributeGroup* attrInst = new nsSchemaAttributeGroup(aSchema,
                                                                  name);
    if (!attrInst) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    attributeGroup = attrInst;

    rv = attrInst->Init();
    NS_ENSURE_SUCCESS(rv, rv);

    nsChildElementIterator iterator(aElement,
                                    kSchemaNamespaces,
                                    kSchemaNamespacesLength);
    nsCOMPtr<nsIDOMElement> childElement;
    nsCOMPtr<nsIAtom> tagName;

    while (NS_SUCCEEDED(iterator.GetNextChild(getter_AddRefs(childElement),
                                              getter_AddRefs(tagName))) &&
           childElement) {
      if ((tagName == nsSchemaAtoms::sAttribute_atom) ||
          (tagName == nsSchemaAtoms::sAttributeGroup_atom) ||
          (tagName == nsSchemaAtoms::sAnyAttribute_atom)) {
        nsCOMPtr<nsISVSchemaAttributeComponent> attribute;
        
        rv = ProcessAttributeComponent(aErrorHandler, aSchema,
                                       childElement, tagName,
                                       getter_AddRefs(attribute));
        if (NS_FAILED(rv)) {
          return rv;
        }
        
        rv = attrInst->AddAttribute(attribute);
        if (NS_FAILED(rv)) {
          return rv;
        }
      }
    }
  }

  *aAttributeGroup = attributeGroup;
  NS_ADDREF(*aAttributeGroup);

  return NS_OK;
}
 
nsresult
nsSchemaLoader::ProcessFacet(nsISVSchemaErrorHandler* aErrorHandler,
                             nsSchema* aSchema,
                             nsIDOMElement* aElement,
                             nsIAtom* aTagName,
                             nsISVSchemaFacet** aFacet)
{
  nsresult rv;

  nsCOMPtr<nsISVSchemaFacet> facet;
  nsSchemaFacet* facetInst = new nsSchemaFacet(aSchema);
  if (!facetInst) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  facet = facetInst;

  PRUint16 facetType;
  if (aTagName == nsSchemaAtoms::sLength_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_LENGTH;
  }
  else if (aTagName == nsSchemaAtoms::sMinLength_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_MINLENGTH;
  }
  else if (aTagName == nsSchemaAtoms::sMaxLength_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_MAXLENGTH;
  }
  else if (aTagName == nsSchemaAtoms::sPattern_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_PATTERN;
  }
  else if (aTagName == nsSchemaAtoms::sEnumeration_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_ENUMERATION;
  }
  else if (aTagName == nsSchemaAtoms::sWhiteSpace_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_WHITESPACE;
  }
  else if (aTagName == nsSchemaAtoms::sMaxInclusive_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_MAXINCLUSIVE;
  }
  else if (aTagName == nsSchemaAtoms::sMinInclusive_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_MININCLUSIVE;
  }
  else if (aTagName == nsSchemaAtoms::sMaxExclusive_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_MAXEXCLUSIVE;
  }
  else if (aTagName == nsSchemaAtoms::sMinExclusive_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_MINEXCLUSIVE;
  }
  else if (aTagName == nsSchemaAtoms::sTotalDigits_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_TOTALDIGITS;
  }
  else if (aTagName == nsSchemaAtoms::sFractionDigits_atom) {
    facetType = nsISVSchemaFacet::FACET_TYPE_FRACTIONDIGITS;
  }
  else {
    nsAutoString elementName;
    rv = aElement->GetTagName(elementName);
    NS_ENSURE_SUCCESS(rv, rv);

    nsAutoString errorMsg;
    errorMsg.AppendLiteral("Failure processing schema, unknown type of facet \"");
    errorMsg.Append(elementName);
    errorMsg.AppendLiteral("\"");

    NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_UNEXPECTED, errorMsg);

    return NS_ERROR_UNEXPECTED;
  }
  facetInst->SetFacetType(facetType);

  nsAutoString valueStr;
  aElement->GetAttribute(NS_LITERAL_STRING("value"), valueStr);

  /*
   * Enumerations contain data depending on the base type. Some base types allow empty
   * content (e.g. xsd:string), others don't.
   * XXX: Check the requirements of the base type for the enumeration value.
   */
  if (aTagName != nsSchemaAtoms::sEnumeration_atom && valueStr.IsEmpty()) {
    nsAutoString elementName;
    rv = aElement->GetTagName(elementName);
    NS_ENSURE_SUCCESS(rv, rv);

    rv = NS_ERROR_SCHEMA_FACET_VALUE_ERROR;

    nsAutoString errorMsg;
    errorMsg.AppendLiteral("Failure processing schema, invalid empty value ");
    errorMsg.AppendLiteral("for facet \"");
    errorMsg.Append(elementName);
    errorMsg.AppendLiteral("\"");

    NS_SCHEMALOADER_FIRE_ERROR(rv, errorMsg);

    return rv;
  }

  if ((aTagName == nsSchemaAtoms::sLength_atom) ||
      (aTagName == nsSchemaAtoms::sMinLength_atom) ||
      (aTagName == nsSchemaAtoms::sMaxLength_atom) ||
      (aTagName == nsSchemaAtoms::sTotalDigits_atom) ||
      (aTagName == nsSchemaAtoms::sFractionDigits_atom)) {
    PRInt32 intVal = valueStr.ToInteger(&rv);

    if (NS_FAILED(rv) ||
        (intVal < 0) ||
        ((aTagName == nsSchemaAtoms::sTotalDigits_atom) && (intVal == 0))) {
      nsAutoString elementName;
      rv = aElement->GetTagName(elementName);
      NS_ENSURE_SUCCESS(rv, rv);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema, invalid value for facet \"");
      errorMsg.Append(elementName);
      errorMsg.AppendLiteral("\", <=0");

      NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_FACET_VALUE_ERROR, errorMsg);

      return NS_ERROR_SCHEMA_FACET_VALUE_ERROR;
    }

    facetInst->SetUintValue((PRUint32)intVal);
  }
  else if (aTagName == nsSchemaAtoms::sWhiteSpace_atom) {
    PRUint16 whiteSpaceVal;
    if (valueStr.EqualsLiteral("collapse")) {
      whiteSpaceVal = nsSchemaFacet::WHITESPACE_COLLAPSE;
    }
    else if (valueStr.EqualsLiteral("preserve")) {
      whiteSpaceVal = nsSchemaFacet::WHITESPACE_PRESERVE;
    }
    else if (valueStr.EqualsLiteral("replace")) {
      whiteSpaceVal = nsSchemaFacet::WHITESPACE_REPLACE;
    }
    else {
      nsAutoString elementName;
      rv = aElement->GetTagName(elementName);
      NS_ENSURE_SUCCESS(rv, rv);

      nsAutoString errorMsg;
      errorMsg.AppendLiteral("Failure processing schema, invalid value for facet \"");
      errorMsg.Append(elementName);
      errorMsg.AppendLiteral("\", should be \"collapse\", \"preserve\" or \"replace\"");

      NS_SCHEMALOADER_FIRE_ERROR(NS_ERROR_SCHEMA_FACET_VALUE_ERROR, errorMsg);

      return NS_ERROR_SCHEMA_FACET_VALUE_ERROR;
    }

    facetInst->SetWhitespaceValue(whiteSpaceVal);
  }
  else {
    facetInst->SetValue(valueStr);
  }
  
  nsAutoString isFixed;
  aElement->GetAttribute(NS_LITERAL_STRING("fixed"), isFixed);
  facetInst->SetIsFixed(isFixed.EqualsLiteral("true"));

  *aFacet = facet;
  NS_ADDREF(*aFacet);

  return NS_OK;
}

void
nsSchemaLoader::GetUse(nsIDOMElement* aElement,
                       PRUint16* aUse)
{
  *aUse = nsISVSchemaAttribute::USE_OPTIONAL;

  nsAutoString use;
  aElement->GetAttribute(NS_LITERAL_STRING("use"), use);
  
  if (use.EqualsLiteral("prohibited")) {
    *aUse = nsISVSchemaAttribute::USE_PROHIBITED;
  }
  else if (use.EqualsLiteral("required")) {
    *aUse = nsISVSchemaAttribute::USE_REQUIRED;
  }
}

void
nsSchemaLoader::GetProcess(nsIDOMElement* aElement,
                           PRUint16* aProcess)
{
  *aProcess = nsISVSchemaAnyParticle::PROCESS_STRICT;

  nsAutoString process;
  aElement->GetAttribute(NS_LITERAL_STRING("process"), process);

  if (process.EqualsLiteral("lax")) {
    *aProcess = nsISVSchemaAnyParticle::PROCESS_LAX;
  }
  else if (process.EqualsLiteral("skip")) {
    *aProcess = nsISVSchemaAnyParticle::PROCESS_SKIP;
  }
}

void
nsSchemaLoader::GetMinAndMax(nsIDOMElement* aElement,
                             PRUint32* aMinOccurs,
                             PRUint32* aMaxOccurs)
{
  *aMinOccurs = 1;
  *aMaxOccurs = 1;

  nsAutoString minStr, maxStr;
  aElement->GetAttribute(NS_LITERAL_STRING("minOccurs"), minStr);
  aElement->GetAttribute(NS_LITERAL_STRING("maxOccurs"), maxStr);
  
  nsresult rv;
  if (!minStr.IsEmpty()) {
    PRInt32 minVal = minStr.ToInteger(&rv);
    if (NS_SUCCEEDED(rv) && (minVal >= 0)) {
      *aMinOccurs = (PRUint32)minVal;
    }
  }

  if (!maxStr.IsEmpty()) {
    if (maxStr.EqualsLiteral("unbounded")) {
      *aMaxOccurs = nsISVSchemaParticle::OCCURRENCE_UNBOUNDED;
    }
    else {
      PRInt32 maxVal = maxStr.ToInteger(&rv);
      if (NS_SUCCEEDED(rv) && (maxVal >= 0)) {
        *aMaxOccurs = (PRUint32)maxVal;
      }
    }
  }
}

nsresult
nsSchemaLoader::ParseNameAndNS(const nsAString& aName, nsIDOMElement* aElement,
                               nsAString& aTypeName, nsAString& aTypeNS)
{
  nsresult rv;
  nsCOMPtr<nsIParserService> parserService =
    do_GetService("@mozilla.org/parser/parser-service;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  const nsString& qName = PromiseFlatString(aName);
  const PRUnichar *colon;
  rv = parserService->CheckQName(qName, PR_TRUE, &colon);
  NS_ENSURE_SUCCESS(rv, rv);

  if (colon) {
    const PRUnichar* end = qName.EndReading();

    nsAutoString schemaTypePrefix;
    schemaTypePrefix.Assign(Substring(qName.get(), colon));
    aTypeName.Assign(Substring(colon + 1, end));

    nsCOMPtr<nsIDOM3Node> domNode3 = do_QueryInterface(aElement);
    NS_ENSURE_STATE(domNode3);

    // get the namespace url from the prefix
    rv = domNode3->LookupNamespaceURI(schemaTypePrefix, aTypeNS);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  return rv;
}

nsresult
nsSchemaLoader::GetDocumentFromURI(const nsAString& aUri,
                                   nsIDOMDocument** aDocument)
{
  *aDocument = nsnull;

  nsCOMPtr<nsIURI> resolvedURI;
  nsresult rv = GetResolvedURI(aUri, "load", getter_AddRefs(resolvedURI));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIXMLHttpRequest> request =
    do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCAutoString spec;
  resolvedURI->GetSpec(spec);

  const nsAString& empty = EmptyString();
  rv = request->OpenRequest(NS_LITERAL_CSTRING("GET"), spec, PR_FALSE, empty,
                            empty);
  NS_ENSURE_SUCCESS(rv, rv);

  // Force the mimetype of the returned stream to be xml.
  rv = request->OverrideMimeType(NS_LITERAL_CSTRING("application/xml"));
  NS_ENSURE_SUCCESS(rv, rv);

  rv = request->Send(nsnull);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIDOMDocument> document;
  rv = request->GetResponseXML(getter_AddRefs(document));
  NS_ENSURE_SUCCESS(rv, rv);

  if (document) {
    document.swap(*aDocument);
  }

  return NS_OK;
}
