/* -*- 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 Schema Validation. * * The Initial Developer of the Original Code is * IBM Corporation. * Portions created by the Initial Developer are Copyright (C) 2004-2005 * IBM Corporation. All Rights Reserved. * * Contributor(s): * Doron Rosenberg (original author) * Laurent Jouanneau * * 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 "nsSchemaValidator.h" // content includes #include "nsIContent.h" #include "nsIDOMNode.h" #include "nsIDOM3Node.h" #include "nsIDOMElement.h" #include "nsIDOMAttr.h" #include "nsIDOMNodeList.h" #include "nsIParserService.h" #include "nsIDOMNamedNodeMap.h" #include "nsDataHashtable.h" #include "nsIVariant.h" #include "nsIAttribute.h" // string includes #include "nsStringAPI.h" #include "nsUnicharUtils.h" // XPCOM includes #include "nsMemory.h" #include "nsIServiceManager.h" #include "nsIComponentManager.h" #include "nsISVSchema.h" #include "nsISVSchemaLoader.h" #include "nsSchemaValidatorUtils.h" #include "nsNetUtil.h" #include "prlog.h" #include "nspr.h" #include #include #include #include "prprf.h" #include "prtime.h" #include "plbase64.h" #include #include "nsIAtomService.h" #define NS_SCHEMA_1999_NAMESPACE "http://www.w3.org/1999/XMLSchema" #define NS_SCHEMA_2001_NAMESPACE "http://www.w3.org/2001/XMLSchema" #define NS_SCHEMA_INSTANCE_NAMESPACE "http://www.w3.org/2001/XMLSchema-instance" #ifdef PR_LOGGING PRLogModuleInfo *gSchemaValidationLog = PR_NewLogModule("schemaValidation"); #define LOG(x) PR_LOG(gSchemaValidationLog, PR_LOG_DEBUG, x) #define LOG_ENABLED() PR_LOG_TEST(gSchemaValidationLog, PR_LOG_DEBUG) #else #define LOG(x) #endif NS_IMPL_ISUPPORTS1(nsSchemaValidator, nsISchemaValidator) nsSchemaValidator::nsSchemaValidator() : mForceInvalid(PR_FALSE) { } nsSchemaValidator::~nsSchemaValidator() { } //////////////////////////////////////////////////////////// // // nsSchemaValidator implementation // //////////////////////////////////////////////////////////// NS_IMETHODIMP nsSchemaValidator::LoadSchema(nsISVSchema* aSchema) { if (aSchema) aSchema->GetCollection(getter_AddRefs(mSchema)); return NS_OK; } NS_IMETHODIMP nsSchemaValidator::ValidateString(const nsAString & aValue, const nsAString & aType, const nsAString & aNamespace, PRBool *aResult) { LOG(("--------- nsSchemaValidator::ValidateString called ---------")); // empty type is invalid if (aType.IsEmpty()) return NS_ERROR_NOT_AVAILABLE; // no schemas loaded and type is not builtin, abort if (!mSchema && !aNamespace.EqualsLiteral(NS_SCHEMA_1999_NAMESPACE) && !aNamespace.EqualsLiteral(NS_SCHEMA_2001_NAMESPACE)) return NS_ERROR_SCHEMAVALIDATOR_NO_SCHEMA_LOADED; // figure out if it's a simple or complex type nsCOMPtr type; nsresult rv = GetType(aType, aNamespace, getter_AddRefs(type)); NS_ENSURE_SUCCESS(rv, rv); PRUint16 typevalue; rv = type->GetSchemaType(&typevalue); NS_ENSURE_SUCCESS(rv, rv); PRBool isValid = PR_FALSE; if (typevalue == nsISVSchemaType::SCHEMA_TYPE_SIMPLE) { nsCOMPtr simpleType = do_QueryInterface(type); NS_ENSURE_TRUE(simpleType, NS_ERROR_FAILURE); LOG((" Type is a simple type! \n String to validate is: |%s|", NS_ConvertUTF16toUTF8(aValue).get())); rv = ValidateSimpletype(aValue, simpleType, &isValid); } else { // if its not a simpletype, validating a string makes no sense. rv = NS_ERROR_UNEXPECTED; LOG((" -- unexpected type")); } #ifdef PR_LOGGING if (!isValid) LOG((" *** INVALID ***")); #endif *aResult = isValid; return rv; } NS_IMETHODIMP nsSchemaValidator::Validate(nsIDOMNode* aElement, PRBool *aResult) { LOG(("--------- nsSchemaValidator::Validate called ---------")); if (!aElement) return NS_ERROR_SCHEMAVALIDATOR_NO_DOM_NODE_SPECIFIED; // init the override mForceInvalid = PR_FALSE; // will hold the type to validate against nsCOMPtr type; nsresult rv = GetElementXsiType(aElement, getter_AddRefs(type)); NS_ENSURE_SUCCESS(rv, rv); if (!type) { if (!mSchema) { // no type attribute and no loaded schemas, so abort // XXX: needed better error here return NS_ERROR_SCHEMAVALIDATOR_NO_SCHEMA_LOADED; } // no type attribute, look for an xsd:element in the schema that matches LOG((" -- no type attribute found, so looking for matching xsd:element")); // get namespace from node nsAutoString schemaTypeNamespace; rv = aElement->GetNamespaceURI(schemaTypeNamespace); NS_ENSURE_SUCCESS(rv, rv); nsAutoString localName; rv = aElement->GetLocalName(localName); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr element; mSchema->GetElement(localName, schemaTypeNamespace, getter_AddRefs(element)); if (!element) { // no type and no matching element, abort // XXX: needed better error here return NS_ERROR_SCHEMAVALIDATOR_NO_SCHEMA_LOADED; } rv = element->GetType(getter_AddRefs(type)); NS_ENSURE_SUCCESS(rv, rv); } /* * We allow the schema validator to continue validating a structure * even if the nodevalue is invalid per its simpletype binding. This is done * so that we continue to mark nodes with their types, even if we encounter * an invalid nodevalue. We remember that we had an invalid nodevalue by * using mForceInvalid as an override for the final return. */ PRBool isValid = PR_FALSE; rv = ValidateAgainstType(aElement, type, &isValid); *aResult = mForceInvalid ? PR_FALSE : isValid; return rv; } // This is passed into setProperty so that the variant passed as // an xsdtype is released when the property is destroyed static void VariantDTor(void *aObject, nsIAtom *aPropertyName, void *aPropertyValue, void *aData) { nsIVariant *pVariant = static_cast(aPropertyValue); NS_IF_RELEASE(pVariant); } NS_IMETHODIMP nsSchemaValidator::ValidateAgainstType(nsIDOMNode* aElement, nsISVSchemaType* aType, PRBool *aResult) { if (!aElement) return NS_ERROR_SCHEMAVALIDATOR_NO_DOM_NODE_SPECIFIED; NS_ENSURE_STATE(aType); PRUint16 typevalue; nsresult rv = aType->GetSchemaType(&typevalue); NS_ENSURE_SUCCESS(rv, rv); PRBool isValid = PR_FALSE; if (typevalue == nsISVSchemaType::SCHEMA_TYPE_SIMPLE) { nsCOMPtr simpleType = do_QueryInterface(aType); if (simpleType) { // get the nodevalue using DOM 3's textContent nsAutoString nodeValue; nsCOMPtr domNode3 = do_QueryInterface(aElement); domNode3->GetTextContent(nodeValue); LOG((" Type is a simple type!")); LOG((" String to validate is: |%s|", NS_ConvertUTF16toUTF8(nodeValue).get())); rv = ValidateSimpletype(nodeValue, simpleType, &isValid); NS_ENSURE_SUCCESS(rv, rv); nsAutoString typeName, nodeName; rv = aType->GetName(typeName); NS_ENSURE_SUCCESS(rv, rv); rv = aElement->GetLocalName(nodeName); NS_ENSURE_SUCCESS(rv, rv); // go on validating, but remember we failed if (!isValid) { mForceInvalid = PR_TRUE; isValid = PR_TRUE; } nsCOMPtr holder = do_CreateInstance("@mozilla.org/variant;1"); NS_ENSURE_STATE(holder); holder->SetAsInterface(nsISVSchemaType::GetIID(), aType); // and save on the node nsCOMPtr content = do_QueryInterface(domNode3); NS_ENSURE_STATE(content); // we have to be really careful to set the destructor function correctly. // this also has to be a pointer to a variant nsCOMPtr atomServ = do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr key; rv = atomServ->GetAtomUTF8("xsdtype", getter_AddRefs(key)); NS_ENSURE_SUCCESS(rv, rv); nsIVariant *pVariant = holder; NS_IF_ADDREF(pVariant); rv = content->SetProperty(key, pVariant, &VariantDTor); NS_ENSURE_SUCCESS(rv, rv); } } else if (typevalue == nsISVSchemaType::SCHEMA_TYPE_COMPLEX) { nsCOMPtr complexType = do_QueryInterface(aType); if (complexType) { LOG((" Type is a complex type!")); rv = ValidateComplextype(aElement, complexType, &isValid); } } else { rv = NS_ERROR_UNEXPECTED; } *aResult = isValid; return rv; } /* Returns the nsISVSchemaType for a given value/type/namespace pair. */ NS_IMETHODIMP nsSchemaValidator::GetType(const nsAString & aType, const nsAString & aNamespace, nsISVSchemaType ** aSchemaType) { nsresult rv; if (!mSchema) { // if we are a built-in type, we can get a nsISVSchemaType for it from // nsISVSchemaCollection->GetType. nsCOMPtr schemaLoader = do_CreateInstance(NS_SVSCHEMALOADER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); mSchema = do_QueryInterface(schemaLoader); NS_ENSURE_STATE(mSchema); } // First try looking for xsi:type rv = mSchema->GetType(aType, aNamespace, aSchemaType); NS_ENSURE_SUCCESS(rv, rv); // maybe its a xsd:element if (!*aSchemaType) { nsCOMPtr schemaElement; rv = mSchema->GetElement(aType, aNamespace, getter_AddRefs(schemaElement)); NS_ENSURE_SUCCESS(rv, rv); if (schemaElement) { rv = schemaElement->GetType(aSchemaType); NS_ENSURE_SUCCESS(rv, rv); } if (!*aSchemaType) { // if its not a built-in type as well, its an unknown schema type. LOG((" Error: The Schema type was not found.\n")); rv = NS_ERROR_SCHEMAVALIDATOR_TYPE_NOT_FOUND; } } return rv; } nsresult nsSchemaValidator::ValidateSimpletype(const nsAString & aNodeValue, nsISVSchemaSimpleType *aSchemaSimpleType, PRBool *aResult) { NS_ENSURE_ARG(aSchemaSimpleType); // get the simpletype type. PRUint16 simpleTypeValue; nsresult rv = aSchemaSimpleType->GetSimpleType(&simpleTypeValue); NS_ENSURE_SUCCESS(rv, rv); PRBool isValid = PR_FALSE; switch (simpleTypeValue) { case nsISVSchemaSimpleType::SIMPLE_TYPE_RESTRICTION: { // handle simpletypes with restrictions rv = ValidateRestrictionSimpletype(aNodeValue, aSchemaSimpleType, &isValid); break; } case nsISVSchemaSimpleType::SIMPLE_TYPE_BUILTIN: { // handle built-in types rv = ValidateBuiltinType(aNodeValue, aSchemaSimpleType, &isValid); break; } case nsISVSchemaSimpleType::SIMPLE_TYPE_LIST: { // handle lists rv = ValidateListSimpletype(aNodeValue, aSchemaSimpleType, nsnull, &isValid); break; } case nsISVSchemaSimpleType::SIMPLE_TYPE_UNION: { // handle unions rv = ValidateUnionSimpletype(aNodeValue, aSchemaSimpleType, &isValid); break; } } *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateDerivedSimpletype(const nsAString & aNodeValue, nsSchemaDerivedSimpleType *aDerived, PRBool *aResult) { // This method is called when validating a simpletype that derives from another // simpletype. PRBool isValid = PR_FALSE; PRUint16 simpleTypeValue; nsresult rv = aDerived->mBaseType->GetSimpleType(&simpleTypeValue); switch (simpleTypeValue) { case nsISVSchemaSimpleType::SIMPLE_TYPE_BUILTIN: { rv = ValidateDerivedBuiltinType(aNodeValue, aDerived, &isValid); break; } case nsISVSchemaSimpleType::SIMPLE_TYPE_RESTRICTION: { // this happens when for example someone derives from a union which then // derives from another type. rv = nsSchemaValidatorUtils::GetDerivedSimpleType(aDerived->mBaseType, aDerived); ValidateDerivedSimpletype(aNodeValue, aDerived, &isValid); break; } case nsISVSchemaSimpleType::SIMPLE_TYPE_LIST: { rv = ValidateListSimpletype(aNodeValue, aDerived->mBaseType, aDerived, &isValid); break; } case nsISVSchemaSimpleType::SIMPLE_TYPE_UNION: { rv = ValidateDerivedUnionSimpletype(aNodeValue, aDerived, &isValid); break; } } *aResult = isValid; return rv; } /** * Simpletype restrictions allow restricting a built-in type with several * facets, such as totalDigits or maxLength. */ nsresult nsSchemaValidator::ValidateRestrictionSimpletype(const nsAString & aNodeValue, nsISVSchemaSimpleType *aType, PRBool *aResult) { PRBool isValid = PR_FALSE; nsCOMPtr restrictionType = do_QueryInterface(aType); NS_ENSURE_STATE(restrictionType); LOG((" The simpletype definition contains restrictions.")); nsCOMPtr simpleBaseType; nsresult rv = restrictionType->GetBaseType(getter_AddRefs(simpleBaseType)); NS_ENSURE_SUCCESS(rv, rv); nsSchemaDerivedSimpleType derivedType; rv = nsSchemaValidatorUtils::GetDerivedSimpleType(aType, &derivedType); rv = ValidateDerivedSimpletype(aNodeValue, &derivedType, &isValid); *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateDerivedBuiltinType(const nsAString & aNodeValue, nsSchemaDerivedSimpleType *aDerived, PRBool *aResult) { PRBool isValid = PR_FALSE; // now that we have loaded all the restriction facets, // check the base type and validate nsCOMPtr schemaBuiltinType = do_QueryInterface(aDerived->mBaseType); NS_ENSURE_STATE(schemaBuiltinType); PRUint16 builtinTypeValue; nsresult rv = schemaBuiltinType->GetBuiltinType(&builtinTypeValue); #ifdef PR_LOGGING DumpBaseType(schemaBuiltinType); #endif switch (builtinTypeValue) { case nsISVSchemaBuiltinType::BUILTIN_TYPE_STRING: { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING: { if (nsSchemaValidatorUtils::IsValidSchemaNormalizedString(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_TOKEN: { if (nsSchemaValidatorUtils::IsValidSchemaToken(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_LANGUAGE: { if (nsSchemaValidatorUtils::IsValidSchemaLanguage(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_BOOLEAN: { rv = ValidateBuiltinTypeBoolean(aNodeValue, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GDAY: { rv = ValidateBuiltinTypeGDay(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GMONTH: { rv = ValidateBuiltinTypeGMonth(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GYEAR: { rv = ValidateBuiltinTypeGYear(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GYEARMONTH: { rv = ValidateBuiltinTypeGYearMonth(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GMONTHDAY: { rv = ValidateBuiltinTypeGMonthDay(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DATE: { rv = ValidateBuiltinTypeDate(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_TIME: { rv = ValidateBuiltinTypeTime(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DATETIME: { rv = ValidateBuiltinTypeDateTime(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DURATION: { rv = ValidateBuiltinTypeDuration(aNodeValue, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_INTEGER: { rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://w3.org/TR/xmlschema-2/#nonPositiveInteger */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER: { if (aDerived->maxExclusive.value.IsEmpty()) { aDerived->maxExclusive.value.AssignLiteral("1"); } else if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("0"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://w3.org/TR/xmlschema-2/#nonNegativeInteger */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER: { if (aDerived->minExclusive.value.IsEmpty()) { aDerived->minExclusive.value.AssignLiteral("-1"); } else if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("0"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#negativeInteger */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER: { if (aDerived->maxExclusive.value.IsEmpty()) { aDerived->maxExclusive.value.AssignLiteral("0"); } else if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("-1"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://w3.org/TR/xmlschema-2/#positiveInteger */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER: { if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("1"); } else if (aDerived->minExclusive.value.IsEmpty()) { aDerived->minExclusive.value.AssignLiteral("0"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#long */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_LONG: { if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("9223372036854775807"); } if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("-9223372036854775808"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#int */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_INT: { if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("2147483647"); } if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("-2147483648"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#short */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_SHORT: { if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("32767"); } if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("-32768"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#unsignedLong */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG: { if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("18446744073709551615"); } if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("0"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#unsignedInt */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT: { if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("4294967295"); } if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("0"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#unsignedShort */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT: { if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("65535"); } if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("0"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#unsignedByte */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE: { if (aDerived->maxInclusive.value.IsEmpty()) { aDerived->maxInclusive.value.AssignLiteral("255"); } if (aDerived->minInclusive.value.IsEmpty()) { aDerived->minInclusive.value.AssignLiteral("0"); } rv = ValidateBuiltinTypeInteger(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_BYTE: { rv = ValidateBuiltinTypeByte(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_FLOAT: { rv = ValidateBuiltinTypeFloat(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DOUBLE: { rv = ValidateBuiltinTypeDouble(aNodeValue, aDerived->totalDigits.value, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DECIMAL: { rv = ValidateBuiltinTypeDecimal(aNodeValue, aDerived->totalDigits.value, aDerived->fractionDigits.value, aDerived->fractionDigits.isDefined, aDerived->maxExclusive.value, aDerived->minExclusive.value, aDerived->maxInclusive.value, aDerived->minInclusive.value, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_ANYURI: { rv = ValidateBuiltinTypeAnyURI(aNodeValue, aDerived->length.value, aDerived->minLength.value, aDerived->maxLength.value, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_BASE64BINARY: { rv = ValidateBuiltinTypeBase64Binary(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_HEXBINARY: { rv = ValidateBuiltinTypeHexBinary(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_QNAME: { rv = ValidateBuiltinTypeQName(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#name */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_NAME: { if (nsSchemaValidatorUtils::IsValidSchemaName(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_NCNAME: { if (nsSchemaValidatorUtils::IsValidSchemaNCName(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_ID: { if (nsSchemaValidatorUtils::IsValidSchemaID(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_IDREF: { if (nsSchemaValidatorUtils::IsValidSchemaIDRef(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_IDREFS: { if (nsSchemaValidatorUtils::IsValidSchemaIDRefs(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_NMTOKEN: { if (nsSchemaValidatorUtils::IsValidSchemaNMToken(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_NMTOKENS: { if (nsSchemaValidatorUtils::IsValidSchemaNMTokens(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, aDerived->length.value, aDerived->length.isDefined, aDerived->minLength.value, aDerived->minLength.isDefined, aDerived->maxLength.value, aDerived->maxLength.isDefined, &aDerived->enumerationList, &isValid); } break; } default: rv = NS_ERROR_SCHEMAVALIDATOR_TYPE_NOT_FOUND; break; } // finally check if a pattern is defined, as all types can be constrained by // regexp patterns. if (isValid && aDerived->pattern.isDefined) { // check if the pattern matches nsCOMPtr regexp = do_GetService(kREGEXP_CID); rv = regexp->RunRegexp(aNodeValue, aDerived->pattern.value, "g", &isValid); NS_ENSURE_SUCCESS(rv, rv); #ifdef PR_LOGGING LOG((" Checking Regular Expression")); if (isValid) { LOG((" -- pattern validates!")); } else { LOG((" -- pattern does not validate!")); } #endif } *aResult = isValid; return rv; } /* Handles validation of built-in schema types. */ nsresult nsSchemaValidator::ValidateBuiltinType(const nsAString & aNodeValue, nsISVSchemaSimpleType *aSchemaSimpleType, PRBool *aResult) { nsCOMPtr builtinType = do_QueryInterface(aSchemaSimpleType); NS_ENSURE_STATE(builtinType); PRUint16 builtinTypeValue; nsresult rv = builtinType->GetBuiltinType(&builtinTypeValue); NS_ENSURE_SUCCESS(rv, rv); #ifdef PR_LOGGING DumpBaseType(builtinType); #endif PRBool isValid = PR_FALSE; switch(builtinTypeValue) { case nsISVSchemaBuiltinType::BUILTIN_TYPE_STRING: { isValid = PR_TRUE; break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING: { if (nsSchemaValidatorUtils::IsValidSchemaNormalizedString(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, 0, PR_FALSE, 0, PR_FALSE, 0, PR_FALSE, nsnull, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_TOKEN: { if (nsSchemaValidatorUtils::IsValidSchemaToken(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, 0, PR_FALSE, 0, PR_FALSE, 0, PR_FALSE, nsnull, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_LANGUAGE: { if (nsSchemaValidatorUtils::IsValidSchemaLanguage(aNodeValue)) { rv = ValidateBuiltinTypeString(aNodeValue, 0, PR_FALSE, 0, PR_FALSE, 0, PR_FALSE, nsnull, &isValid); } break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_BOOLEAN: { rv = ValidateBuiltinTypeBoolean(aNodeValue, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GDAY: { isValid = IsValidSchemaGDay(aNodeValue, nsnull); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GMONTH: { isValid = IsValidSchemaGMonth(aNodeValue, nsnull); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GYEAR: { isValid = IsValidSchemaGYear(aNodeValue, nsnull); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GYEARMONTH: { isValid = IsValidSchemaGYearMonth(aNodeValue, nsnull); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_GMONTHDAY: { isValid = IsValidSchemaGMonthDay(aNodeValue, nsnull); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DATE: { nsSchemaDate tmp; isValid = IsValidSchemaDate(aNodeValue, &tmp); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_TIME: { nsSchemaTime tmp; isValid = IsValidSchemaTime(aNodeValue, &tmp); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DATETIME: { nsSchemaDateTime tmp; isValid = IsValidSchemaDateTime(aNodeValue, &tmp); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DURATION: { nsCOMPtr temp; isValid = IsValidSchemaDuration(aNodeValue, getter_AddRefs(temp)); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_INTEGER: { isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aNodeValue, nsnull); break; } /* http://w3.org/TR/xmlschema-2/#nonPositiveInteger */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER: { // nonPositiveInteger inherits from integer, with maxInclusive // being 0 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("0"), EmptyString(), nsnull, &isValid); break; } /* http://w3.org/TR/xmlschema-2/#nonNegativeInteger */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER: { // nonNegativeInteger inherits from integer, with minInclusive // being 0 (only positive integers) ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), EmptyString(), NS_LITERAL_STRING("0"), nsnull, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#negativeInteger */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER: { // negativeInteger inherits from integer, with maxInclusive // being -1 (only negative integers) ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("-1"), EmptyString(), nsnull, &isValid); break; } /* http://w3.org/TR/xmlschema-2/#positiveInteger */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER: { // positiveInteger inherits from integer, with minInclusive // being 1 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), EmptyString(), NS_LITERAL_STRING("1"), nsnull, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#long */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_LONG: { // maxInclusive is 9223372036854775807 and minInclusive is // -9223372036854775808 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("9223372036854775807"), NS_LITERAL_STRING("-9223372036854775808"), nsnull, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#int */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_INT: { // maxInclusive is 2147483647 and minInclusive is -2147483648 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("2147483647"), NS_LITERAL_STRING("-2147483648"), nsnull, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#short */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_SHORT: { // maxInclusive is 32767 and minInclusive is -32768 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("32767"), NS_LITERAL_STRING("-32768"), nsnull, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#unsignedLong */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG: { // maxInclusive is 18446744073709551615. and minInclusive is 0 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("18446744073709551615"), NS_LITERAL_STRING("0"), nsnull, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#unsignedInt */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT: { // maxInclusive is 4294967295. and minInclusive is 0 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("4294967295"), NS_LITERAL_STRING("0"), nsnull, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#unsignedShort */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT: { // maxInclusive is 65535. and minInclusive is 0 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("65535"), NS_LITERAL_STRING("0"), nsnull, &isValid); break; } /* http://www.w3.org/TR/xmlschema-2/#unsignedByte */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE: { // maxInclusive is 255. and minInclusive is 0 ValidateBuiltinTypeInteger(aNodeValue, nsnull, EmptyString(), EmptyString(), NS_LITERAL_STRING("255"), NS_LITERAL_STRING("0"), nsnull, &isValid); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_BYTE: { isValid = IsValidSchemaByte(aNodeValue, nsnull); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_FLOAT: { isValid = IsValidSchemaFloat(aNodeValue, nsnull); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DOUBLE: { isValid = IsValidSchemaDouble(aNodeValue, nsnull); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_DECIMAL: { nsAutoString wholePart, fractionPart; isValid = IsValidSchemaDecimal(aNodeValue, wholePart, fractionPart); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_ANYURI: { isValid = IsValidSchemaAnyURI(aNodeValue); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_BASE64BINARY: { char* decodedString; isValid = IsValidSchemaBase64Binary(aNodeValue, &decodedString); nsMemory::Free(decodedString); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_HEXBINARY: { isValid = IsValidSchemaHexBinary(aNodeValue); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_QNAME: { isValid = IsValidSchemaQName(aNodeValue); break; } /* http://www.w3.org/TR/xmlschema-2/#name */ case nsISVSchemaBuiltinType::BUILTIN_TYPE_NAME: { isValid = nsSchemaValidatorUtils::IsValidSchemaName(aNodeValue); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_NCNAME: { isValid = nsSchemaValidatorUtils::IsValidSchemaNCName(aNodeValue); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_ID: { isValid = nsSchemaValidatorUtils::IsValidSchemaID(aNodeValue); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_IDREF: { isValid = nsSchemaValidatorUtils::IsValidSchemaIDRef(aNodeValue); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_IDREFS: { isValid = nsSchemaValidatorUtils::IsValidSchemaIDRefs(aNodeValue); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_NMTOKEN: { isValid = nsSchemaValidatorUtils::IsValidSchemaNMToken(aNodeValue); break; } case nsISVSchemaBuiltinType::BUILTIN_TYPE_NMTOKENS: { isValid = nsSchemaValidatorUtils::IsValidSchemaNMTokens(aNodeValue); break; } default: rv = NS_ERROR_SCHEMAVALIDATOR_TYPE_NOT_FOUND; break; } *aResult = isValid; return rv; } /* http://www.w3.org/TR/xmlschema-2/#dt-list */ nsresult nsSchemaValidator::ValidateListSimpletype(const nsAString & aNodeValue, nsISVSchemaSimpleType *aSchemaSimpleType, nsSchemaDerivedSimpleType *aFacets, PRBool *aResult) { /* When a datatype is derived from an list datatype, the following facets apply: * length, maxLength, minLength, enumeration, pattern, whitespace * * The length facets apply to the whole list, ie the number of items in the list. * Patterns are applied to the whole list as well. */ PRBool isValid = PR_FALSE; nsresult rv; nsCOMPtr listType = do_QueryInterface(aSchemaSimpleType, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr listSimpleType; rv = listType->GetListType(getter_AddRefs(listSimpleType)); NS_ENSURE_SUCCESS(rv, rv); if (listSimpleType) { nsCStringArray stringArray; stringArray.ParseString(NS_ConvertUTF16toUTF8(aNodeValue).get(), " \t\r\n"); PRUint32 count = stringArray.Count(); // if facets have been provided, check them first PRBool facetsValid = PR_TRUE; if (aFacets) { if (aFacets->length.isDefined) { if (aFacets->length.value != count) { facetsValid = PR_FALSE; LOG((" Not valid: List is not the right length (%d)", aFacets->length.value)); } } if (facetsValid && aFacets->maxLength.isDefined) { if (aFacets->maxLength.value < count) { facetsValid = PR_FALSE; LOG((" Not valid: Length (%d) of the list is too large", aFacets->maxLength.value)); } } if (facetsValid && aFacets->minLength.isDefined) { if (aFacets->minLength.value > count) { facetsValid = PR_FALSE; LOG((" Not valid: Length (%d) of the list is too small", aFacets->minLength.value)); } } if (facetsValid && aFacets->pattern.isDefined) { // check if the pattern matches nsCOMPtr regexp = do_GetService(kREGEXP_CID); rv = regexp->RunRegexp(aNodeValue, aFacets->pattern.value, "g", &facetsValid); #ifdef PR_LOGGING LOG((" Checking Regular Expression")); if (facetsValid) { LOG((" -- pattern validates!")); } else { LOG((" -- pattern does not validate!")); } #endif } if (facetsValid && aFacets->enumerationList.Count() > 0) { facetsValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, aFacets->enumerationList); } } // either no facets passed in or facets validated fine if (facetsValid) { nsAutoString tmp; for (PRUint32 i=0; i < count; ++i) { CopyUTF8toUTF16(*stringArray[i], tmp); LOG((" Validating List Item (%d): %s", i, NS_ConvertUTF16toUTF8(tmp).get())); rv = ValidateSimpletype(tmp, listSimpleType, &isValid); if (!isValid) break; } } } *aResult = isValid; return rv; } /* http://www.w3.org/TR/xmlschema-2/#dt-union */ nsresult nsSchemaValidator::ValidateUnionSimpletype(const nsAString & aNodeValue, nsISVSchemaSimpleType *aSchemaSimpleType, PRBool *aResult) { PRBool isValid = PR_FALSE; nsresult rv; nsCOMPtr unionType = do_QueryInterface(aSchemaSimpleType, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr unionSimpleType; PRUint32 count; unionType->GetUnionTypeCount(&count); // compare against the union simpletypes in order until a match is found for (PRUint32 i=0; i < count; ++i) { rv = unionType->GetUnionType(i, getter_AddRefs(unionSimpleType)); NS_ENSURE_SUCCESS(rv, rv); LOG((" Validating Untion Type #%d", i)); rv = ValidateSimpletype(aNodeValue, unionSimpleType, &isValid); if (isValid) break; } *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateDerivedUnionSimpletype(const nsAString & aNodeValue, nsSchemaDerivedSimpleType *aDerived, PRBool *aResult) { // This method is called when a simple type is derived from a union type // via restrictions. So we walk all the possible types, and pass in the // loaded restriction facets so that they will override the ones defined // my the union type. We actually have to create a custom // nsSchemaDerivedSimpleType for each type, since we need to change the basetype // and to avoid any new restrictions being added to aDerived. PRBool isValid = PR_FALSE; nsresult rv; nsCOMPtr unionType = do_QueryInterface(aDerived->mBaseType, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr unionSimpleType; PRUint32 count; unionType->GetUnionTypeCount(&count); // compare against the union simpletypes in order until a match is found for (PRUint32 i=0; i < count; ++i) { rv = unionType->GetUnionType(i, getter_AddRefs(unionSimpleType)); NS_ENSURE_SUCCESS(rv, rv); nsSchemaDerivedSimpleType derivedType; nsSchemaValidatorUtils::CopyDerivedSimpleType(&derivedType, aDerived); derivedType.mBaseType = unionSimpleType; LOG((" Validating Union Type #%d", i)); rv = ValidateDerivedSimpletype(aNodeValue, &derivedType, &isValid); if (isValid) break; } *aResult = isValid; return rv; } /* http://www.w3.org/TR/xmlschema-2/#string */ nsresult nsSchemaValidator::ValidateBuiltinTypeString(const nsAString & aNodeValue, PRUint32 aLength, PRBool aLengthDefined, PRUint32 aMinLength, PRBool aMinLengthDefined, PRUint32 aMaxLength, PRBool aMaxLengthDefined, nsStringArray *aEnumerationList, PRBool *aResult) { // XXX needs to check if all chars are legal per spec // IsUTF8(NS_ConvertUTF16toUTF8(theString).get()) PRBool isValid = PR_TRUE; PRUint32 length = aNodeValue.Length(); if (aLengthDefined && (length != aLength)) { isValid = PR_FALSE; LOG((" Not valid: Not the right length (%d)", length)); } if (isValid && aMinLengthDefined && (length < aMinLength)) { isValid = PR_FALSE; LOG((" Not valid: Length (%d) is too small", length)); } if (isValid && aMaxLengthDefined && (length > aMaxLength)) { isValid = PR_FALSE; LOG((" Not valid: Length (%d) is too large", length)); } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } #ifdef PR_LOGGING if (isValid) LOG((" Value is valid!")); #endif *aResult = isValid; return NS_OK; } /* http://www.w3.org/TR/xmlschema-2/#boolean */ nsresult nsSchemaValidator::ValidateBuiltinTypeBoolean(const nsAString & aNodeValue, PRBool *aResult) { // possible values are 0, 1, true, false. Spec makes no mention if TRUE is // valid, so performing a case insensitive comparision to be safe *aResult = aNodeValue.EqualsLiteral("false") || aNodeValue.EqualsLiteral("true") || aNodeValue.EqualsLiteral("1") || aNodeValue.EqualsLiteral("0"); #ifdef PR_LOGGING if (*aResult) { LOG((" Value is valid.")); } else { LOG((" Not valid: Value (%s) is not in {1,0,true,false}.", NS_ConvertUTF16toUTF8(aNodeValue).get())); } #endif return NS_OK; } /* http://www.w3.org/TR/xmlschema-2/#gDay */ nsresult nsSchemaValidator::ValidateBuiltinTypeGDay(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { PRBool isValidGDay; nsSchemaGDay gday; PRBool isValid = IsValidSchemaGDay(aNodeValue, &gday); if (isValid && !aMaxExclusive.IsEmpty()) { nsSchemaGDay maxExclusive; isValidGDay = IsValidSchemaGDay(aMaxExclusive, &maxExclusive); if (isValidGDay) { if (gday.day >= maxExclusive.day) { isValid = PR_FALSE; LOG((" Not valid: Value (%d) is too large", gday.day)); } } } if (isValid && !aMinExclusive.IsEmpty()) { nsSchemaGDay minExclusive; isValidGDay = IsValidSchemaGDay(aMinExclusive, &minExclusive); if (isValidGDay) { if (gday.day <= minExclusive.day) { isValid = PR_FALSE; LOG((" Not valid: Value (%d) is too small", gday.day)); } } } if (isValid && !aMaxInclusive.IsEmpty()) { nsSchemaGDay maxInclusive; isValidGDay = IsValidSchemaGDay(aMaxInclusive, &maxInclusive); if (isValidGDay) { if (gday.day > maxInclusive.day) { isValid = PR_FALSE; LOG((" Not valid: Value (%d) is too large", gday.day)); } } } if (isValid && !aMinInclusive.IsEmpty()) { nsSchemaGDay minInclusive; isValidGDay = IsValidSchemaGDay(aMinInclusive, &minInclusive); if (isValidGDay) { if (gday.day < minInclusive.day) { isValid = PR_FALSE; LOG((" Not valid: Value (%d) is too small", gday.day)); } } } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaGDay(const nsAString & aNodeValue, nsSchemaGDay *aResult) { // GDay looks like this: ---DD(Z|(+|-)hh:mm) PRUint32 strLength = aNodeValue.Length(); // ---DD ---DDZ ---DD(+|-)hh:mm if (strLength != 5 && strLength != 6 && strLength != 11) return PR_FALSE; char timezoneHour[3] = ""; char timezoneMinute[3] = ""; PRUnichar tzSign = PRUnichar(' '); int dayInt; // iterate over the string and parse/validate const PRUnichar *start, *end; aNodeValue.BeginReading(&start, &end); nsAutoString nodeValue(aNodeValue); // validate the ---DD part PRBool isValid = Substring(start, start + 3).EqualsLiteral("---") && IsValidSchemaGType(Substring(start + 3, start + 5), 1, 31, &dayInt); if (isValid) { tzSign = nodeValue.CharAt(5); if (strLength == 6) { isValid &= (tzSign == 'Z'); } else if (strLength == 11) { const nsAString &tz = Substring(start + 6, end); isValid &= ((tzSign == '+' || tzSign == '-') && nsSchemaValidatorUtils::ParseSchemaTimeZone(tz, timezoneHour, timezoneMinute)); } } if (isValid && aResult) { char * pEnd; aResult->day = dayInt; aResult->tz_negative = (tzSign == '-') ? PR_TRUE : PR_FALSE; aResult->tz_hour = (timezoneHour[0] == '\0') ? nsnull : strtol(timezoneHour, &pEnd, 10); aResult->tz_minute = (timezoneMinute[0] == '\0') ? nsnull : strtol(timezoneMinute, &pEnd, 10); } return isValid; } // Helper function used to validate gregorian date types PRBool nsSchemaValidator::IsValidSchemaGType(const nsAString & aNodeValue, long aMinValue, long aMaxValue, int *aResult) { long intValue; PRBool isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aNodeValue, &intValue, PR_TRUE); *aResult = intValue; return isValid && (intValue >= aMinValue) && (intValue <= aMaxValue); } /* http://www.w3.org/TR/xmlschema-2/#gMonth */ nsresult nsSchemaValidator::ValidateBuiltinTypeGMonth(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { nsSchemaGMonth gmonth; PRBool isValid = IsValidSchemaGMonth(aNodeValue, &gmonth); if (isValid && !aMaxExclusive.IsEmpty()) { nsSchemaGMonth maxExclusive; if (IsValidSchemaGMonth(aMaxExclusive, &maxExclusive) && gmonth.month >= maxExclusive.month) { isValid = PR_FALSE; LOG((" Not valid: Value (%d) is too large", gmonth.month)); } } if (isValid && !aMinExclusive.IsEmpty()) { nsSchemaGMonth minExclusive; if (IsValidSchemaGMonth(aMinExclusive, &minExclusive) && gmonth.month <= minExclusive.month) { isValid = PR_FALSE; LOG((" Not valid: Value (%d) is too small", gmonth.month)); } } if (isValid && !aMaxInclusive.IsEmpty()) { nsSchemaGMonth maxInclusive; if (IsValidSchemaGMonth(aMaxInclusive, &maxInclusive) && gmonth.month > maxInclusive.month) { isValid = PR_FALSE; LOG((" Not valid: Value (%d) is too large", gmonth.month)); } } if (isValid && !aMinInclusive.IsEmpty()) { nsSchemaGMonth minInclusive; if (IsValidSchemaGMonth(aMinInclusive, &minInclusive) && gmonth.month < minInclusive.month) { isValid = PR_FALSE; LOG((" Not valid: Value (%d) is too small", gmonth.month)); } } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaGMonth(const nsAString & aNodeValue, nsSchemaGMonth *aResult) { // GMonth looks like this: --MM(Z|(+|-)hh:mm) PRUint32 strLength = aNodeValue.Length(); // --MM --MMZ --MM(+|-)hh:mm if ((strLength != 4) && (strLength != 5) && (strLength != 10)) return PR_FALSE; char timezoneHour[3] = ""; char timezoneMinute[3] = ""; PRUnichar tzSign = 0; int monthInt; const PRUnichar *start, *end; aNodeValue.BeginReading(&start, &end); nsAutoString nodeValue(aNodeValue); // validate the --MM part PRBool isValid = Substring(start, start + 2).EqualsLiteral("--") && IsValidSchemaGType(Substring(start + 2, start + 4), 1, 12, &monthInt); if (isValid) { tzSign = nodeValue.CharAt(4); if (strLength == 5) { isValid &= (tzSign == 'Z'); } else if (strLength == 10) { const nsAString &tz = Substring(start + 5, end); isValid &= ((tzSign == '+' || tzSign == '-') && nsSchemaValidatorUtils::ParseSchemaTimeZone(tz, timezoneHour, timezoneMinute)); } } if (isValid && aResult) { char * pEnd; aResult->month = monthInt; aResult->tz_negative = (tzSign == '-') ? PR_TRUE : PR_FALSE; aResult->tz_hour = (timezoneHour[0] == '\0') ? nsnull : strtol(timezoneHour, &pEnd, 10); aResult->tz_minute = (timezoneMinute[0] == '\0') ? nsnull : strtol(timezoneMinute, &pEnd, 10); } return isValid; } /* http://www.w3.org/TR/xmlschema-2/#gYear */ nsresult nsSchemaValidator::ValidateBuiltinTypeGYear(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { nsSchemaGYear gyear; PRBool isValid = IsValidSchemaGYear(aNodeValue, &gyear); if (isValid && !aMaxExclusive.IsEmpty()) { nsSchemaGYear maxExclusive; if (IsValidSchemaGYear(aMaxExclusive, &maxExclusive) && gyear.year >= maxExclusive.year) { isValid = PR_FALSE; LOG((" Not valid: Value (%ld) is too large", gyear.year)); } } if (isValid && !aMinExclusive.IsEmpty()) { nsSchemaGYear minExclusive; if (IsValidSchemaGYear(aMinExclusive, &minExclusive) && gyear.year <= minExclusive.year) { isValid = PR_FALSE; LOG((" Not valid: Value (%ld) is too small", gyear.year)); } } if (isValid && !aMaxInclusive.IsEmpty()) { nsSchemaGYear maxInclusive; if (IsValidSchemaGYear(aMaxInclusive, &maxInclusive) && gyear.year > maxInclusive.year) { isValid = PR_FALSE; LOG((" Not valid: Value (%ld) is too large", gyear.year)); } } if (isValid && !aMinInclusive.IsEmpty()) { nsSchemaGYear minInclusive; if (IsValidSchemaGYear(aMinInclusive, &minInclusive) && gyear.year < minInclusive.year) { isValid = PR_FALSE; LOG((" Not valid: Value (%ld) is too small", gyear.year)); } } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaGYear(const nsAString & aNodeValue, nsSchemaGYear *aResult) { // GYear looks like this: (-)CCYY(Z|(+|-)hh:mm) PRBool isValid = PR_FALSE; PRUint32 strLength = aNodeValue.Length(); char timezoneHour[3] = ""; char timezoneMinute[3] = ""; PRUnichar tzSign = 0; const PRUnichar *start, *end, *buffStart; aNodeValue.BeginReading(&start, &end); aNodeValue.BeginReading(&buffStart); PRUint32 state = 0; PRUint32 buffLength = 0, yearLength = 0; PRBool done = PR_FALSE; long yearNum; if (aNodeValue.First() == '-') { // jump the negative sign *start++; buffLength++; } while ((start != end) && !done) { // 0 - year // 1 - timezone if (state == 0) { // year PRUnichar temp = *start++; // walk till the first non-numerical char while (((temp >= '0') && (temp <= '9')) && (buffLength < strLength)) { buffLength++; temp = *start++; yearLength++; } // if we are not at the end of the string, back up one, as we hit the // timezone separator. if (buffLength < strLength) *start--; // need a minimum of 4 digits for year if (yearLength >= 4) { isValid = nsSchemaValidatorUtils::IsValidSchemaInteger( Substring(buffStart, start), &yearNum, PR_TRUE); } // 0 is an invalid year per the spec if (isValid && (yearNum == 0)) isValid = PR_FALSE; if (isValid && (strLength > buffLength)) { state = 1; } else { done = PR_TRUE; } } else if (state == 1) { // timezone int lengthDiff = (strLength - buffLength); if (lengthDiff == 0){ // timezone is optional isValid = PR_TRUE; } else if (lengthDiff == 1) { // timezone is one character, so has to be 'Z' isValid = (*start == 'Z'); } else if (lengthDiff == 6){ // timezone is (+|-)hh:mm tzSign = *start++; if ((tzSign == '+') || (tzSign == '-')) { isValid = nsSchemaValidatorUtils::ParseSchemaTimeZone( Substring(start, end), timezoneHour, timezoneMinute); } } else { // invalid length for timezone isValid = PR_FALSE; } done = PR_TRUE; } } if (isValid && aResult) { char * pEnd; aResult->year = yearNum; aResult->tz_negative = (tzSign == '-') ? PR_TRUE : PR_FALSE; aResult->tz_hour = (timezoneHour[0] == '\0') ? nsnull : strtol(timezoneHour, &pEnd, 10); aResult->tz_minute = (timezoneMinute[0] == '\0') ? nsnull : strtol(timezoneMinute, &pEnd, 10); } return isValid; } /* http://www.w3.org/TR/xmlschema-2/#gYearMonth */ nsresult nsSchemaValidator::ValidateBuiltinTypeGYearMonth(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { nsSchemaGYearMonth gyearmonth; PRBool isValid = IsValidSchemaGYearMonth(aNodeValue, &gyearmonth); if (isValid && !aMaxExclusive.IsEmpty()) { nsSchemaGYearMonth maxExclusive; if (IsValidSchemaGYearMonth(aMaxExclusive, &maxExclusive) && (nsSchemaValidatorUtils::CompareGYearMonth(gyearmonth, maxExclusive) > -1)) { isValid = PR_FALSE; LOG((" Not valid: Value (%ld-%d) is too large", gyearmonth.gYear.year, gyearmonth.gMonth.month)); } } if (isValid && !aMinExclusive.IsEmpty()) { nsSchemaGYearMonth minExclusive; if (IsValidSchemaGYearMonth(aMinExclusive, &minExclusive) && (nsSchemaValidatorUtils::CompareGYearMonth(gyearmonth, minExclusive) < 1)) { isValid = PR_FALSE; LOG((" Not valid: Value (%ld-%d) is too small", gyearmonth.gYear.year, gyearmonth.gMonth.month)); } } if (isValid && !aMaxInclusive.IsEmpty()) { nsSchemaGYearMonth maxInclusive; if (IsValidSchemaGYearMonth(aMaxInclusive, &maxInclusive) && (nsSchemaValidatorUtils::CompareGYearMonth(gyearmonth, maxInclusive) > 0)) { isValid = PR_FALSE; LOG((" Not valid: Value (%ld-%d) is too large", gyearmonth.gYear.year, gyearmonth.gMonth.month)); } } if (isValid && !aMinInclusive.IsEmpty()) { nsSchemaGYearMonth minInclusive; if (IsValidSchemaGYearMonth(aMinInclusive, &minInclusive) && (nsSchemaValidatorUtils::CompareGYearMonth(gyearmonth, minInclusive) < 0)) { isValid = PR_FALSE; LOG((" Not valid: Value (%ld-%d) is too small", gyearmonth.gYear.year, gyearmonth.gMonth.month)); } } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaGYearMonth(const nsAString & aNodeValue, nsSchemaGYearMonth *aYearMonth) { // GYearMonth looks like this: (-)CCYY-MM(Z|(+|-)hh:mm) PRBool isValid = PR_FALSE; const PRUnichar *start, *end, *buffStart; aNodeValue.BeginReading(&start, &end); aNodeValue.BeginReading(&buffStart); PRUint32 buffLength = 0; PRBool done = PR_FALSE; PRUint32 strLength = aNodeValue.Length(); if (aNodeValue.First() == '-') { // jump the negative sign *start++; *buffStart++; buffLength++; } while ((start != end) && !done) { if (*start++ == '-') { // separator found int monthLength = strLength - buffLength; // -MMZ -MM(+|-)hh:mm -MM if ((monthLength != 4) && (monthLength != 9) && (monthLength != 3)) return PR_FALSE; // validate year nsAutoString year; // back one up since we have the separator included year = Substring(buffStart, --start); isValid = IsValidSchemaGYear(year, aYearMonth ? &aYearMonth->gYear : nsnull); if (isValid) { nsAutoString month; month.AppendLiteral("--"); start++; month.Append(Substring(start, end)); isValid = IsValidSchemaGMonth(month, aYearMonth ? &aYearMonth->gMonth : nsnull); } done = PR_TRUE; } else { buffLength++; } } return isValid; } /* http://www.w3.org/TR/xmlschema-2/#gMonthDay */ nsresult nsSchemaValidator::ValidateBuiltinTypeGMonthDay(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { nsSchemaGMonthDay gmonthday; PRBool isValid = IsValidSchemaGMonthDay(aNodeValue, &gmonthday); if (isValid && !aMaxExclusive.IsEmpty()) { nsSchemaGMonthDay maxExclusive; if (IsValidSchemaGMonthDay(aMaxExclusive, &maxExclusive) && (nsSchemaValidatorUtils::CompareGMonthDay(gmonthday, maxExclusive) > -1)) { isValid = PR_FALSE; LOG((" Not valid: Value (%d-%d) is too large", gmonthday.gMonth.month, gmonthday.gDay.day)); } } if (isValid && !aMinExclusive.IsEmpty()) { nsSchemaGMonthDay minExclusive; if (IsValidSchemaGMonthDay(aMinExclusive, &minExclusive) && (nsSchemaValidatorUtils::CompareGMonthDay(gmonthday, minExclusive) < 1)) { isValid = PR_FALSE; LOG((" Not valid: Value (%d-%d) is too small", gmonthday.gMonth.month, gmonthday.gDay.day)); } } if (isValid && !aMaxInclusive.IsEmpty()) { nsSchemaGMonthDay maxInclusive; if (IsValidSchemaGMonthDay(aMaxInclusive, &maxInclusive) && (nsSchemaValidatorUtils::CompareGMonthDay(gmonthday, maxInclusive) > 0)) { isValid = PR_FALSE; LOG((" Not valid: Value (%d-%d) is too large", gmonthday.gMonth.month, gmonthday.gDay.day)); } } if (isValid && !aMinInclusive.IsEmpty()) { nsSchemaGMonthDay minInclusive; if (IsValidSchemaGMonthDay(aMinInclusive, &minInclusive) && (nsSchemaValidatorUtils::CompareGMonthDay(gmonthday, minInclusive) < 0)) { isValid = PR_FALSE; LOG((" Not valid: Value (%d-%d) is too small", gmonthday.gMonth.month, gmonthday.gDay.day)); } } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaGMonthDay(const nsAString & aNodeValue, nsSchemaGMonthDay *aMonthDay) { // GMonthDay looks like this: --MM-DD(Z|(+|-)hh:mm) PRBool isValid = PR_FALSE; const PRUnichar *start, *end, *buffStart; aNodeValue.BeginReading(&start, &end); aNodeValue.BeginReading(&buffStart); PRUint32 buffLength = 0; PRUint32 state = 0; PRBool done = PR_FALSE; PRUint32 strLength = aNodeValue.Length(); // --MM-DD --MM-DDZ --MM-DD(+|-)hh:mm if ((strLength != 7) && (strLength != 8) && (strLength != 13)) return PR_FALSE; while ((start != end) && !done) { if (state == 0) { // separator if (*start++ == '-') { buffLength++; if (buffLength == 2) { state = 1; buffStart = start; } } else { done = PR_TRUE; } } else if (state == 1) { // month part - construct an --MM-- nsAutoString month; month.AppendLiteral("--"); start += 2; month.Append(Substring(buffStart, start)); isValid = IsValidSchemaGMonth(month, aMonthDay ? &aMonthDay->gMonth : nsnull); if (isValid) { buffStart = start; state = 2; } else { done = PR_TRUE; } } else if (state == 2) { // day part - construct ---DD(timezone) nsAutoString day; day.AppendLiteral("---"); day.Append(Substring(++buffStart, end)); isValid = IsValidSchemaGDay(day, aMonthDay ? &aMonthDay->gDay : nsnull); done = PR_TRUE; } } return isValid; } /* http://www.w3.org/TR/xmlschema-2/#time */ nsresult nsSchemaValidator::ValidateBuiltinTypeTime(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { nsSchemaTime time; int timeCompare; PRBool isValid = IsValidSchemaTime(aNodeValue, &time); if (isValid && !aMinExclusive.IsEmpty()) { nsSchemaTime minExclusive; if (IsValidSchemaTime(aMinExclusive, &minExclusive)) { timeCompare = nsSchemaValidatorUtils::CompareTime(time, minExclusive); if (timeCompare < 1) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } } if (isValid && !aMaxExclusive.IsEmpty()) { nsSchemaTime maxExclusive; if (IsValidSchemaTime(aMaxExclusive, &maxExclusive)) { timeCompare = nsSchemaValidatorUtils::CompareTime(time, maxExclusive); if (timeCompare > -1) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } } if (isValid && !aMaxInclusive.IsEmpty()) { nsSchemaTime maxInclusive; if (IsValidSchemaTime(aMaxInclusive, &maxInclusive)) { timeCompare = nsSchemaValidatorUtils::CompareTime(time, maxInclusive); if (timeCompare > 0) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } } if (isValid && !aMinInclusive.IsEmpty()) { nsSchemaTime minInclusive; if (IsValidSchemaTime(aMinInclusive, &minInclusive)) { timeCompare = nsSchemaValidatorUtils::CompareTime(time, minInclusive); if (timeCompare < 0) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } } *aResult = isValid; return NS_OK; } NS_IMETHODIMP nsSchemaValidator::ValidateBuiltinTypeTime(const nsAString & aValue, PRTime *aResult) { nsresult rv = NS_OK; nsSchemaTime time; PRBool isValid = IsValidSchemaTime(aValue, &time); if (isValid) { char fulldate[100] = ""; // 22-AUG-1993 10:59:12.82 sprintf(fulldate, "22-AUG-1993 %d:%d:%d.%u", time.hour, time.minute, time.second, time.millisecond); PRBool isGMT = nsSchemaValidatorUtils::IsGMT(aValue); PR_ParseTimeString(fulldate, isGMT ? PR_TRUE : PR_FALSE, aResult); } else { *aResult = nsnull; rv = NS_ERROR_ILLEGAL_VALUE; } return rv; } PRBool nsSchemaValidator::IsValidSchemaTime(const nsAString & aNodeValue, nsSchemaTime *aResult) { PRBool isValid = PR_FALSE; nsAutoString timeString(aNodeValue); PRUint32 len = timeString.Length(); PRUnichar end = 0; if (len > 0) { // length counts null terminator end = timeString.CharAt(len - 1); } // if no timezone ([+/-]hh:ss) or no timezone Z, add a Z to the end so that // nsSchemaValidatorUtils::ParseSchemaTime can parse it if ((timeString.FindChar('-') == kNotFound) && (timeString.FindChar('+') == kNotFound) && end != 'Z') { timeString.Append('Z'); } LOG((" Validating Time: ")); isValid = nsSchemaValidatorUtils::ParseSchemaTime(timeString, aResult); return isValid; } /* http://www.w3.org/TR/xmlschema-2/#date */ nsresult nsSchemaValidator::ValidateBuiltinTypeDate(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { PRBool isValid = PR_FALSE; nsSchemaDate date; int dateCompare; isValid = IsValidSchemaDate(aNodeValue, &date); if (isValid && !aMaxExclusive.IsEmpty()) { nsSchemaDate maxExclusive; if (IsValidSchemaDate(aMaxExclusive, &maxExclusive)) { dateCompare = nsSchemaValidatorUtils::CompareDate(date, maxExclusive); if (dateCompare > -1) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } } if (isValid && !aMinExclusive.IsEmpty()) { nsSchemaDate minExclusive; if (IsValidSchemaDate(aMinExclusive, &minExclusive)) { dateCompare = nsSchemaValidatorUtils::CompareDate(date, minExclusive); if (dateCompare < 1) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } } if (isValid && !aMaxInclusive.IsEmpty()) { nsSchemaDate maxInclusive; if (IsValidSchemaDate(aMaxInclusive, &maxInclusive)) { dateCompare = nsSchemaValidatorUtils::CompareDate(date, maxInclusive); if (dateCompare > 0) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } } if (isValid && !aMinInclusive.IsEmpty()) { nsSchemaDate minInclusive; if (IsValidSchemaDate(aMinInclusive, &minInclusive)) { dateCompare = nsSchemaValidatorUtils::CompareDate(date, minInclusive); if (dateCompare < 0) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } } *aResult = isValid; return NS_OK; } NS_IMETHODIMP nsSchemaValidator::ValidateBuiltinTypeDate(const nsAString & aValue, PRTime *aResult) { nsresult rv = NS_OK; nsSchemaDate date; PRBool isValid = IsValidSchemaDate(aValue, &date); if (isValid) { char fulldate[100] = ""; nsCAutoString monthShorthand; nsSchemaValidatorUtils::GetMonthShorthand(date.month, monthShorthand); // 22-AUG-1993 10:59:12.82 sprintf(fulldate, "%d-%s-%u 00:00:00", date.day, monthShorthand.get(), date.year); PRBool isGMT = nsSchemaValidatorUtils::IsGMT(aValue); PR_ParseTimeString(fulldate, isGMT ? PR_TRUE : PR_FALSE, aResult); } else { *aResult = nsnull; rv = NS_ERROR_ILLEGAL_VALUE; } return rv; } /* http://www.w3.org/TR/xmlschema-2/#dateTime */ nsresult nsSchemaValidator::ValidateBuiltinTypeDateTime(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { nsSchemaDateTime dateTime; PRBool isValid = IsValidSchemaDateTime(aNodeValue, &dateTime); if (isValid && !aMaxExclusive.IsEmpty()) { nsSchemaDateTime maxExclusive; if (IsValidSchemaDateTime(aMaxExclusive, &maxExclusive) && CompareSchemaDateTime(dateTime, maxExclusive) > -1) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } if (isValid && !aMinExclusive.IsEmpty()) { nsSchemaDateTime minExclusive; if (IsValidSchemaDateTime(aMinExclusive, &minExclusive) && CompareSchemaDateTime(dateTime, minExclusive) < 1) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } if (isValid && !aMaxInclusive.IsEmpty()) { nsSchemaDateTime maxInclusive; if (IsValidSchemaDateTime(aMaxInclusive, &maxInclusive) && CompareSchemaDateTime(dateTime, maxInclusive) > 0) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } if (isValid && !aMinInclusive.IsEmpty()) { nsSchemaDateTime minInclusive; if (IsValidSchemaDateTime(aMinInclusive, &minInclusive) && CompareSchemaDateTime(dateTime, minInclusive) < 0) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } *aResult = isValid; return NS_OK; } NS_IMETHODIMP nsSchemaValidator::ValidateBuiltinTypeDateTime(const nsAString & aValue, PRTime *aResult) { nsresult rv = NS_OK; nsSchemaDateTime dateTime; PRBool isValid = IsValidSchemaDateTime(aValue, &dateTime); if (isValid) { char fulldate[100] = ""; nsCAutoString monthShorthand; nsSchemaValidatorUtils::GetMonthShorthand(dateTime.date.month, monthShorthand); // 22-AUG-1993 10:59:12.82 sprintf(fulldate, "%d-%s-%u %d:%d:%d.%u", dateTime.date.day, monthShorthand.get(), dateTime.date.year, dateTime.time.hour, dateTime.time.minute, dateTime.time.second, dateTime.time.millisecond); PRBool isGMT = nsSchemaValidatorUtils::IsGMT(aValue); PR_ParseTimeString(fulldate, isGMT ? PR_TRUE : PR_FALSE, aResult); } else { *aResult = nsnull; rv = NS_ERROR_ILLEGAL_VALUE; } return rv; } int nsSchemaValidator::CompareSchemaDateTime(nsSchemaDateTime datetime1, nsSchemaDateTime datetime2) { return nsSchemaValidatorUtils::CompareDateTime(datetime1, datetime2); } PRBool nsSchemaValidator::IsValidSchemaDate(const nsAString & aNodeValue, nsSchemaDate *aResult) { PRBool isValid = PR_FALSE; LOG((" Validating Date:")); if (aNodeValue.IsEmpty()) return PR_FALSE; nsAutoString dateString(aNodeValue); if (dateString.First() == '-') { aResult->isNegative = PR_TRUE; } /* http://www.w3.org/TR/xmlschema-2/#date (-)CCYY-MM-DD then optionally either: Z or [+/-]hh:mm */ isValid = nsSchemaValidatorUtils::ParseSchemaDate(dateString, PR_TRUE, aResult); return isValid; } PRBool nsSchemaValidator::IsValidSchemaDateTime(const nsAString & aNodeValue, nsSchemaDateTime *aResult) { return nsSchemaValidatorUtils::ParseDateTime(aNodeValue, aResult); } /* http://w3.org/TR/xmlschema-2/#duration */ nsresult nsSchemaValidator::ValidateBuiltinTypeDuration(const nsAString & aNodeValue, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, PRBool *aResult) { PRBool isValid = PR_FALSE; nsCOMPtr duration; isValid = IsValidSchemaDuration(aNodeValue, getter_AddRefs(duration)); if (isValid && !aMaxExclusive.IsEmpty()) { nsCOMPtr maxExclusiveDuration; if (IsValidSchemaDuration(aMaxExclusive, getter_AddRefs(maxExclusiveDuration)) && nsSchemaValidatorUtils::CompareDurations(duration, maxExclusiveDuration) == 1){ isValid = PR_FALSE; LOG((" Not valid: Value is too large or indeterminate")); } } if (isValid && !aMinExclusive.IsEmpty()) { nsCOMPtr minExclusiveDuration; if (IsValidSchemaDuration(aMinExclusive, getter_AddRefs(minExclusiveDuration)) && nsSchemaValidatorUtils::CompareDurations(minExclusiveDuration, duration) == 1){ isValid = PR_FALSE; LOG((" Not valid: Value is too small or indeterminate")); } } if (isValid && !aMaxInclusive.IsEmpty()) { nsCOMPtr maxInclusiveDuration; if (IsValidSchemaDuration(aMaxInclusive, getter_AddRefs(maxInclusiveDuration)) && nsSchemaValidatorUtils::CompareDurations(duration, maxInclusiveDuration) == 1){ isValid = PR_FALSE; LOG((" Not valid: Value is too large or indeterminate")); } } if (isValid && !aMinInclusive.IsEmpty()) { nsCOMPtr minInclusiveDuration; if (IsValidSchemaDuration(aMinInclusive, getter_AddRefs(minInclusiveDuration)) && nsSchemaValidatorUtils::CompareDurations(minInclusiveDuration, duration) == 1){ isValid = PR_FALSE; LOG((" Not valid: Value is too small or indeterminate")); } } *aResult = isValid; return NS_OK; } // scriptable method for validating and parsing durations NS_IMETHODIMP nsSchemaValidator::ValidateBuiltinTypeDuration(const nsAString & aValue, nsISchemaDuration **aDuration) { nsresult rv = NS_OK; *aDuration = nsnull; nsCOMPtr duration; if (IsValidSchemaDuration(aValue, getter_AddRefs(duration))) { duration.swap(*aDuration); } else { rv = NS_ERROR_ILLEGAL_VALUE; } return rv; } PRBool nsSchemaValidator::IsValidSchemaDuration(const nsAString & aNodeValue, nsISchemaDuration **aResult) { PRBool isValid = PR_FALSE; nsCOMPtr duration; isValid = nsSchemaValidatorUtils::ParseSchemaDuration(aNodeValue, getter_AddRefs(duration)); duration.swap(*aResult); return isValid; } /* http://w3.org/TR/xmlschema-2/#integer */ nsresult nsSchemaValidator::ValidateBuiltinTypeInteger(const nsAString & aNodeValue, PRUint32 aTotalDigits, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, nsStringArray *aEnumerationList, PRBool *aResult) { PRBool isValid = PR_FALSE; long intValue; isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aNodeValue, &intValue); if (isValid && aTotalDigits) { PRUint32 length = aNodeValue.Length(); if (aNodeValue.First() == PRUnichar('-')) length -= 1; if (length > aTotalDigits) { isValid = PR_FALSE; LOG((" Not valid: Too many digits (%d)", length)); } } if (isValid && !aMaxExclusive.IsEmpty()){ long maxExclusive; if (nsSchemaValidatorUtils::IsValidSchemaInteger(aMaxExclusive, &maxExclusive) && (nsSchemaValidatorUtils::CompareStrings(aNodeValue, aMaxExclusive) >= 0)) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } if (isValid && !aMinExclusive.IsEmpty()){ long minExclusive; if (nsSchemaValidatorUtils::IsValidSchemaInteger(aMinExclusive, &minExclusive) && (nsSchemaValidatorUtils::CompareStrings(aNodeValue, aMinExclusive) <= 0)) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } if (isValid && !aMaxInclusive.IsEmpty()){ long maxInclusive; if (nsSchemaValidatorUtils::IsValidSchemaInteger(aMaxInclusive, &maxInclusive) && (nsSchemaValidatorUtils::CompareStrings(aNodeValue, aMaxInclusive) > 0)) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } if (isValid && !aMinInclusive.IsEmpty()){ long minInclusive; if (nsSchemaValidatorUtils::IsValidSchemaInteger(aMinInclusive, &minInclusive) && (nsSchemaValidatorUtils::CompareStrings(aNodeValue, aMinInclusive) < 0)) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } *aResult = isValid; return NS_OK; } nsresult nsSchemaValidator::ValidateBuiltinTypeByte(const nsAString & aNodeValue, PRUint32 aTotalDigits, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, nsStringArray *aEnumerationList, PRBool *aResult) { PRBool isValid = PR_FALSE; long intValue; isValid = IsValidSchemaByte(aNodeValue, &intValue); if (aTotalDigits) { PRUint32 length = aNodeValue.Length(); if (aNodeValue.First() == PRUnichar('-')) length -= 1; if (length > aTotalDigits) { isValid = PR_FALSE; LOG((" Not valid: Too many digits (%d)", length)); } } if (isValid && !aMaxExclusive.IsEmpty()){ long maxExclusive; if (IsValidSchemaByte(aMaxExclusive, &maxExclusive) && (intValue >= maxExclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } if (isValid && !aMinExclusive.IsEmpty()){ long minExclusive; if (IsValidSchemaByte(aMinExclusive, &minExclusive) && (intValue <= minExclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } if (isValid && !aMaxInclusive.IsEmpty()){ long maxInclusive; if (IsValidSchemaByte(aMaxInclusive, &maxInclusive) && (intValue > maxInclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value is too large")); } } if (isValid && !aMinInclusive.IsEmpty()){ long minInclusive; if (IsValidSchemaByte(aMinInclusive, &minInclusive) && (intValue < minInclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value is too small")); } } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } *aResult = isValid; return NS_OK; } /* http://w3.org/TR/xmlschema-2/#byte */ PRBool nsSchemaValidator::IsValidSchemaByte(const nsAString & aNodeValue, long *aResult) { PRBool isValid = PR_FALSE; long byteValue; isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aNodeValue, &byteValue, PR_TRUE); if (isValid && ((byteValue > 127) || (byteValue < -128))) isValid = PR_FALSE; if (aResult) *aResult = byteValue; return isValid; } /* http://www.w3.org/TR/xmlschema-2/#float */ nsresult nsSchemaValidator::ValidateBuiltinTypeFloat(const nsAString & aNodeValue, PRUint32 aTotalDigits, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, nsStringArray *aEnumerationList, PRBool *aResult) { PRBool isValid = PR_FALSE; float floatValue; isValid = IsValidSchemaFloat(aNodeValue, &floatValue); if (isValid && !aMaxExclusive.IsEmpty()){ // If there is a facet and value is NaN, // then it is not valid. if (aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; LOG((" Not valid: Value NaN can't be constrained by facets")); } else { float maxExclusive; if (IsValidSchemaFloat(aMaxExclusive, &maxExclusive) && (floatValue >= maxExclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value (%f) is too big", floatValue)); } } } if (isValid && !aMinExclusive.IsEmpty()){ // If there is a facet and value is NaN, // then it is not valid. if (aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; LOG((" Not valid: Value NaN can't be constrained by facets")); } else { float minExclusive; if (IsValidSchemaFloat(aMinExclusive, &minExclusive) && (floatValue <= minExclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value (%f) is too small", floatValue)); } } } if (isValid && !aMaxInclusive.IsEmpty()){ // If there is a facet and value is NaN, // then it is not valid. if (aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; LOG((" Not valid: Value NaN can't be constrained by facets")); } else { float maxInclusive; if (IsValidSchemaFloat(aMaxInclusive, &maxInclusive) && (floatValue > maxInclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value (%f) is too big", floatValue)); } } } if (isValid && !aMinInclusive.IsEmpty()){ // If there is a facet and value is NaN, // then it is not valid. if (aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; LOG((" Not valid: Value NaN can't be constrained by facets")); } else { float minInclusive; if (IsValidSchemaFloat(aMinInclusive, &minInclusive) && (floatValue < minInclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value (%f) is too small", floatValue)); } } } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaFloat(const nsAString & aNodeValue, float *aResult) { PRBool isValid = PR_TRUE; nsresult rv = NS_ERROR_ILLEGAL_VALUE; float floatValue = 0.0f; // took most of this float conversion code from nsCString::ToFloat // which isn't frozen so we can't use it directly NS_LossyConvertUTF16toASCII convertedValue(aNodeValue); const char *value = convertedValue.get(); PRUint32 length = strlen(value); if (length > 0) { char *conv_stopped; floatValue = (float)PR_strtod(value, &conv_stopped); if (conv_stopped == value+length) { rv = NS_OK; } else { // the scan failed somewhere before completion rv = NS_ERROR_ILLEGAL_VALUE; floatValue = 0.0f; } } if (NS_FAILED(rv)) { // floats may be INF, -INF and NaN if (aNodeValue.EqualsLiteral("INF")) { floatValue = FLT_MAX; } else if (aNodeValue.EqualsLiteral("-INF")) { floatValue = - FLT_MAX; } else if (!aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; } } if (aResult) *aResult = floatValue; return isValid; } /* http://www.w3.org/TR/xmlschema-2/#double */ nsresult nsSchemaValidator::ValidateBuiltinTypeDouble(const nsAString & aNodeValue, PRUint32 aTotalDigits, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, nsStringArray *aEnumerationList, PRBool *aResult) { PRBool isValid = PR_FALSE; double doubleValue; isValid = IsValidSchemaDouble(aNodeValue, &doubleValue); if (isValid && !aMaxExclusive.IsEmpty()) { // If there is a facet and value is NaN, // then it is not valid. if (aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; LOG((" Not valid: Value NaN can't be constrained by facets")); } else { double maxExclusive; if (IsValidSchemaDouble(aMaxExclusive, &maxExclusive) && (doubleValue >= maxExclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value (%f) is too big", doubleValue)); } } } if (isValid && !aMinExclusive.IsEmpty()) { // If there is a facet and value is NaN, // then it is not valid. if (aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; LOG((" Not valid: Value NaN can't be constrained by facets")); } else { double minExclusive; if (IsValidSchemaDouble(aMinExclusive, &minExclusive) && (doubleValue <= minExclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value (%f) is too small", doubleValue)); } } } if (isValid && !aMaxInclusive.IsEmpty()) { // If there is a facet and value is NaN, // then it is not valid. if (aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; LOG((" Not valid: Value NaN can't be constrained by facets")); } else { double maxInclusive; if (IsValidSchemaDouble(aMaxInclusive, &maxInclusive) && (doubleValue > maxInclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value (%f) is too big", doubleValue)); } } } if (isValid && !aMinInclusive.IsEmpty()) { // If there is a facet and value is NaN, // then it is not valid. if (aNodeValue.EqualsLiteral("NaN")) { isValid = PR_FALSE; LOG((" Not valid: Value NaN can't be constrained by facets")); } else { double minInclusive; if (IsValidSchemaDouble(aMinInclusive, &minInclusive) && (doubleValue < minInclusive)) { isValid = PR_FALSE; LOG((" Not valid: Value (%f) is too small", doubleValue)); } } } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaDouble(const nsAString & aNodeValue, double *aResult) { return nsSchemaValidatorUtils::IsValidSchemaDouble(aNodeValue, aResult); } /* http://www.w3.org/TR/xmlschema-2/#decimal */ nsresult nsSchemaValidator::ValidateBuiltinTypeDecimal(const nsAString & aNodeValue, PRUint32 aTotalDigits, PRUint32 aTotalFractionDigits, PRBool aFractionDigitsSet, const nsAString & aMaxExclusive, const nsAString & aMinExclusive, const nsAString & aMaxInclusive, const nsAString & aMinInclusive, nsStringArray *aEnumerationList, PRBool *aResult) { PRBool isValid = PR_FALSE; int strcmp; nsAutoString wholePart; nsAutoString wholePartFacet; nsAutoString fractionPart; nsAutoString fractionPartFacet; isValid = IsValidSchemaDecimal(aNodeValue, wholePart, fractionPart); // totalDigits is supposed to be positiveInteger, 1..n if (isValid && (aTotalDigits > 0)) { if (wholePart.Length() > aTotalDigits) { isValid = PR_FALSE; LOG((" Not valid: Expected a maximum of %d digits, but found %d.", aTotalDigits, wholePart.Length())); } } // fractionDigits is nonNegativeInteger, 0..n if (isValid && aFractionDigitsSet) { if (fractionPart.Length() > aTotalFractionDigits) { isValid = PR_FALSE; LOG((" Not valid: Expected a maximum of %d fraction digits, but found %d.", aTotalFractionDigits, fractionPart.Length())); } } if (isValid && !aMaxExclusive.IsEmpty() && IsValidSchemaDecimal(aMaxExclusive, wholePartFacet, fractionPartFacet)) { strcmp = nsSchemaValidatorUtils::CompareStrings(wholePart, wholePartFacet); if (strcmp > 0) { isValid = PR_FALSE; } else if (strcmp == 0) { // if equal check the fraction part strcmp = CompareFractionStrings(fractionPart, fractionPartFacet); if (strcmp >= 0) isValid = PR_FALSE; } if (!isValid) LOG((" Not valid: Value is too big")); } if (isValid && !aMinExclusive.IsEmpty() && IsValidSchemaDecimal(aMinExclusive, wholePartFacet, fractionPartFacet)) { strcmp = nsSchemaValidatorUtils::CompareStrings(wholePart, wholePartFacet); if (strcmp < 0) { isValid = PR_FALSE; } else if (strcmp == 0) { // if equal check the fraction part strcmp = CompareFractionStrings(fractionPart, fractionPartFacet); if (strcmp <= 0) isValid = PR_FALSE; } if (!isValid) LOG((" Not valid: Value is too small")); } if (isValid && !aMaxInclusive.IsEmpty() && IsValidSchemaDecimal(aMaxInclusive, wholePartFacet, fractionPartFacet)) { strcmp = nsSchemaValidatorUtils::CompareStrings(wholePart, wholePartFacet); if (strcmp > 0) { isValid = PR_FALSE; } else if (strcmp == 0) { // if equal check the fraction part strcmp = CompareFractionStrings(fractionPart, fractionPartFacet); if (strcmp > 0) isValid = PR_FALSE; } if (!isValid) LOG((" Not valid: Value is too big")); } if (isValid && !aMinInclusive.IsEmpty() && IsValidSchemaDecimal(aMinInclusive, wholePartFacet, fractionPartFacet)) { strcmp = nsSchemaValidatorUtils::CompareStrings(wholePart, wholePartFacet); if (strcmp < 0) { isValid = PR_FALSE; } else if (strcmp == 0) { // if equal check the fraction part strcmp = CompareFractionStrings(fractionPart, fractionPartFacet); if (strcmp < 0) isValid = PR_FALSE; } if (!isValid) LOG((" Not valid: Value is too small")); } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaDecimal(const nsAString & aNodeValue, nsAString & aWholePart, nsAString & aFractionPart) { PRBool isValid = PR_FALSE; int findString = aNodeValue.FindChar('.'); if (findString == kNotFound) { aWholePart.Assign(aNodeValue); } else { aWholePart = Substring(aNodeValue, 0, findString); aFractionPart = Substring(aNodeValue, findString+1, aNodeValue.Length() - findString - 1); } // to make test easier for example // nsAutoString wh1, wh2, fr1, fr2; // IsValidSchemaDecimal(NS_LITERAL_STRING("123.456"), wh1,fr1); // IsValidSchemaDecimal(NS_LITERAL_STRING("000123.4560000"), wh2,fr2); // if(wh1.Equals(wh2) && fr1.Equals(fr2)){ /* number are equal */ } nsSchemaValidatorUtils::RemoveLeadingZeros(aWholePart); nsSchemaValidatorUtils::RemoveTrailingZeros(aFractionPart); long temp; isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aWholePart, &temp); if (isValid && (findString != kNotFound)) { // XXX: assuming "2." is not valid if (aFractionPart.IsEmpty()) isValid = PR_FALSE; else if ((aFractionPart.First() == '-') || (aFractionPart.First() == '+')) isValid = PR_FALSE; else isValid = nsSchemaValidatorUtils::IsValidSchemaInteger(aFractionPart, &temp); } return isValid; } /* compares 2 strings that contain fraction parts of a decimal -1 - aString1 < aString2 0 - equal 1 - aString1 > aString2 */ int nsSchemaValidator::CompareFractionStrings(const nsAString & aString1, const nsAString & aString2) { int cmpresult = 0; // are the equal? if (aString1.Equals(aString2)) return 0; nsAutoString compareString1; nsAutoString compareString2; // put the shorter string in compareString1 if (aString1.Length() < aString2.Length()) { compareString1.Assign(aString1); compareString2.Assign(aString2); } else { compareString1.Assign(aString2); compareString2.Assign(aString1); } const PRUnichar *start1, *end1, *start2, *end2; compareString1.BeginReading(&start1, &end1); compareString2.BeginReading(&start2, &end2); PRBool done = PR_FALSE; while ((start1 != end1) && !done) { if (*++start1 != *++start2) done = PR_TRUE; } // first string has been iterated through, and all matched // we know the 2 cannot be the same due to the .Equals() check above, // so this means that string2 is equal to string1 and then has some more // numbers, meaning string2 is bigger if ((start1 == end1) && !done) { cmpresult = -1; } else if (done) { // *start1 is != *start2 if (*start1 < *start2) cmpresult = 1; else cmpresult = -1; } return cmpresult; } /* http://w3c.org/TR/xmlschema-2/#anyURI */ nsresult nsSchemaValidator::ValidateBuiltinTypeAnyURI(const nsAString & aNodeValue, PRUint32 aLength, PRUint32 aMinLength, PRUint32 aMaxLength, nsStringArray *aEnumerationList, PRBool *aResult) { PRUint32 length = aNodeValue.Length(); PRBool isValid = PR_FALSE; isValid = (IsValidSchemaAnyURI(aNodeValue)) && (!aLength || length == aLength) && (!aMinLength || length >= aMinLength) && (!aMaxLength || length <= aMaxLength); if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaAnyURI(const nsAString & aString) { PRBool isValid = PR_FALSE; if (aString.IsEmpty()) { isValid = PR_TRUE; } else { nsCOMPtr uri, absUri; // Need to supply a baseURI to allow relative URIs NS_NewURI(getter_AddRefs(absUri), NS_LITERAL_STRING("http://a")); nsresult rv = NS_NewURI(getter_AddRefs(uri), aString, (const char*)nsnull, absUri); if (rv == NS_OK) isValid = PR_TRUE; } return isValid; } // http://w3c.org/TR/xmlschema-2/#base64Binary nsresult nsSchemaValidator::ValidateBuiltinTypeBase64Binary(const nsAString & aNodeValue, PRUint32 aLength, PRBool aLengthDefined, PRUint32 aMinLength, PRBool aMinLengthDefined, PRUint32 aMaxLength, PRBool aMaxLengthDefined, nsStringArray *aEnumerationList, PRBool *aResult) { PRBool isValid = PR_FALSE; PRUint32 length; char* decodedString; isValid = IsValidSchemaBase64Binary(aNodeValue, &decodedString); if (isValid) { length = strlen(decodedString); if (aLengthDefined && (length != aLength)) { isValid = PR_FALSE; LOG((" Not valid: Not the right length (%d)", length)); } if (aMinLengthDefined && (length < aMinLength)) { isValid = PR_FALSE; LOG((" Not valid: Length (%d) is too small", length)); } if (aMaxLengthDefined && (length > aMaxLength)) { isValid = PR_FALSE; LOG((" Not valid: Length (%d) is too large", length)); } } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif nsMemory::Free(decodedString); *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaBase64Binary(const nsAString & aString, char** aDecodedString) { *aDecodedString = PL_Base64Decode(NS_ConvertUTF16toUTF8(aString).get(), aString.Length(), nsnull); return (*aDecodedString != nsnull); } // http://www.w3.org/TR/xmlschema-2/#QName nsresult nsSchemaValidator::ValidateBuiltinTypeQName(const nsAString & aNodeValue, PRUint32 aLength, PRBool aLengthDefined, PRUint32 aMinLength, PRBool aMinLengthDefined, PRUint32 aMaxLength, PRBool aMaxLengthDefined, nsStringArray *aEnumerationList, PRBool *aResult) { PRBool isValid = PR_FALSE; PRUint32 length; isValid = IsValidSchemaQName(aNodeValue); if (isValid) { length = aNodeValue.Length(); if (aLengthDefined && (length != aLength)) { isValid = PR_FALSE; LOG((" Not valid: Not the right length (%d)", length)); } if (aMinLengthDefined && (length < aMinLength)) { isValid = PR_FALSE; LOG((" Not valid: Length (%d) is too small", length)); } if (aMaxLengthDefined && (length > aMaxLength)) { isValid = PR_FALSE; LOG((" Not valid: Length (%d) is too large", length)); } } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } #ifdef PR_LOGGING LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); #endif *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaQName(const nsAString & aString) { PRBool isValid = PR_FALSE; nsresult rv; // split type (ns:type) into namespace and type. nsCOMPtr parserService = do_GetService("@mozilla.org/parser/parser-service;1", &rv); NS_ENSURE_SUCCESS(rv, PR_FALSE); const PRUnichar *colon; const nsString& qName = PromiseFlatString(aString); rv = parserService->CheckQName(qName, PR_TRUE, &colon); if (NS_SUCCEEDED(rv)) { isValid = PR_TRUE; } return isValid; } // http://www.w3.org/TR/xmlschema-2/#hexBinary nsresult nsSchemaValidator::ValidateBuiltinTypeHexBinary(const nsAString & aNodeValue, PRUint32 aLength, PRBool aLengthDefined, PRUint32 aMinLength, PRBool aMinLengthDefined, PRUint32 aMaxLength, PRBool aMaxLengthDefined, nsStringArray *aEnumerationList, PRBool *aResult) { PRBool isValid = IsValidSchemaHexBinary(aNodeValue); if (isValid) { // For hexBinary, length is measured in octets (8 bits) of binary data. So // one byte of binary data is represented by two hex digits, so we // divide by 2 to get the binary data length. PRUint32 binaryDataLength = aNodeValue.Length() / 2; if (aLengthDefined && (binaryDataLength != aLength)) { isValid = PR_FALSE; LOG((" Not valid: Not the right length (%d)", binaryDataLength)); } if (isValid && aMinLengthDefined && (binaryDataLength < aMinLength)) { isValid = PR_FALSE; LOG((" Not valid: Length (%d) is too small", binaryDataLength)); } if (isValid && aMaxLengthDefined && (binaryDataLength > aMaxLength)) { isValid = PR_FALSE; LOG((" Not valid: Length (%d) is too large", binaryDataLength)); } if (isValid && aEnumerationList && (aEnumerationList->Count() > 0)) { isValid = nsSchemaValidatorUtils::HandleEnumeration(aNodeValue, *aEnumerationList); } } LOG((isValid ? (" Value is valid!") : (" Value is not valid!"))); *aResult = isValid; return NS_OK; } PRBool nsSchemaValidator::IsValidSchemaHexBinary(const nsAString & aString) { // hex binary length has to be even PRUint32 length = aString.Length(); if (length % 2 != 0) return PR_FALSE; const PRUnichar *start, *end; aString.BeginReading(&start, &end); PRBool isValid = PR_TRUE; // each character has to be in [0-9a-fA-F] while (start != end) { PRUnichar temp = *start++; if (!isxdigit(temp)) { isValid = PR_FALSE; break; } } return isValid; } #ifdef DEBUG void nsSchemaValidator::DumpBaseType(nsISVSchemaBuiltinType *aBuiltInType) { nsAutoString typeName; aBuiltInType->GetName(typeName); PRUint16 foo; aBuiltInType->GetBuiltinType(&foo); LOG((" Base Type is %s (%d)", NS_ConvertUTF16toUTF8(typeName).get(),foo)); } #endif /**************************************************** * Complex Type Validation * ****************************************************/ // http://w3c.org/TR/xmlschema-1/#Complex_Type_Definitions nsresult nsSchemaValidator::ValidateComplextype(nsIDOMNode* aNode, nsISVSchemaComplexType *aSchemaComplexType, PRBool *aResult) { PRBool isValid = PR_FALSE; *aResult = PR_FALSE; PRUint16 contentModel; nsresult rv = aSchemaComplexType->GetContentModel(&contentModel); NS_ENSURE_SUCCESS(rv, rv); switch(contentModel) { case nsISVSchemaComplexType::CONTENT_MODEL_EMPTY: { LOG((" complex type, empty content")); rv = ValidateComplexModelEmpty(aNode, aSchemaComplexType, &isValid); break; } case nsISVSchemaComplexType::CONTENT_MODEL_SIMPLE: { LOG((" complex type, simple content")); rv = ValidateComplexModelSimple(aNode, aSchemaComplexType, &isValid); break; } case nsISVSchemaComplexType::CONTENT_MODEL_ELEMENT_ONLY: { LOG((" xsd:element only")); rv = ValidateComplexModelElement(aNode, aSchemaComplexType, &isValid); break; } case nsISVSchemaComplexType::CONTENT_MODEL_MIXED: { LOG((" complex type, mixed content")); rv = ValidateComplexModelElement(aNode, aSchemaComplexType, &isValid); break; } } if (!isValid) { *aResult = PR_FALSE; return NS_OK; } // if we are valid, validate the attributes nsCOMPtr attrMap; rv = aNode->GetAttributes(getter_AddRefs(attrMap)); NS_ENSURE_SUCCESS(rv, rv); PRUint32 attrCount = 0, ignoreAttrCount = 0, totalAttr = 0, foundAttrCount = 0; // Get total attributes on node. // We need to walk all nodes and count attribute nodes who don't get used // in schema validation - namespace declarations (xmlns=, xmlns:foo=) and // attributes nodes in the schema instance namespace attrMap->GetLength(&totalAttr); nsCOMPtr attrNode; nsCOMPtr attr; nsAutoString name; for (PRUint32 i = 0; i < totalAttr; ++i) { rv = attrMap->Item(i, getter_AddRefs(attrNode)); NS_ENSURE_SUCCESS(rv, rv); attr = do_QueryInterface(attrNode); if (attr) { // XXX: we need to make this per spec (such as Part 1 3.2.7) attr->GetName(name); if (StringBeginsWith(name, NS_LITERAL_STRING("xmlns"))) { // is the entire string xmlns or does : come right after xmlns? if (name.Length() == 5 || name.CharAt(5) == ':') ignoreAttrCount++; } else { // check if the namespace is in the schema instance namespace nsAutoString nsuri; rv = attr->GetNamespaceURI(nsuri); NS_ENSURE_SUCCESS(rv, rv); if (nsuri.EqualsLiteral(NS_SCHEMA_INSTANCE_NAMESPACE)) ignoreAttrCount++; } } } // we subtract namespace declaration attributes totalAttr -= ignoreAttrCount; LOG((" Number of attributes on node: %d (%d omitted)", totalAttr, ignoreAttrCount)); // get total defined attributes on schema aSchemaComplexType->GetAttributeCount(&attrCount); LOG((" Number of schema-defined attributes found: %d", attrCount)); // iterate through all the attribute definitions on the schema and check // if they pass PRUint32 count = 0; nsCOMPtr attrComp; while (isValid && (count < attrCount)) { rv = aSchemaComplexType->GetAttributeByIndex(count, getter_AddRefs(attrComp)); NS_ENSURE_SUCCESS(rv, rv); rv = ValidateAttributeComponent(aNode, attrComp, &foundAttrCount, &isValid); NS_ENSURE_SUCCESS(rv, rv); ++count; } // we walked all the attribute definitions. If foundAttrCount is smaller // than the attributes on the node, we have undefined attributes. if (isValid && (totalAttr > foundAttrCount)) { isValid = PR_FALSE; LOG((" -- Node contains attributes not defined in its schema!")); } *aResult = isValid; return rv; } // http://w3c.org/TR/xmlschema-1/#cElement_Declarations nsresult nsSchemaValidator::ValidateComplexModelElement(nsIDOMNode* aNode, nsISVSchemaComplexType *aSchemaComplexType, PRBool *aResult) { PRBool isValid = PR_FALSE; nsCOMPtr modelGroup; nsresult rv = aSchemaComplexType->GetModelGroup(getter_AddRefs(modelGroup)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr leftOvers, startNode; aNode->GetFirstChild(getter_AddRefs(startNode)); rv = ValidateComplexModelGroup(startNode, modelGroup, getter_AddRefs(leftOvers), &isValid); if (isValid && leftOvers) { // unexpected node isValid = PR_FALSE; #ifdef PR_LOGGING nsAutoString name; leftOvers->GetNodeName(name); LOG((" -- Expected end but found more nodes (%s)!", NS_ConvertUTF16toUTF8(name).get())); #endif } *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateComplexModelEmpty(nsIDOMNode* aNode, nsISVSchemaComplexType *aSchemaComplexType, PRBool *aResult) { PRBool isValid = PR_TRUE; nsresult rv = NS_OK; nsCOMPtr currentNode; rv = aNode->GetFirstChild(getter_AddRefs(currentNode)); NS_ENSURE_SUCCESS(rv, rv); while (isValid && currentNode) { PRUint16 nodeType; currentNode->GetNodeType(&nodeType); if (nodeType == nsIDOMNode::ELEMENT_NODE || nodeType == nsIDOMNode::TEXT_NODE) { LOG((" -- Empty content model contains element or text!")); isValid = PR_FALSE; break; } nsCOMPtr node; currentNode->GetNextSibling(getter_AddRefs(node)); currentNode.swap(node); } *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateComplexModelSimple(nsIDOMNode* aNode, nsISVSchemaComplexType *aSchemaComplexType, PRBool *aResult) { PRBool isValid = PR_FALSE; PRUint16 derivation;; nsresult rv = aSchemaComplexType->GetDerivation(&derivation); NS_ENSURE_SUCCESS(rv, rv); // two choices for complex model with simple content: derivation through // extension or through restriction switch(derivation) { case nsISVSchemaComplexType::DERIVATION_EXTENSION_SIMPLE: case nsISVSchemaComplexType::DERIVATION_RESTRICTION_SIMPLE: { #ifdef PR_LOGGING if (derivation == nsISVSchemaComplexType::DERIVATION_EXTENSION_SIMPLE) LOG((" -- deriviation by extension of a simple type")); else LOG((" -- deriviation by restriction of a simple type")); #endif nsCOMPtr simpleBaseType; rv = aSchemaComplexType->GetSimpleBaseType(getter_AddRefs(simpleBaseType)); NS_ENSURE_SUCCESS(rv, rv); nsAutoString nodeValue; nsCOMPtr domNode3 = do_QueryInterface(aNode); domNode3->GetTextContent(nodeValue); rv = ValidateSimpletype(nodeValue, simpleBaseType, &isValid); break; } default: rv = NS_ERROR_UNEXPECTED; } *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateComplexModelGroup(nsIDOMNode* aNode, nsISVSchemaModelGroup *aSchemaModelGroup, nsIDOMNode **aLeftOvers, PRBool *aResult) { nsresult rv = NS_OK; PRBool notFound = PR_FALSE, isValid = PR_FALSE; // a model group can be of type All, Sequence or Choice // http://w3c.org/TR/xmlschema-1/#Model_Groups PRUint16 compositor; rv = aSchemaModelGroup->GetCompositor(&compositor); NS_ENSURE_SUCCESS(rv, rv); PRUint32 validatedNodes = 0; PRUint32 minOccurs; aSchemaModelGroup->GetMinOccurs(&minOccurs); PRUint32 maxOccurs; aSchemaModelGroup->GetMaxOccurs(&maxOccurs); PRUint32 particleCount; aSchemaModelGroup->GetParticleCount(&particleCount); nsCOMPtr currentNode(aNode), leftOvers; switch(compositor) { case nsISVSchemaModelGroup::COMPOSITOR_ALL: { LOG((" - It is a All Compositor (%d)", particleCount)); // xsd:all has several limitations: // - order does not matter // - xsd:all can only occur a maximum of once // - it can contain only xsd:elements, who may only occur 0 or 1 times. // since an xsd:all can only happen once or never, validate it once // and return. rv = ValidateComplexAll(currentNode, aSchemaModelGroup, getter_AddRefs(leftOvers), ¬Found, &isValid); currentNode = leftOvers; // if it wasn't found but is required to happen once, it is invalid if (isValid && notFound && minOccurs == 1) { isValid = PR_FALSE; #ifdef PR_LOGGING nsCOMPtr particle; aSchemaModelGroup->GetParticle(0, getter_AddRefs(particle)); if (particle) { nsAutoString name; particle->GetName(name); LOG((" - Expected one occurance of %s, but found none!", NS_ConvertUTF16toUTF8(name).get())); } #endif } break; } case nsISVSchemaModelGroup::COMPOSITOR_SEQUENCE: { LOG((" - It is a Sequence (%d)", particleCount)); PRUint32 iterations = 0; isValid = PR_TRUE; while (currentNode && isValid && (iterations < maxOccurs) && !notFound) { rv = ValidateComplexSequence(currentNode, aSchemaModelGroup, getter_AddRefs(leftOvers), ¬Found, &isValid, &validatedNodes); if (isValid && !notFound) { iterations++; } currentNode = leftOvers; } // Special case of found nothing and expected nothing, so it's easy if (validatedNodes == 0 && iterations == 0 && minOccurs == 0) { isValid = PR_TRUE; } else if (isValid && (iterations < minOccurs) && (validatedNodes > 0)) { // if we didn't hit minOccurs and not empty sequence, invalid isValid = PR_FALSE; #ifdef PR_LOGGING nsCOMPtr particle; nsAutoString name; aSchemaModelGroup->GetParticle(0, getter_AddRefs(particle)); if (particle) { particle->GetName(name); LOG((" - Expected at least %d iterations of %s, only found %d", minOccurs, NS_ConvertUTF16toUTF8(name).get(), iterations)); } #endif } break; } case nsISVSchemaModelGroup::COMPOSITOR_CHOICE: { LOG((" - It is a Choice")); PRUint32 iterations = 0; isValid = PR_TRUE; while (currentNode && isValid && (iterations < maxOccurs)) { rv = ValidateComplexChoice(currentNode, aSchemaModelGroup, getter_AddRefs(leftOvers), ¬Found, &isValid); if (isValid) { iterations++; } currentNode = leftOvers; } // if we didn't hit minOccurs, invalid if (isValid && (iterations < minOccurs)) { isValid = PR_FALSE; #ifdef PR_LOGGING nsCOMPtr particle; nsAutoString name; aSchemaModelGroup->GetParticle(0, getter_AddRefs(particle)); if (particle) { particle->GetName(name); LOG((" - Expected at least %d iterations of %s, only found %d", minOccurs, NS_ConvertUTF16toUTF8(name).get(), iterations)); } #endif } break; } } leftOvers.swap(*aLeftOvers); *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateComplexSequence(nsIDOMNode* aStartNode, nsISVSchemaModelGroup *aSchemaModelGroup, nsIDOMNode **aLeftOvers, PRBool *aNotFound, PRBool *aResult, PRUint32 *aValidatedNodes) { if (!aStartNode || !aSchemaModelGroup) return NS_ERROR_UNEXPECTED; PRBool isValid = PR_FALSE; PRBool notFound = PR_FALSE; // get the model group details PRUint32 minOccurs; nsresult rv = aSchemaModelGroup->GetMinOccurs(&minOccurs); NS_ENSURE_SUCCESS(rv, rv); PRUint32 maxOccurs; rv = aSchemaModelGroup->GetMaxOccurs(&maxOccurs); NS_ENSURE_SUCCESS(rv, rv); PRUint32 particleCount; rv = aSchemaModelGroup->GetParticleCount(&particleCount); NS_ENSURE_SUCCESS(rv, rv); // xsd:sequence means that the order of the particles matters PRUint32 validatedNodes = 0; PRUint32 particleCounter = 0; PRUint16 nodeType; PRBool done = PR_FALSE; nsCOMPtr particle; nsCOMPtr currentNode(aStartNode), leftOvers, tmpNode; LOG(("====================== New Sequence ===========================")); // while valid and not done // we are done when we hit a node that doesn't fit our schema while (!done && currentNode && (particleCounter < particleCount)) { // get node type currentNode->GetNodeType(&nodeType); // if not an element node, skip if (nodeType != nsIDOMNode::ELEMENT_NODE) { currentNode->GetNextSibling(getter_AddRefs(tmpNode)); currentNode = tmpNode; continue; } // get the particle rv = aSchemaModelGroup->GetParticle(particleCounter, getter_AddRefs(particle)); NS_ENSURE_SUCCESS(rv, rv); rv = ValidateComplexParticle(currentNode, particle, getter_AddRefs(leftOvers), ¬Found, &isValid); NS_ENSURE_SUCCESS(rv, rv); // if we found the node, increment the amount of validated nodes. if (!notFound) validatedNodes++; if (isValid) { particleCounter++; } else { done = PR_TRUE; } // valid and not found means it was optional, so ok if (isValid && notFound) { notFound = PR_FALSE; } // not valid and not found means we finish this sequence. If any particles // are left that are required, that will be handled below. if (!isValid && notFound) { isValid = PR_TRUE; particleCounter++; } currentNode = leftOvers; } *aValidatedNodes = validatedNodes; if (validatedNodes == 0) { // we didn't walk through any nodes, thus empty sequence. The caller // will check if enough occurances (minOccurs) on the sequence happened. // We need to continue to make sure we don't have all element // declarations with each having minOccurs=0, thus empty content is allowed. isValid = PR_TRUE; notFound = PR_TRUE; } // check if any of the remaining particles are required while (isValid && (particleCounter < particleCount)) { nsCOMPtr tmpParticle; rv = aSchemaModelGroup->GetParticle(particleCounter, getter_AddRefs(tmpParticle)); NS_ENSURE_SUCCESS(rv, rv); PRUint32 tmpMinOccurs; rv = tmpParticle->GetMinOccurs(&tmpMinOccurs); NS_ENSURE_SUCCESS(rv, rv); if (tmpMinOccurs == 0) { // this particle isn't required particleCounter++; } else { isValid = PR_FALSE; #ifdef PR_LOGGING nsAutoString particleName; tmpParticle->GetName(particleName); LOG((" - Nodelist missing required element (%s)", NS_ConvertUTF16toUTF8(particleName).get())); #endif } } // make sure aLeftOvers points to null or an element node nsSchemaValidatorUtils::SetToNullOrElement(currentNode, aLeftOvers); *aNotFound = notFound; *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateComplexParticle(nsIDOMNode* aNode, nsISVSchemaParticle *aSchemaParticle, nsIDOMNode **aLeftOvers, PRBool *aNotFound, PRBool *aResult) { PRBool isValid = PR_FALSE; PRBool notFound = PR_FALSE; PRUint16 particleType; nsresult rv = aSchemaParticle->GetParticleType(&particleType); NS_ENSURE_SUCCESS(rv, rv); PRUint32 minOccurs; rv = aSchemaParticle->GetMinOccurs(&minOccurs); NS_ENSURE_SUCCESS(rv, rv); PRUint32 maxOccurs; rv = aSchemaParticle->GetMaxOccurs(&maxOccurs); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr leftOvers, tmpNode; switch(particleType) { case nsISVSchemaParticle::PARTICLE_TYPE_ELEMENT: { LOG((" -- Particle is an Element")); PRUint32 iterations = 0; PRBool done = PR_FALSE; nsAutoString nodeName, particleName; leftOvers = aNode; while (leftOvers && !done && (iterations < maxOccurs)) { // get node type PRUint16 nodeType; leftOvers->GetNodeType(&nodeType); // if not an element node, skip if (nodeType != nsIDOMNode::ELEMENT_NODE) { rv = leftOvers->GetNextSibling(getter_AddRefs(tmpNode)); NS_ENSURE_SUCCESS(rv, rv); leftOvers = tmpNode; continue; } // use localname since SchemaLoader has already resolved all namespace // references for us. leftOvers->GetLocalName(nodeName); rv = aSchemaParticle->GetName(particleName); NS_ENSURE_SUCCESS(rv, rv); if (nodeName.Equals(particleName)) { LOG(("<%s>", NS_ConvertUTF16toUTF8(nodeName).get())); rv = ValidateComplexElement(leftOvers, aSchemaParticle, &isValid); NS_ENSURE_SUCCESS(rv, rv); LOG((" (Valid = %s)", NS_ConvertUTF16toUTF8(nodeName).get(), (isValid ? "true" : "false"))); // set rest to the next element if node is valid if (isValid) { rv = leftOvers->GetNextSibling(getter_AddRefs(tmpNode)); NS_ENSURE_SUCCESS(rv, rv); leftOvers = tmpNode; } iterations++; done = !isValid; } else { done = PR_TRUE; } } // optional and not found, so ok if (!isValid && (iterations == 0) && (minOccurs == 0)) { isValid = PR_TRUE; } else if ((iterations > 0) && (iterations < minOccurs)) { // we stopped finding the element, but haven't met the minOccurs isValid = PR_FALSE; LOG((" -- Unexpected Node Found (%s), was expecting %s", NS_ConvertUTF16toUTF8(nodeName).get(), NS_ConvertUTF16toUTF8(particleName).get())); } notFound = (iterations == 0); break; } case nsISVSchemaParticle::PARTICLE_TYPE_MODEL_GROUP: { LOG((" -- Particle is an Model Group")); nsCOMPtr modelGroup = do_QueryInterface(aSchemaParticle); rv = ValidateComplexModelGroup(aNode, modelGroup, getter_AddRefs(leftOvers), &isValid); break; } case nsISVSchemaParticle::PARTICLE_TYPE_ANY: return Validate(aNode, aResult); } leftOvers.swap(*aLeftOvers); *aNotFound = notFound; *aResult = isValid; return rv; } nsresult nsSchemaValidator::GetElementXsiType(nsIDOMNode* aNode, nsISVSchemaType** aType) { nsCOMPtr domElement = do_QueryInterface(aNode); NS_ENSURE_STATE(domElement); PRBool hasTypeAttribute = PR_FALSE; nsresult rv = domElement->HasAttributeNS(NS_LITERAL_STRING( NS_SCHEMA_INSTANCE_NAMESPACE), NS_LITERAL_STRING("type"), &hasTypeAttribute); NS_ENSURE_SUCCESS(rv, rv); /* XXX: This all may need to change when element.GetSchemaTypeInfo() is implemented from DOM Level 3 Core, see: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Attr-schemaTypeInfo */ if (hasTypeAttribute) { LOG((" -- found xsi:type attribute")); nsAutoString typeAttribute; rv = domElement->GetAttributeNS(NS_LITERAL_STRING( NS_SCHEMA_INSTANCE_NAMESPACE), NS_LITERAL_STRING("type"), typeAttribute); NS_ENSURE_SUCCESS(rv, rv); LOG((" Type is: %s", NS_ConvertUTF16toUTF8(typeAttribute).get())); if (typeAttribute.IsEmpty()) return NS_ERROR_SCHEMAVALIDATOR_NO_TYPE_FOUND; // split type (ns:type) into namespace and type. nsCOMPtr parserService = do_GetService("@mozilla.org/parser/parser-service;1", &rv); NS_ENSURE_SUCCESS(rv, rv); const nsString& qName = PromiseFlatString(typeAttribute); const PRUnichar *colon; rv = parserService->CheckQName(qName, PR_TRUE, &colon); NS_ENSURE_SUCCESS(rv, rv); const PRUnichar* end = qName.EndReading(); nsAutoString schemaTypePrefix, schemaType, schemaTypeNamespace; if (!colon) { // colon not found, so no prefix schemaType.Assign(typeAttribute); // get namespace from node aNode->GetNamespaceURI(schemaTypeNamespace); } else { schemaTypePrefix.Assign(Substring(qName.get(), colon)); schemaType.Assign(Substring(colon + 1, end)); // get the namespace url from the prefix nsCOMPtr domNode3 = do_QueryInterface(aNode); rv = domNode3->LookupNamespaceURI(schemaTypePrefix, schemaTypeNamespace); NS_ENSURE_SUCCESS(rv, rv); } LOG((" Type to validate against is %s:%s", NS_LossyConvertUTF16toASCII(schemaTypePrefix).get(), NS_LossyConvertUTF16toASCII(schemaType).get())); // no schemas loaded and type is not builtin, abort if (!mSchema && !schemaTypeNamespace.EqualsLiteral(NS_SCHEMA_1999_NAMESPACE) && !schemaTypeNamespace.EqualsLiteral(NS_SCHEMA_2001_NAMESPACE)) return NS_ERROR_SCHEMAVALIDATOR_NO_SCHEMA_LOADED; // get the type rv = GetType(schemaType, schemaTypeNamespace, aType); NS_ENSURE_SUCCESS(rv, rv); } return rv; } nsresult nsSchemaValidator::ValidateComplexElement(nsIDOMNode* aNode, nsISVSchemaParticle *aSchemaParticle, PRBool *aResult) { PRBool isValid = PR_FALSE; nsCOMPtr schemaElement(do_QueryInterface(aSchemaParticle)); if (!schemaElement) return NS_ERROR_UNEXPECTED; // will hold the type to validate against nsCOMPtr type; nsresult rv = GetElementXsiType(aNode, getter_AddRefs(type)); NS_ENSURE_SUCCESS(rv, rv); if (!type) { rv = schemaElement->GetType(getter_AddRefs(type)); NS_ENSURE_SUCCESS(rv, rv); if (!type) return NS_ERROR_UNEXPECTED; } PRUint16 typeValue; rv = type->GetSchemaType(&typeValue); NS_ENSURE_SUCCESS(rv, rv); switch(typeValue) { case nsISVSchemaType::SCHEMA_TYPE_SIMPLE: { nsCOMPtr simpleType(do_QueryInterface(type)); if (simpleType) { LOG((" Element is a simple type!")); rv = ValidateAgainstType(aNode, simpleType, &isValid); } break; } case nsISVSchemaType::SCHEMA_TYPE_COMPLEX: { nsCOMPtr complexType(do_QueryInterface(type)); if (complexType) { LOG((" Element is a complex type!")); rv = ValidateAgainstType(aNode, complexType, &isValid); } break; } case nsISVSchemaType::SCHEMA_TYPE_PLACEHOLDER: { rv = NS_ERROR_NOT_IMPLEMENTED; break; } } *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateComplexChoice(nsIDOMNode* aStartNode, nsISVSchemaModelGroup *aSchemaModelGroup, nsIDOMNode **aLeftOvers, PRBool *aNotFound, PRBool *aResult) { // get the model group details PRUint32 minOccurs; nsresult rv = aSchemaModelGroup->GetMinOccurs(&minOccurs); NS_ENSURE_SUCCESS(rv, rv); PRUint32 maxOccurs; rv = aSchemaModelGroup->GetMaxOccurs(&maxOccurs); NS_ENSURE_SUCCESS(rv, rv); PRUint32 particleCount; rv = aSchemaModelGroup->GetParticleCount(&particleCount); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr nodeList; aStartNode->GetChildNodes(getter_AddRefs(nodeList)); NS_ENSURE_SUCCESS(rv, rv); if (!nodeList) return NS_ERROR_UNEXPECTED; PRUint32 childNodesLength; rv = nodeList->GetLength(&childNodesLength); NS_ENSURE_SUCCESS(rv, rv); /* xsd:choice means one of the particles must validate. */ PRBool isValid = PR_FALSE; PRBool notFound = PR_FALSE; nsCOMPtr particle; nsCOMPtr currentNode(aStartNode), leftOvers, tmpNode; nsAutoString localName, particleName; PRUint32 particleCounter = 0; LOG(("======================== New Choice ===============================")); while (!isValid && currentNode && (particleCounter < particleCount)) { // get node type PRUint16 nodeType; currentNode->GetNodeType(&nodeType); // if not an element node, skip if (nodeType != nsIDOMNode::ELEMENT_NODE) { currentNode->GetNextSibling(getter_AddRefs(tmpNode)); currentNode = tmpNode; continue; } // get the particle rv = aSchemaModelGroup->GetParticle(particleCounter, getter_AddRefs(particle)); NS_ENSURE_SUCCESS(rv, rv); particle->GetName(particleName); currentNode->GetLocalName(localName); // if the particle has no name (so not an xsd:element) or the name matches // the node's localname, then we should try to validate. if (particleName.IsEmpty() || localName.Equals(particleName)) { rv = ValidateComplexParticle(currentNode, particle, getter_AddRefs(leftOvers), ¬Found, &isValid); // if not valid and the names matched, we can bail early if (!isValid && localName.Equals(particleName)) { // The schema spec says that you can't have 2 particles with the same name, // so we know we don't have to continue looking for matching particles. LOG((" Invalid: We found a matching particle, but it did not validate")); break; } currentNode = leftOvers; } particleCounter++; } if (!isValid) { // None of the particles managed to validate. notFound = PR_TRUE; } // make sure aLeftOvers points to null or an element node nsSchemaValidatorUtils::SetToNullOrElement(currentNode, aLeftOvers); *aNotFound = notFound; *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateComplexAll(nsIDOMNode* aStartNode, nsISVSchemaModelGroup *aSchemaModelGroup, nsIDOMNode **aLeftOvers, PRBool *aNotFound, PRBool *aResult) { if (!aStartNode || !aSchemaModelGroup) return NS_ERROR_UNEXPECTED; // get the model group details PRUint32 minOccurs; nsresult rv = aSchemaModelGroup->GetMinOccurs(&minOccurs); NS_ENSURE_SUCCESS(rv, rv); PRUint32 maxOccurs; rv = aSchemaModelGroup->GetMaxOccurs(&maxOccurs); NS_ENSURE_SUCCESS(rv, rv); PRUint32 particleCount; rv = aSchemaModelGroup->GetParticleCount(&particleCount); NS_ENSURE_SUCCESS(rv, rv); // since xsd:all does not care about order, we need to record how often each // particle was hit nsDataHashtable particleHits; if (!particleHits.Init()) return NS_ERROR_OUT_OF_MEMORY; // xsd:all means that the order of the particles does not matter, and // that particles can only occur 0 or 1 time PRBool isValid = PR_FALSE; PRBool notFound = PR_FALSE; PRBool done = PR_FALSE; nsCOMPtr particle; nsCOMPtr currentNode(aStartNode), leftOvers, tmpNode; nsAutoString localName, particleName; for (PRUint32 i = 0; i < particleCount; ++i) { rv = aSchemaModelGroup->GetParticle(i, getter_AddRefs(particle)); NS_ENSURE_SUCCESS(rv, rv); particleHits.Put(particle, 0); } LOG(("====================== New All ===========================")); PRUint32 validatedNodes = 0; // we are done when we hit a node that doesn't fit our schema while (!done && currentNode) { // get node type PRUint16 nodeType; currentNode->GetNodeType(&nodeType); currentNode->GetLocalName(localName); // if not an element node, skip if (nodeType != nsIDOMNode::ELEMENT_NODE) { currentNode->GetNextSibling(getter_AddRefs(tmpNode)); currentNode = tmpNode; continue; } LOG((" - Validating element (%s)", NS_ConvertUTF16toUTF8(localName).get())); // walk all the particles until we find one that validates PRUint32 particleNum = 0; PRBool foundParticle = PR_FALSE; while (!foundParticle && particleNum < particleCount) { rv = aSchemaModelGroup->GetParticle(particleNum, getter_AddRefs(particle)); NS_ENSURE_SUCCESS(rv, rv); particle->GetName(particleName); if (particleName.Equals(localName)) { // try to validate rv = ValidateComplexParticle(currentNode, particle, getter_AddRefs(leftOvers), ¬Found, &foundParticle); NS_ENSURE_SUCCESS(rv, rv); } if (foundParticle) { validatedNodes++; PRUint32 hitCount = 0; particleHits.Get(particle, &hitCount); if (hitCount > 0) { // particles in an xsd:all can only occur a maximum of once. If we hit // a particle twice, we finish this iteration and reset the leftover // to the currentNode. We basically say this xsd:all is done, the current // node might be the start of another compositor (assuming all required // particles are hit foundParticle = PR_TRUE; leftOvers = currentNode; done = PR_TRUE; LOG((" -- Particle (%s) occured more than once, so ending", NS_ConvertUTF16toUTF8(particleName).get())); break; } else { hitCount++; particleHits.Put(particle, hitCount); LOG((" -- Element validated")); } } else { particleNum++; } } // set isvalid isValid = foundParticle; if (!isValid) { done = PR_TRUE; LOG((" -- Element could not be validated!")); } currentNode = leftOvers; } if (validatedNodes == 0) { // we didn't walk through any nodes, thus empty sequence. The caller // will check if enough occurances (minOccurs) happened. isValid = PR_TRUE; notFound = PR_TRUE; } else { // check if any of the particles didn't occur enough. We already checked // if a particle is hit more than once PRUint32 hits = 0; PRUint32 particleMinOccurs, particleMaxOccurs; for (PRUint32 i = 0; i < particleCount; ++i) { rv = aSchemaModelGroup->GetParticle(i, getter_AddRefs(particle)); NS_ENSURE_SUCCESS(rv, rv); // we assume the schema is valid and min/max is not larger that 1 particle->GetMinOccurs(&particleMinOccurs); particle->GetMaxOccurs(&particleMaxOccurs); particleHits.Get(particle, &hits); if (hits < particleMinOccurs || hits > particleMaxOccurs) { isValid = PR_FALSE; particle->GetName(particleName); LOG((" - Particle (%s) occured %d times, but should have occured [%d, %d] times", NS_ConvertUTF16toUTF8(particleName).get(), hits, particleMinOccurs, particleMaxOccurs)); break; } } } // make sure aLeftOvers points to null or an element node nsSchemaValidatorUtils::SetToNullOrElement(currentNode, aLeftOvers); *aNotFound = notFound; *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateAttributeComponent(nsIDOMNode* aNode, nsISVSchemaAttributeComponent *aAttrComp, PRUint32 *aFoundAttrCount, PRBool *aResult) { PRBool isValid = PR_FALSE; PRUint16 componentType; nsresult rv = aAttrComp->GetComponentType(&componentType); NS_ENSURE_SUCCESS(rv, rv); nsAutoString name; rv = aAttrComp->GetName(name); NS_ENSURE_SUCCESS(rv, rv); switch(componentType) { case nsISVSchemaAttributeComponent::COMPONENT_TYPE_ATTRIBUTE: { nsCOMPtr attr(do_QueryInterface(aAttrComp, &rv)); NS_ENSURE_SUCCESS(rv, rv); LOG((" Attribute Component (%s) is an attribute!", NS_ConvertUTF16toUTF8(name).get())); rv = ValidateSchemaAttribute(aNode, attr, name, aFoundAttrCount, &isValid); NS_ENSURE_SUCCESS(rv, rv); break; } case nsISVSchemaAttributeComponent::COMPONENT_TYPE_GROUP: { nsCOMPtr attrGroup(do_QueryInterface(aAttrComp, &rv)); NS_ENSURE_SUCCESS(rv, rv); LOG((" Attribute Component (%s) is an attribute group!", NS_ConvertUTF16toUTF8(name).get())); rv = ValidateSchemaAttributeGroup(aNode, attrGroup, name, aFoundAttrCount, &isValid); NS_ENSURE_SUCCESS(rv, rv); break; } case nsISVSchemaAttributeComponent::COMPONENT_TYPE_ANY: { // for now we just accept this one as being valid. // should look at the attribute namespace and validate the // attribute against it // XXX: implement this isValid = PR_TRUE; break; } } *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateSchemaAttribute(nsIDOMNode* aNode, nsISVSchemaAttribute *aAttr, const nsAString & aAttrName, PRUint32 *aFoundAttrCount, PRBool *aResult) { PRUint16 use; nsresult rv = aAttr->GetUse(&use); NS_ENSURE_SUCCESS(rv, rv); nsAutoString fixedValue, attrValue; rv = aAttr->GetFixedValue(fixedValue); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr simpleType; rv = aAttr->GetType(getter_AddRefs(simpleType)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr elm(do_QueryInterface(aNode)); // check if the attribute is to be qualified or not nsAutoString qualifiedNamespace; aAttr->GetQualifiedNamespace(qualifiedNamespace); PRBool hasAttr = PR_FALSE; PRBool isValid = PR_FALSE; if (!qualifiedNamespace.IsEmpty()) { rv = elm->HasAttributeNS(qualifiedNamespace, aAttrName, &hasAttr); NS_ENSURE_SUCCESS(rv, rv); if (hasAttr) { rv = elm->GetAttributeNS(qualifiedNamespace, aAttrName, attrValue); NS_ENSURE_SUCCESS(rv, rv); } } else { rv = elm->HasAttribute(aAttrName, &hasAttr); NS_ENSURE_SUCCESS(rv, rv); if (hasAttr) { rv = elm->GetAttribute(aAttrName, attrValue); NS_ENSURE_SUCCESS(rv, rv); } } if (!hasAttr) { // no attribute found if (use == nsISVSchemaAttribute::USE_OPTIONAL) { isValid = PR_TRUE; LOG((" -- attribute not found, but optional, so fine!")); } else if (use == nsISVSchemaAttribute::USE_REQUIRED) { // we default to invalid LOG((" -- attribute not found, but required!")); } else if (use == nsISVSchemaAttribute::USE_PROHIBITED) { // prohibited and doesn't exist is valid isValid = PR_TRUE; LOG((" -- attribute not found, but prohibited, so fine!")); } } else if (!fixedValue.IsEmpty()) { // XXX: what about default="" ? // we assume the default or fixed value is valid for now (*aFoundAttrCount)++; if (attrValue.Equals(fixedValue)) { LOG((" -- attribute has fixed value and it equals the attribute value.")); isValid = PR_TRUE; } else { LOG((" -- attribute has fixed value, but does not equal the attribute value!")); } } else { (*aFoundAttrCount)++; if (use == nsISVSchemaAttribute::USE_PROHIBITED) { // If it is is prohibited and it exists, we don't have to do anything, // since we default to invalid. LOG((" -- attribute prohibited!")); } else { if (simpleType) { // save the type on the attribute nsCOMPtr attrNode; if (NS_SUCCEEDED(elm->GetAttributeNode(aAttrName, getter_AddRefs(attrNode)))) { nsCOMPtr holder = do_CreateInstance("@mozilla.org/variant;1"); NS_ENSURE_STATE(holder); holder->SetAsInterface(nsISVSchemaType::GetIID(), simpleType); // and save on the node nsCOMPtr pAttribute(do_QueryInterface(attrNode)); if (pAttribute) { // we have to be really careful to set the destructor function // correctly. this also has to be a pointer to a variant nsCOMPtr atomServ = do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr key; rv = atomServ->GetAtomUTF8("xsdtype", getter_AddRefs(key)); NS_ENSURE_SUCCESS(rv, rv); nsIVariant *pVariant = holder; NS_IF_ADDREF(pVariant); rv = pAttribute->SetProperty(key, pVariant, &VariantDTor); NS_ENSURE_SUCCESS(rv, rv); } } rv = ValidateSimpletype(attrValue, simpleType, &isValid); } else { // If no type exists (ergo no simpleType), we should use the // simple ur-type definition defined at: // (http://www.w3.org/TR/xmlschema-1/#simple-ur-type-itself). // XXX: So for now, we default to true. isValid = PR_TRUE; } } } *aResult = isValid; return rv; } nsresult nsSchemaValidator::ValidateSchemaAttributeGroup(nsIDOMNode* aNode, nsISVSchemaAttributeGroup *aAttrGroup, const nsAString & aAttrName, PRUint32 *aFoundAttrCount, PRBool *aResult) { PRBool isValid = PR_TRUE; PRUint32 attrCount, count = 0; nsresult rv = aAttrGroup->GetAttributeCount(&attrCount); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr attrComp; while (isValid && (count < attrCount)) { rv = aAttrGroup->GetAttributeByIndex(count, getter_AddRefs(attrComp)); NS_ENSURE_SUCCESS(rv, rv); rv = ValidateAttributeComponent(aNode, attrComp, aFoundAttrCount, &isValid); NS_ENSURE_SUCCESS(rv, rv); ++count; } *aResult = isValid; return rv; }