/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "txStylesheetCompileHandlers.h" #include #include "mozilla/ArrayUtils.h" #include "mozilla/FloatingPoint.h" #include "mozilla/UniquePtrExtensions.h" #include "nsGkAtoms.h" #include "nsWhitespaceTokenizer.h" #include "txCore.h" #include "txInstructions.h" #include "txNamespaceMap.h" #include "txPatternParser.h" #include "txStringUtils.h" #include "txStylesheet.h" #include "txStylesheetCompiler.h" #include "txToplevelItems.h" #include "txURIUtils.h" #include "txXSLTFunctions.h" #include "nsStringFlags.h" #include "nsStyleUtil.h" #include "nsStringIterator.h" using namespace mozilla; txHandlerTable* gTxIgnoreHandler = 0; txHandlerTable* gTxRootHandler = 0; txHandlerTable* gTxEmbedHandler = 0; txHandlerTable* gTxTopHandler = 0; txHandlerTable* gTxTemplateHandler = 0; txHandlerTable* gTxTextHandler = 0; txHandlerTable* gTxApplyTemplatesHandler = 0; txHandlerTable* gTxCallTemplateHandler = 0; txHandlerTable* gTxVariableHandler = 0; txHandlerTable* gTxForEachHandler = 0; txHandlerTable* gTxTopVariableHandler = 0; txHandlerTable* gTxChooseHandler = 0; txHandlerTable* gTxParamHandler = 0; txHandlerTable* gTxImportHandler = 0; txHandlerTable* gTxAttributeSetHandler = 0; txHandlerTable* gTxFallbackHandler = 0; static nsresult txFnStartLRE(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState); static void txFnEndLRE(txStylesheetCompilerState& aState); #define TX_RETURN_IF_WHITESPACE(_str, _state) \ do { \ if (!_state.mElementContext->mPreserveWhitespace && \ XMLUtils::isWhitespace(_str)) { \ return NS_OK; \ } \ } while (0) static nsresult getStyleAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, int32_t aNamespace, nsAtom* aName, bool aRequired, txStylesheetAttr** aAttr) { int32_t i; for (i = 0; i < aAttrCount; ++i) { txStylesheetAttr* attr = aAttributes + i; if (attr->mNamespaceID == aNamespace && attr->mLocalName == aName) { attr->mLocalName = nullptr; *aAttr = attr; return NS_OK; } } *aAttr = nullptr; if (aRequired) { // XXX ErrorReport: missing required attribute return NS_ERROR_XSLT_PARSE_FAILURE; } return NS_OK; } static nsresult parseUseAttrSets(txStylesheetAttr* aAttributes, int32_t aAttrCount, bool aInXSLTNS, txStylesheetCompilerState& aState) { txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, aInXSLTNS ? kNameSpaceID_XSLT : kNameSpaceID_None, nsGkAtoms::useAttributeSets, false, &attr); if (!attr) { return rv; } nsWhitespaceTokenizer tok(attr->mValue); while (tok.hasMoreTokens()) { txExpandedName name; rv = name.init(tok.nextToken(), aState.mElementContext->mMappings, false); NS_ENSURE_SUCCESS(rv, rv); aState.addInstruction(MakeUnique(name)); } return NS_OK; } static nsresult parseExcludeResultPrefixes(txStylesheetAttr* aAttributes, int32_t aAttrCount, int32_t aNamespaceID) { txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, aNamespaceID, nsGkAtoms::excludeResultPrefixes, false, &attr); if (!attr) { return rv; } // XXX Needs to be implemented. return NS_OK; } static nsresult getQNameAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, nsAtom* aName, bool aRequired, txStylesheetCompilerState& aState, txExpandedName& aExpName) { aExpName.reset(); txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, aName, aRequired, &attr); if (!attr) { return rv; } rv = aExpName.init(attr->mValue, aState.mElementContext->mMappings, false); if (!aRequired && NS_FAILED(rv) && aState.fcp()) { aExpName.reset(); rv = NS_OK; } return rv; } static nsresult getExprAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, nsAtom* aName, bool aRequired, txStylesheetCompilerState& aState, UniquePtr& aExpr) { aExpr = nullptr; txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, aName, aRequired, &attr); if (!attr) { return rv; } rv = txExprParser::createExpr(attr->mValue, &aState, getter_Transfers(aExpr)); if (NS_FAILED(rv) && aState.ignoreError(rv)) { // use default value in fcp for not required exprs if (aRequired) { aExpr = MakeUnique( #ifdef TX_TO_STRING attr->mValue #endif ); } else { aExpr = nullptr; } return NS_OK; } return rv; } static nsresult getAVTAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, nsAtom* aName, bool aRequired, txStylesheetCompilerState& aState, UniquePtr& aAVT) { aAVT = nullptr; txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, aName, aRequired, &attr); if (!attr) { return rv; } rv = txExprParser::createAVT(attr->mValue, &aState, getter_Transfers(aAVT)); if (NS_FAILED(rv) && aState.fcp()) { // use default value in fcp for not required exprs if (aRequired) { aAVT = MakeUnique( #ifdef TX_TO_STRING attr->mValue #endif ); } else { aAVT = nullptr; } return NS_OK; } return rv; } static nsresult getPatternAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, nsAtom* aName, bool aRequired, txStylesheetCompilerState& aState, UniquePtr& aPattern) { aPattern = nullptr; txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, aName, aRequired, &attr); if (!attr) { return rv; } rv = txPatternParser::createPattern(attr->mValue, &aState, getter_Transfers(aPattern)); if (NS_FAILED(rv) && (aRequired || !aState.ignoreError(rv))) { // XXX ErrorReport: XSLT-Pattern parse failure return rv; } return NS_OK; } static nsresult getNumberAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, nsAtom* aName, bool aRequired, txStylesheetCompilerState& aState, double& aNumber) { aNumber = UnspecifiedNaN(); txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, aName, aRequired, &attr); if (!attr) { return rv; } aNumber = txDouble::toDouble(attr->mValue); if (std::isnan(aNumber) && (aRequired || !aState.fcp())) { // XXX ErrorReport: number parse failure return NS_ERROR_XSLT_PARSE_FAILURE; } return NS_OK; } static nsresult getAtomAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, nsAtom* aName, bool aRequired, txStylesheetCompilerState& aState, nsAtom** aAtom) { *aAtom = nullptr; txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, aName, aRequired, &attr); if (!attr) { return rv; } *aAtom = NS_Atomize(attr->mValue).take(); NS_ENSURE_TRUE(*aAtom, NS_ERROR_OUT_OF_MEMORY); return NS_OK; } static nsresult getYesNoAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, nsAtom* aName, bool aRequired, txStylesheetCompilerState& aState, txThreeState& aRes) { aRes = eNotSet; RefPtr atom; nsresult rv = getAtomAttr(aAttributes, aAttrCount, aName, aRequired, aState, getter_AddRefs(atom)); if (!atom) { return rv; } if (atom == nsGkAtoms::yes) { aRes = eTrue; } else if (atom == nsGkAtoms::no) { aRes = eFalse; } else if (aRequired || !aState.fcp()) { // XXX ErrorReport: unknown values return NS_ERROR_XSLT_PARSE_FAILURE; } return NS_OK; } static nsresult getCharAttr(txStylesheetAttr* aAttributes, int32_t aAttrCount, nsAtom* aName, bool aRequired, txStylesheetCompilerState& aState, char16_t& aChar) { // Don't reset aChar since it contains the default value txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, aName, aRequired, &attr); if (!attr) { return rv; } if (attr->mValue.Length() == 1) { aChar = attr->mValue.CharAt(0); } else if (aRequired || !aState.fcp()) { // XXX ErrorReport: not a character return NS_ERROR_XSLT_PARSE_FAILURE; } return NS_OK; } static void pushInstruction(txStylesheetCompilerState& aState, UniquePtr aInstruction) { aState.pushObject(aInstruction.release()); } template static UniquePtr popInstruction(txStylesheetCompilerState& aState) { return UniquePtr(static_cast(aState.popObject())); } /** * Ignore and error handlers */ static nsresult txFnTextIgnore(const nsAString& aStr, txStylesheetCompilerState& aState) { return NS_OK; } static nsresult txFnTextError(const nsAString& aStr, txStylesheetCompilerState& aState) { TX_RETURN_IF_WHITESPACE(aStr, aState); return NS_ERROR_XSLT_PARSE_FAILURE; } void clearAttributes(txStylesheetAttr* aAttributes, int32_t aAttrCount) { int32_t i; for (i = 0; i < aAttrCount; ++i) { aAttributes[i].mLocalName = nullptr; } } static nsresult txFnStartElementIgnore(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { if (!aState.fcp()) { clearAttributes(aAttributes, aAttrCount); } return NS_OK; } static void txFnEndElementIgnore(txStylesheetCompilerState& aState) {} static nsresult txFnStartElementSetIgnore(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { if (!aState.fcp()) { clearAttributes(aAttributes, aAttrCount); } aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndElementSetIgnore(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } static nsresult txFnStartElementError(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { return NS_ERROR_XSLT_PARSE_FAILURE; } static void txFnEndElementError(txStylesheetCompilerState& aState) { MOZ_CRASH("txFnEndElementError shouldn't be called"); } /** * Root handlers */ static nsresult txFnStartStylesheet(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { // extension-element-prefixes is handled in // txStylesheetCompiler::startElementInternal txStylesheetAttr* attr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::id, false, &attr); NS_ENSURE_SUCCESS(rv, rv); rv = parseExcludeResultPrefixes(aAttributes, aAttrCount, kNameSpaceID_None); NS_ENSURE_SUCCESS(rv, rv); rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::version, true, &attr); NS_ENSURE_SUCCESS(rv, rv); aState.pushHandlerTable(gTxImportHandler); return NS_OK; } static void txFnEndStylesheet(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } static nsresult txFnStartElementContinueTopLevel( int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.mHandlerTable = gTxTopHandler; return NS_XSLT_GET_NEW_HANDLER; } static nsresult txFnStartLREStylesheet(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { txStylesheetAttr* attr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_XSLT, nsGkAtoms::version, true, &attr); NS_ENSURE_SUCCESS(rv, rv); txExpandedName nullExpr; double prio = UnspecifiedNaN(); UniquePtr match(new txRootPattern()); UniquePtr templ( new txTemplateItem(std::move(match), nullExpr, nullExpr, prio)); aState.openInstructionContainer(templ.get()); aState.addToplevelItem(templ.release()); aState.pushHandlerTable(gTxTemplateHandler); return txFnStartLRE(aNamespaceID, aLocalName, aPrefix, aAttributes, aAttrCount, aState); } static void txFnEndLREStylesheet(txStylesheetCompilerState& aState) { txFnEndLRE(aState); aState.popHandlerTable(); aState.addInstruction(MakeUnique()); aState.closeInstructionContainer(); } static nsresult txFnStartEmbed(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { if (!aState.handleEmbeddedSheet()) { return NS_OK; } if (aNamespaceID != kNameSpaceID_XSLT || (aLocalName != nsGkAtoms::stylesheet && aLocalName != nsGkAtoms::transform)) { return NS_ERROR_XSLT_PARSE_FAILURE; } return txFnStartStylesheet(aNamespaceID, aLocalName, aPrefix, aAttributes, aAttrCount, aState); } static void txFnEndEmbed(txStylesheetCompilerState& aState) { if (!aState.handleEmbeddedSheet()) { return; } txFnEndStylesheet(aState); aState.doneEmbedding(); } /** * Top handlers */ static nsresult txFnStartOtherTop(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { if (aNamespaceID == kNameSpaceID_None || (aNamespaceID == kNameSpaceID_XSLT && !aState.fcp())) { return NS_ERROR_XSLT_PARSE_FAILURE; } aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndOtherTop(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } // xsl:attribute-set static nsresult txFnStartAttributeSet(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); UniquePtr attrSet(new txAttributeSetItem(name)); aState.openInstructionContainer(attrSet.get()); aState.addToplevelItem(attrSet.release()); rv = parseUseAttrSets(aAttributes, aAttrCount, false, aState); NS_ENSURE_SUCCESS(rv, rv); aState.pushHandlerTable(gTxAttributeSetHandler); return NS_OK; } static void txFnEndAttributeSet(txStylesheetCompilerState& aState) { aState.popHandlerTable(); aState.addInstruction(MakeUnique()); aState.closeInstructionContainer(); } // xsl:decimal-format static nsresult txFnStartDecimalFormat(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, false, aState, name); NS_ENSURE_SUCCESS(rv, rv); UniquePtr format(new txDecimalFormat); rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::decimalSeparator, false, aState, format->mDecimalSeparator); NS_ENSURE_SUCCESS(rv, rv); rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::groupingSeparator, false, aState, format->mGroupingSeparator); NS_ENSURE_SUCCESS(rv, rv); txStylesheetAttr* attr = nullptr; rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::infinity, false, &attr); NS_ENSURE_SUCCESS(rv, rv); if (attr) { format->mInfinity = attr->mValue; } rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::minusSign, false, aState, format->mMinusSign); NS_ENSURE_SUCCESS(rv, rv); rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::NaN, false, &attr); NS_ENSURE_SUCCESS(rv, rv); if (attr) { format->mNaN = attr->mValue; } rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::percent, false, aState, format->mPercent); NS_ENSURE_SUCCESS(rv, rv); rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::perMille, false, aState, format->mPerMille); NS_ENSURE_SUCCESS(rv, rv); rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::zeroDigit, false, aState, format->mZeroDigit); NS_ENSURE_SUCCESS(rv, rv); rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::digit, false, aState, format->mDigit); NS_ENSURE_SUCCESS(rv, rv); rv = getCharAttr(aAttributes, aAttrCount, nsGkAtoms::patternSeparator, false, aState, format->mPatternSeparator); NS_ENSURE_SUCCESS(rv, rv); rv = aState.mStylesheet->addDecimalFormat(name, std::move(format)); NS_ENSURE_SUCCESS(rv, rv); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndDecimalFormat(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } // xsl:import static nsresult txFnStartImport(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { UniquePtr import(new txImportItem); import->mFrame = MakeUnique(); txStylesheet::ImportFrame* frame = import->mFrame.get(); aState.addToplevelItem(import.release()); txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::href, true, &attr); NS_ENSURE_SUCCESS(rv, rv); nsAutoString absUri; URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI, absUri); rv = aState.loadImportedStylesheet(absUri, frame); NS_ENSURE_SUCCESS(rv, rv); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndImport(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } // xsl:include static nsresult txFnStartInclude(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::href, true, &attr); NS_ENSURE_SUCCESS(rv, rv); nsAutoString absUri; URIUtils::resolveHref(attr->mValue, aState.mElementContext->mBaseURI, absUri); rv = aState.loadIncludedStylesheet(absUri); NS_ENSURE_SUCCESS(rv, rv); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndInclude(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } // xsl:key static nsresult txFnStartKey(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); aState.mDisAllowed = txIParseContext::KEY_FUNCTION; UniquePtr match; rv = getPatternAttr(aAttributes, aAttrCount, nsGkAtoms::match, true, aState, match); NS_ENSURE_SUCCESS(rv, rv); UniquePtr use; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::use, true, aState, use); NS_ENSURE_SUCCESS(rv, rv); aState.mDisAllowed = 0; rv = aState.mStylesheet->addKey(name, std::move(match), std::move(use)); NS_ENSURE_SUCCESS(rv, rv); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndKey(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } // xsl:namespace-alias static nsresult txFnStartNamespaceAlias(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::stylesheetPrefix, true, &attr); NS_ENSURE_SUCCESS(rv, rv); rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::resultPrefix, true, &attr); NS_ENSURE_SUCCESS(rv, rv); // XXX Needs to be implemented. aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndNamespaceAlias(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } // xsl:output static nsresult txFnStartOutput(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; UniquePtr item(new txOutputItem); txExpandedName methodExpName; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::method, false, aState, methodExpName); NS_ENSURE_SUCCESS(rv, rv); if (!methodExpName.isNull()) { if (methodExpName.mNamespaceID != kNameSpaceID_None) { // The spec doesn't say what to do here so we'll just ignore the // value. We could possibly warn. } else if (methodExpName.mLocalName == nsGkAtoms::html) { item->mFormat.mMethod = eHTMLOutput; } else if (methodExpName.mLocalName == nsGkAtoms::text) { item->mFormat.mMethod = eTextOutput; } else if (methodExpName.mLocalName == nsGkAtoms::xml) { item->mFormat.mMethod = eXMLOutput; } else { return NS_ERROR_XSLT_PARSE_FAILURE; } } txStylesheetAttr* attr = nullptr; getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::version, false, &attr); if (attr) { item->mFormat.mVersion = attr->mValue; } getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::encoding, false, &attr); if (attr) { item->mFormat.mEncoding = attr->mValue; } rv = getYesNoAttr(aAttributes, aAttrCount, nsGkAtoms::omitXmlDeclaration, false, aState, item->mFormat.mOmitXMLDeclaration); NS_ENSURE_SUCCESS(rv, rv); rv = getYesNoAttr(aAttributes, aAttrCount, nsGkAtoms::standalone, false, aState, item->mFormat.mStandalone); NS_ENSURE_SUCCESS(rv, rv); getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::doctypePublic, false, &attr); if (attr) { item->mFormat.mPublicId = attr->mValue; } getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::doctypeSystem, false, &attr); if (attr) { item->mFormat.mSystemId = attr->mValue; } getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::cdataSectionElements, false, &attr); if (attr) { nsWhitespaceTokenizer tokens(attr->mValue); while (tokens.hasMoreTokens()) { UniquePtr qname(new txExpandedName()); rv = qname->init(tokens.nextToken(), aState.mElementContext->mMappings, false); NS_ENSURE_SUCCESS(rv, rv); item->mFormat.mCDATASectionElements.add(qname.release()); } } rv = getYesNoAttr(aAttributes, aAttrCount, nsGkAtoms::indent, false, aState, item->mFormat.mIndent); NS_ENSURE_SUCCESS(rv, rv); getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::mediaType, false, &attr); if (attr) { item->mFormat.mMediaType = NS_ConvertUTF16toUTF8(attr->mValue); } aState.addToplevelItem(item.release()); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndOutput(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } // xsl:strip-space/xsl:preserve-space static nsresult txFnStartStripSpace(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { txStylesheetAttr* attr = nullptr; nsresult rv = getStyleAttr(aAttributes, aAttrCount, kNameSpaceID_None, nsGkAtoms::elements, true, &attr); NS_ENSURE_SUCCESS(rv, rv); bool strip = aLocalName == nsGkAtoms::stripSpace; UniquePtr stripItem(new txStripSpaceItem); nsWhitespaceTokenizer tokenizer(attr->mValue); while (tokenizer.hasMoreTokens()) { const nsAString& name = tokenizer.nextToken(); int32_t ns = kNameSpaceID_None; RefPtr prefix, localName; rv = XMLUtils::splitQName(name, getter_AddRefs(prefix), getter_AddRefs(localName)); if (NS_FAILED(rv)) { // check for "*" or "prefix:*" uint32_t length = name.Length(); const char16_t* c; name.BeginReading(c); if (length == 2 || c[length - 1] != '*') { // these can't work return NS_ERROR_XSLT_PARSE_FAILURE; } if (length > 1) { // Check for a valid prefix, that is, the returned prefix // should be empty and the real prefix is returned in // localName. if (c[length - 2] != ':') { return NS_ERROR_XSLT_PARSE_FAILURE; } rv = XMLUtils::splitQName(StringHead(name, length - 2), getter_AddRefs(prefix), getter_AddRefs(localName)); if (NS_FAILED(rv) || prefix) { // bad chars or two ':' return NS_ERROR_XSLT_PARSE_FAILURE; } prefix = localName; } localName = nsGkAtoms::_asterisk; } if (prefix) { ns = aState.mElementContext->mMappings->lookupNamespace(prefix); NS_ENSURE_TRUE(ns != kNameSpaceID_Unknown, NS_ERROR_FAILURE); } stripItem->addStripSpaceTest( new txStripSpaceTest(prefix, localName, ns, strip)); } aState.addToplevelItem(stripItem.release()); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndStripSpace(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } // xsl:template static nsresult txFnStartTemplate(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, false, aState, name); NS_ENSURE_SUCCESS(rv, rv); txExpandedName mode; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::mode, false, aState, mode); NS_ENSURE_SUCCESS(rv, rv); double prio = UnspecifiedNaN(); rv = getNumberAttr(aAttributes, aAttrCount, nsGkAtoms::priority, false, aState, prio); NS_ENSURE_SUCCESS(rv, rv); UniquePtr match; rv = getPatternAttr(aAttributes, aAttrCount, nsGkAtoms::match, name.isNull(), aState, match); NS_ENSURE_SUCCESS(rv, rv); UniquePtr templ( new txTemplateItem(std::move(match), name, mode, prio)); aState.openInstructionContainer(templ.get()); aState.addToplevelItem(templ.release()); aState.pushHandlerTable(gTxParamHandler); return NS_OK; } static void txFnEndTemplate(txStylesheetCompilerState& aState) { aState.popHandlerTable(); aState.addInstruction(MakeUnique()); aState.closeInstructionContainer(); } // xsl:variable, xsl:param static nsresult txFnStartTopVariable(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false, aState, select); NS_ENSURE_SUCCESS(rv, rv); UniquePtr var(new txVariableItem( name, std::move(select), aLocalName == nsGkAtoms::param)); aState.openInstructionContainer(var.get()); aState.pushPtr(var.get(), aState.eVariableItem); if (var->mValue) { // XXX should be gTxErrorHandler? aState.pushHandlerTable(gTxIgnoreHandler); } else { aState.pushHandlerTable(gTxTopVariableHandler); } aState.addToplevelItem(var.release()); return NS_OK; } static void txFnEndTopVariable(txStylesheetCompilerState& aState) { txHandlerTable* prev = aState.mHandlerTable; aState.popHandlerTable(); txVariableItem* var = static_cast(aState.popPtr(aState.eVariableItem)); if (prev == gTxTopVariableHandler) { // No children were found. NS_ASSERTION(!var->mValue, "There shouldn't be a select-expression here"); var->mValue = MakeUnique(u""_ns); } else if (!var->mValue) { // If we don't have a select-expression there mush be children. aState.addInstruction(MakeUnique()); } aState.closeInstructionContainer(); } static nsresult txFnStartElementStartTopVar(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.mHandlerTable = gTxTemplateHandler; return NS_XSLT_GET_NEW_HANDLER; } static nsresult txFnTextStartTopVar(const nsAString& aStr, txStylesheetCompilerState& aState) { TX_RETURN_IF_WHITESPACE(aStr, aState); aState.mHandlerTable = gTxTemplateHandler; return NS_XSLT_GET_NEW_HANDLER; } /** * Template Handlers */ /* LRE txStartLREElement txInsertAttrSet one for each qname in xsl:use-attribute-sets txLREAttribute one for each attribute [children] txEndElement */ static nsresult txFnStartLRE(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; aState.addInstruction( MakeUnique(aNamespaceID, aLocalName, aPrefix)); rv = parseExcludeResultPrefixes(aAttributes, aAttrCount, kNameSpaceID_XSLT); NS_ENSURE_SUCCESS(rv, rv); rv = parseUseAttrSets(aAttributes, aAttrCount, true, aState); NS_ENSURE_SUCCESS(rv, rv); txStylesheetAttr* attr = nullptr; int32_t i; for (i = 0; i < aAttrCount; ++i) { attr = aAttributes + i; if (attr->mNamespaceID == kNameSpaceID_XSLT) { if (attr->mLocalName == nsGkAtoms::version) { attr->mLocalName = nullptr; } continue; } UniquePtr avt; rv = txExprParser::createAVT(attr->mValue, &aState, getter_Transfers(avt)); NS_ENSURE_SUCCESS(rv, rv); aState.addInstruction(MakeUnique( attr->mNamespaceID, attr->mLocalName, attr->mPrefix, std::move(avt))); } return NS_OK; } static void txFnEndLRE(txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique()); } /* "LRE text" txText */ static nsresult txFnText(const nsAString& aStr, txStylesheetCompilerState& aState) { TX_RETURN_IF_WHITESPACE(aStr, aState); aState.addInstruction(MakeUnique(aStr, false)); return NS_OK; } /* xsl:apply-imports txApplyImportsStart txApplyImportsEnd */ static nsresult txFnStartApplyImports(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique()); aState.addInstruction(MakeUnique()); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndApplyImports(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } /* xsl:apply-templates txPushParams [params] txPushNewContext -+ (holds s) txApplyTemplate <-+ | txLoopNodeSet -+ | txPopParams <-+ */ static nsresult txFnStartApplyTemplates(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; aState.addInstruction(MakeUnique()); txExpandedName mode; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::mode, false, aState, mode); NS_ENSURE_SUCCESS(rv, rv); pushInstruction(aState, MakeUnique(mode)); UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false, aState, select); NS_ENSURE_SUCCESS(rv, rv); if (!select) { UniquePtr nt(new txNodeTypeTest(txNodeTypeTest::NODE_TYPE)); select = MakeUnique(nt.release(), LocationStep::CHILD_AXIS); } UniquePtr pushcontext( new txPushNewContext(std::move(select))); aState.pushSorter(pushcontext.get()); pushInstruction(aState, std::move(pushcontext)); aState.pushHandlerTable(gTxApplyTemplatesHandler); return NS_OK; } static void txFnEndApplyTemplates(txStylesheetCompilerState& aState) { aState.popHandlerTable(); txPushNewContext* pushcontext = aState.addInstruction(popInstruction(aState)); aState.popSorter(); // txApplyTemplates txInstruction* instr = aState.addInstruction(popInstruction(aState)); aState.addInstruction(MakeUnique(instr)); pushcontext->mBailTarget = aState.addInstruction(MakeUnique()); } /* xsl:attribute txPushStringHandler [children] txAttribute */ static nsresult txFnStartAttribute(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; aState.addInstruction(MakeUnique(true)); UniquePtr name; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); UniquePtr nspace; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::_namespace, false, aState, nspace); NS_ENSURE_SUCCESS(rv, rv); pushInstruction(aState, MakeUnique(std::move(name), std::move(nspace), aState.mElementContext->mMappings)); // We need to push the template-handler since the current might be // the attributeset-handler aState.pushHandlerTable(gTxTemplateHandler); return NS_OK; } static void txFnEndAttribute(txStylesheetCompilerState& aState) { aState.popHandlerTable(); aState.addInstruction(popInstruction(aState)); } /* xsl:call-template txPushParams [params] txCallTemplate txPopParams */ static nsresult txFnStartCallTemplate(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; aState.addInstruction(MakeUnique()); txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); pushInstruction(aState, MakeUnique(name)); aState.pushHandlerTable(gTxCallTemplateHandler); return NS_OK; } static void txFnEndCallTemplate(txStylesheetCompilerState& aState) { aState.popHandlerTable(); // txCallTemplate aState.addInstruction(popInstruction(aState)); aState.addInstruction(MakeUnique()); } /* xsl:choose txCondotionalGoto --+ \ [children] | | one for each xsl:when txGoTo --+ | / | | txCondotionalGoto | <-+ --+ [children] | | txGoTo --+ | | | [children] | <-+ for the xsl:otherwise, if there is one <-+ */ static nsresult txFnStartChoose(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.pushChooseGotoList(); aState.pushHandlerTable(gTxChooseHandler); return NS_OK; } static void txFnEndChoose(txStylesheetCompilerState& aState) { aState.popHandlerTable(); txListIterator iter(aState.mChooseGotoList.get()); txGoTo* gotoinstr; while ((gotoinstr = static_cast(iter.next()))) { aState.addGotoTarget(&gotoinstr->mTarget); } aState.popChooseGotoList(); } /* xsl:comment txPushStringHandler [children] txComment */ static nsresult txFnStartComment(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique(true)); return NS_OK; } static void txFnEndComment(txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique()); } /* xsl:copy txCopy -+ txInsertAttrSet | one for each qname in use-attribute-sets [children] | txEndElement | <-+ */ static nsresult txFnStartCopy(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.pushPtr(aState.addInstruction(MakeUnique()), aState.eCopy); return parseUseAttrSets(aAttributes, aAttrCount, false, aState); } static void txFnEndCopy(txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique()); txCopy* copy = static_cast(aState.popPtr(aState.eCopy)); aState.addGotoTarget(©->mBailTarget); } /* xsl:copy-of txCopyOf */ static nsresult txFnStartCopyOf(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, true, aState, select); NS_ENSURE_SUCCESS(rv, rv); aState.addInstruction(MakeUnique(std::move(select))); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndCopyOf(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } /* xsl:element txStartElement txInsertAttrSet one for each qname in use-attribute-sets [children] txEndElement */ static nsresult txFnStartElement(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; UniquePtr name; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); UniquePtr nspace; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::_namespace, false, aState, nspace); NS_ENSURE_SUCCESS(rv, rv); aState.addInstruction(MakeUnique( std::move(name), std::move(nspace), aState.mElementContext->mMappings)); rv = parseUseAttrSets(aAttributes, aAttrCount, false, aState); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } static void txFnEndElement(txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique()); } /* xsl:fallback [children] */ static nsresult txFnStartFallback(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.mSearchingForFallback = false; aState.pushHandlerTable(gTxTemplateHandler); return NS_OK; } static void txFnEndFallback(txStylesheetCompilerState& aState) { aState.popHandlerTable(); NS_ASSERTION(!aState.mSearchingForFallback, "bad nesting of unknown-instruction and fallback handlers"); } /* xsl:for-each txPushNewContext -+ (holds s) txPushNullTemplateRule <-+ | [children] | | txLoopNodeSet -+ | <-+ */ static nsresult txFnStartForEach(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, true, aState, select); NS_ENSURE_SUCCESS(rv, rv); txPushNewContext* pushcontext = aState.addInstruction(MakeUnique(std::move(select))); aState.pushPtr(pushcontext, aState.ePushNewContext); aState.pushSorter(pushcontext); aState.pushPtr(aState.addInstruction(MakeUnique()), aState.ePushNullTemplateRule); aState.pushHandlerTable(gTxForEachHandler); return NS_OK; } static void txFnEndForEach(txStylesheetCompilerState& aState) { aState.popHandlerTable(); // This is a txPushNullTemplateRule txInstruction* pnullrule = static_cast(aState.popPtr(aState.ePushNullTemplateRule)); aState.addInstruction(MakeUnique(pnullrule)); aState.popSorter(); txPushNewContext* pushcontext = static_cast(aState.popPtr(aState.ePushNewContext)); aState.addGotoTarget(&pushcontext->mBailTarget); } static nsresult txFnStartElementContinueTemplate( int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.mHandlerTable = gTxTemplateHandler; return NS_XSLT_GET_NEW_HANDLER; } static nsresult txFnTextContinueTemplate(const nsAString& aStr, txStylesheetCompilerState& aState) { TX_RETURN_IF_WHITESPACE(aStr, aState); aState.mHandlerTable = gTxTemplateHandler; return NS_XSLT_GET_NEW_HANDLER; } /* xsl:if txConditionalGoto -+ [children] | <-+ */ static nsresult txFnStartIf(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; UniquePtr test; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::test, true, aState, test); NS_ENSURE_SUCCESS(rv, rv); aState.pushPtr(aState.addInstruction( MakeUnique(std::move(test), nullptr)), aState.eConditionalGoto); return NS_OK; } static void txFnEndIf(txStylesheetCompilerState& aState) { txConditionalGoto* condGoto = static_cast(aState.popPtr(aState.eConditionalGoto)); aState.addGotoTarget(&condGoto->mTarget); } /* xsl:message txPushStringHandler [children] txMessage */ static nsresult txFnStartMessage(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique(false)); txThreeState term; nsresult rv = getYesNoAttr(aAttributes, aAttrCount, nsGkAtoms::terminate, false, aState, term); NS_ENSURE_SUCCESS(rv, rv); pushInstruction(aState, MakeUnique(term == eTrue)); return NS_OK; } static void txFnEndMessage(txStylesheetCompilerState& aState) { aState.addInstruction(popInstruction(aState)); } /* xsl:number txNumber */ static nsresult txFnStartNumber(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; RefPtr levelAtom; rv = getAtomAttr(aAttributes, aAttrCount, nsGkAtoms::level, false, aState, getter_AddRefs(levelAtom)); NS_ENSURE_SUCCESS(rv, rv); txXSLTNumber::LevelType level = txXSLTNumber::eLevelSingle; if (levelAtom == nsGkAtoms::multiple) { level = txXSLTNumber::eLevelMultiple; } else if (levelAtom == nsGkAtoms::any) { level = txXSLTNumber::eLevelAny; } else if (levelAtom && levelAtom != nsGkAtoms::single && !aState.fcp()) { return NS_ERROR_XSLT_PARSE_FAILURE; } UniquePtr count; rv = getPatternAttr(aAttributes, aAttrCount, nsGkAtoms::count, false, aState, count); NS_ENSURE_SUCCESS(rv, rv); UniquePtr from; rv = getPatternAttr(aAttributes, aAttrCount, nsGkAtoms::from, false, aState, from); NS_ENSURE_SUCCESS(rv, rv); UniquePtr value; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::value, false, aState, value); NS_ENSURE_SUCCESS(rv, rv); UniquePtr format; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::format, false, aState, format); NS_ENSURE_SUCCESS(rv, rv); UniquePtr lang; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::lang, false, aState, lang); NS_ENSURE_SUCCESS(rv, rv); UniquePtr letterValue; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::letterValue, false, aState, letterValue); NS_ENSURE_SUCCESS(rv, rv); UniquePtr groupingSeparator; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::groupingSeparator, false, aState, groupingSeparator); NS_ENSURE_SUCCESS(rv, rv); UniquePtr groupingSize; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::groupingSize, false, aState, groupingSize); NS_ENSURE_SUCCESS(rv, rv); aState.addInstruction(MakeUnique( level, std::move(count), std::move(from), std::move(value), std::move(format), std::move(groupingSeparator), std::move(groupingSize))); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndNumber(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } /* xsl:otherwise (see xsl:choose) */ static nsresult txFnStartOtherwise(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.pushHandlerTable(gTxTemplateHandler); return NS_OK; } static void txFnEndOtherwise(txStylesheetCompilerState& aState) { aState.popHandlerTable(); aState.mHandlerTable = gTxIgnoreHandler; // XXX should be gTxErrorHandler } /* xsl:param txCheckParam --+ txPushRTFHandler | --- (for RTF-parameters) [children] | / txSetVariable | <-+ */ static nsresult txFnStartParam(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); aState.pushPtr(aState.addInstruction(MakeUnique(name)), aState.eCheckParam); UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false, aState, select); NS_ENSURE_SUCCESS(rv, rv); UniquePtr var = MakeUnique(name, std::move(select)); if (var->mValue) { // XXX should be gTxErrorHandler? aState.pushHandlerTable(gTxIgnoreHandler); } else { aState.pushHandlerTable(gTxVariableHandler); } pushInstruction(aState, std::move(var)); return NS_OK; } static void txFnEndParam(txStylesheetCompilerState& aState) { UniquePtr var = popInstruction(aState); txHandlerTable* prev = aState.mHandlerTable; aState.popHandlerTable(); if (prev == gTxVariableHandler) { // No children were found. NS_ASSERTION(!var->mValue, "There shouldn't be a select-expression here"); var->mValue = MakeUnique(u""_ns); } aState.addVariable(var->mName); aState.addInstruction(std::move(var)); txCheckParam* checkParam = static_cast(aState.popPtr(aState.eCheckParam)); aState.addGotoTarget(&checkParam->mBailTarget); } /* xsl:processing-instruction txPushStringHandler [children] txProcessingInstruction */ static nsresult txFnStartPI(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique(true)); UniquePtr name; nsresult rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); pushInstruction(aState, MakeUnique(std::move(name))); return NS_OK; } static void txFnEndPI(txStylesheetCompilerState& aState) { aState.addInstruction(popInstruction(aState)); } /* xsl:sort (no instructions) */ static nsresult txFnStartSort(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false, aState, select); NS_ENSURE_SUCCESS(rv, rv); if (!select) { UniquePtr nt(new txNodeTypeTest(txNodeTypeTest::NODE_TYPE)); select = MakeUnique(nt.release(), LocationStep::SELF_AXIS); } UniquePtr lang; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::lang, false, aState, lang); NS_ENSURE_SUCCESS(rv, rv); UniquePtr dataType; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::dataType, false, aState, dataType); NS_ENSURE_SUCCESS(rv, rv); UniquePtr order; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::order, false, aState, order); NS_ENSURE_SUCCESS(rv, rv); UniquePtr caseOrder; rv = getAVTAttr(aAttributes, aAttrCount, nsGkAtoms::caseOrder, false, aState, caseOrder); NS_ENSURE_SUCCESS(rv, rv); aState.mSorter->addSort(std::move(select), std::move(lang), std::move(dataType), std::move(order), std::move(caseOrder)); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndSort(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } /* xsl:text [children] (only txText) */ static nsresult txFnStartText(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { NS_ASSERTION(!aState.mDOE, "nested d-o-e elements should not happen"); nsresult rv = NS_OK; txThreeState doe; rv = getYesNoAttr(aAttributes, aAttrCount, nsGkAtoms::disableOutputEscaping, false, aState, doe); NS_ENSURE_SUCCESS(rv, rv); aState.mDOE = doe == eTrue; aState.pushHandlerTable(gTxTextHandler); return NS_OK; } static void txFnEndText(txStylesheetCompilerState& aState) { aState.mDOE = false; aState.popHandlerTable(); } static nsresult txFnTextText(const nsAString& aStr, txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique(aStr, aState.mDOE)); return NS_OK; } /* xsl:value-of txValueOf */ static nsresult txFnStartValueOf(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txThreeState doe; rv = getYesNoAttr(aAttributes, aAttrCount, nsGkAtoms::disableOutputEscaping, false, aState, doe); NS_ENSURE_SUCCESS(rv, rv); UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, true, aState, select); NS_ENSURE_SUCCESS(rv, rv); aState.addInstruction(MakeUnique(std::move(select), doe == eTrue)); aState.pushHandlerTable(gTxIgnoreHandler); return NS_OK; } static void txFnEndValueOf(txStylesheetCompilerState& aState) { aState.popHandlerTable(); } /* xsl:variable txPushRTFHandler --- (for RTF-parameters) [children] / txSetVariable */ static nsresult txFnStartVariable(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false, aState, select); NS_ENSURE_SUCCESS(rv, rv); UniquePtr var = MakeUnique(name, std::move(select)); if (var->mValue) { // XXX should be gTxErrorHandler? aState.pushHandlerTable(gTxIgnoreHandler); } else { aState.pushHandlerTable(gTxVariableHandler); } pushInstruction(aState, std::move(var)); return NS_OK; } static void txFnEndVariable(txStylesheetCompilerState& aState) { UniquePtr var = popInstruction(aState); txHandlerTable* prev = aState.mHandlerTable; aState.popHandlerTable(); if (prev == gTxVariableHandler) { // No children were found. NS_ASSERTION(!var->mValue, "There shouldn't be a select-expression here"); var->mValue = MakeUnique(u""_ns); } aState.addVariable(var->mName); aState.addInstruction(std::move(var)); } static nsresult txFnStartElementStartRTF(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { aState.addInstruction(MakeUnique()); aState.mHandlerTable = gTxTemplateHandler; return NS_XSLT_GET_NEW_HANDLER; } static nsresult txFnTextStartRTF(const nsAString& aStr, txStylesheetCompilerState& aState) { TX_RETURN_IF_WHITESPACE(aStr, aState); aState.addInstruction(MakeUnique()); aState.mHandlerTable = gTxTemplateHandler; return NS_XSLT_GET_NEW_HANDLER; } /* xsl:when (see xsl:choose) */ static nsresult txFnStartWhen(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; UniquePtr test; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::test, true, aState, test); NS_ENSURE_SUCCESS(rv, rv); aState.pushPtr(aState.addInstruction( MakeUnique(std::move(test), nullptr)), aState.eConditionalGoto); aState.pushHandlerTable(gTxTemplateHandler); return NS_OK; } static void txFnEndWhen(txStylesheetCompilerState& aState) { aState.popHandlerTable(); aState.mChooseGotoList->add( aState.addInstruction(MakeUnique(nullptr))); txConditionalGoto* condGoto = static_cast(aState.popPtr(aState.eConditionalGoto)); aState.addGotoTarget(&condGoto->mTarget); } /* xsl:with-param txPushRTFHandler -- for RTF-parameters [children] / txSetParam */ static nsresult txFnStartWithParam(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { nsresult rv = NS_OK; txExpandedName name; rv = getQNameAttr(aAttributes, aAttrCount, nsGkAtoms::name, true, aState, name); NS_ENSURE_SUCCESS(rv, rv); UniquePtr select; rv = getExprAttr(aAttributes, aAttrCount, nsGkAtoms::select, false, aState, select); NS_ENSURE_SUCCESS(rv, rv); UniquePtr var = MakeUnique(name, std::move(select)); if (var->mValue) { // XXX should be gTxErrorHandler? aState.pushHandlerTable(gTxIgnoreHandler); } else { aState.pushHandlerTable(gTxVariableHandler); } pushInstruction(aState, std::move(var)); return NS_OK; } static void txFnEndWithParam(txStylesheetCompilerState& aState) { UniquePtr var = popInstruction(aState); txHandlerTable* prev = aState.mHandlerTable; aState.popHandlerTable(); if (prev == gTxVariableHandler) { // No children were found. NS_ASSERTION(!var->mValue, "There shouldn't be a select-expression here"); var->mValue = MakeUnique(u""_ns); } aState.addInstruction(std::move(var)); } /* Unknown instruction [fallbacks] if one or more xsl:fallbacks are found or txErrorInstruction otherwise */ static nsresult txFnStartUnknownInstruction(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount, txStylesheetCompilerState& aState) { NS_ASSERTION(!aState.mSearchingForFallback, "bad nesting of unknown-instruction and fallback handlers"); if (aNamespaceID == kNameSpaceID_XSLT && !aState.fcp()) { return NS_ERROR_XSLT_PARSE_FAILURE; } aState.mSearchingForFallback = true; aState.pushHandlerTable(gTxFallbackHandler); return NS_OK; } static void txFnEndUnknownInstruction(txStylesheetCompilerState& aState) { aState.popHandlerTable(); if (aState.mSearchingForFallback) { aState.addInstruction(MakeUnique()); } aState.mSearchingForFallback = false; } /** * Table Datas */ struct txHandlerTableData { txElementHandler mOtherHandler; txElementHandler mLREHandler; HandleTextFn mTextHandler; }; const txHandlerTableData gTxIgnoreTableData = { // Other {0, 0, txFnStartElementIgnore, txFnEndElementIgnore}, // LRE {0, 0, txFnStartElementIgnore, txFnEndElementIgnore}, // Text txFnTextIgnore}; const txElementHandler gTxRootElementHandlers[] = { {kNameSpaceID_XSLT, "stylesheet", txFnStartStylesheet, txFnEndStylesheet}, {kNameSpaceID_XSLT, "transform", txFnStartStylesheet, txFnEndStylesheet}}; const txHandlerTableData gTxRootTableData = { // Other {0, 0, txFnStartElementError, txFnEndElementError}, // LRE {0, 0, txFnStartLREStylesheet, txFnEndLREStylesheet}, // Text txFnTextError}; const txHandlerTableData gTxEmbedTableData = { // Other {0, 0, txFnStartEmbed, txFnEndEmbed}, // LRE {0, 0, txFnStartEmbed, txFnEndEmbed}, // Text txFnTextIgnore}; const txElementHandler gTxTopElementHandlers[] = { {kNameSpaceID_XSLT, "attribute-set", txFnStartAttributeSet, txFnEndAttributeSet}, {kNameSpaceID_XSLT, "decimal-format", txFnStartDecimalFormat, txFnEndDecimalFormat}, {kNameSpaceID_XSLT, "include", txFnStartInclude, txFnEndInclude}, {kNameSpaceID_XSLT, "key", txFnStartKey, txFnEndKey}, {kNameSpaceID_XSLT, "namespace-alias", txFnStartNamespaceAlias, txFnEndNamespaceAlias}, {kNameSpaceID_XSLT, "output", txFnStartOutput, txFnEndOutput}, {kNameSpaceID_XSLT, "param", txFnStartTopVariable, txFnEndTopVariable}, {kNameSpaceID_XSLT, "preserve-space", txFnStartStripSpace, txFnEndStripSpace}, {kNameSpaceID_XSLT, "strip-space", txFnStartStripSpace, txFnEndStripSpace}, {kNameSpaceID_XSLT, "template", txFnStartTemplate, txFnEndTemplate}, {kNameSpaceID_XSLT, "variable", txFnStartTopVariable, txFnEndTopVariable}}; const txHandlerTableData gTxTopTableData = { // Other {0, 0, txFnStartOtherTop, txFnEndOtherTop}, // LRE {0, 0, txFnStartOtherTop, txFnEndOtherTop}, // Text txFnTextIgnore}; const txElementHandler gTxTemplateElementHandlers[] = { {kNameSpaceID_XSLT, "apply-imports", txFnStartApplyImports, txFnEndApplyImports}, {kNameSpaceID_XSLT, "apply-templates", txFnStartApplyTemplates, txFnEndApplyTemplates}, {kNameSpaceID_XSLT, "attribute", txFnStartAttribute, txFnEndAttribute}, {kNameSpaceID_XSLT, "call-template", txFnStartCallTemplate, txFnEndCallTemplate}, {kNameSpaceID_XSLT, "choose", txFnStartChoose, txFnEndChoose}, {kNameSpaceID_XSLT, "comment", txFnStartComment, txFnEndComment}, {kNameSpaceID_XSLT, "copy", txFnStartCopy, txFnEndCopy}, {kNameSpaceID_XSLT, "copy-of", txFnStartCopyOf, txFnEndCopyOf}, {kNameSpaceID_XSLT, "element", txFnStartElement, txFnEndElement}, {kNameSpaceID_XSLT, "fallback", txFnStartElementSetIgnore, txFnEndElementSetIgnore}, {kNameSpaceID_XSLT, "for-each", txFnStartForEach, txFnEndForEach}, {kNameSpaceID_XSLT, "if", txFnStartIf, txFnEndIf}, {kNameSpaceID_XSLT, "message", txFnStartMessage, txFnEndMessage}, {kNameSpaceID_XSLT, "number", txFnStartNumber, txFnEndNumber}, {kNameSpaceID_XSLT, "processing-instruction", txFnStartPI, txFnEndPI}, {kNameSpaceID_XSLT, "text", txFnStartText, txFnEndText}, {kNameSpaceID_XSLT, "value-of", txFnStartValueOf, txFnEndValueOf}, {kNameSpaceID_XSLT, "variable", txFnStartVariable, txFnEndVariable}}; const txHandlerTableData gTxTemplateTableData = { // Other {0, 0, txFnStartUnknownInstruction, txFnEndUnknownInstruction}, // LRE {0, 0, txFnStartLRE, txFnEndLRE}, // Text txFnText}; const txHandlerTableData gTxTextTableData = { // Other {0, 0, txFnStartElementError, txFnEndElementError}, // LRE {0, 0, txFnStartElementError, txFnEndElementError}, // Text txFnTextText}; const txElementHandler gTxApplyTemplatesElementHandlers[] = { {kNameSpaceID_XSLT, "sort", txFnStartSort, txFnEndSort}, {kNameSpaceID_XSLT, "with-param", txFnStartWithParam, txFnEndWithParam}}; const txHandlerTableData gTxApplyTemplatesTableData = { // Other {0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore}, // should this be error? // LRE {0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore}, // Text txFnTextIgnore}; const txElementHandler gTxCallTemplateElementHandlers[] = { {kNameSpaceID_XSLT, "with-param", txFnStartWithParam, txFnEndWithParam}}; const txHandlerTableData gTxCallTemplateTableData = { // Other {0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore}, // should this be error? // LRE {0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore}, // Text txFnTextIgnore}; const txHandlerTableData gTxVariableTableData = { // Other {0, 0, txFnStartElementStartRTF, 0}, // LRE {0, 0, txFnStartElementStartRTF, 0}, // Text txFnTextStartRTF}; const txElementHandler gTxForEachElementHandlers[] = { {kNameSpaceID_XSLT, "sort", txFnStartSort, txFnEndSort}}; const txHandlerTableData gTxForEachTableData = { // Other {0, 0, txFnStartElementContinueTemplate, 0}, // LRE {0, 0, txFnStartElementContinueTemplate, 0}, // Text txFnTextContinueTemplate}; const txHandlerTableData gTxTopVariableTableData = { // Other {0, 0, txFnStartElementStartTopVar, 0}, // LRE {0, 0, txFnStartElementStartTopVar, 0}, // Text txFnTextStartTopVar}; const txElementHandler gTxChooseElementHandlers[] = { {kNameSpaceID_XSLT, "otherwise", txFnStartOtherwise, txFnEndOtherwise}, {kNameSpaceID_XSLT, "when", txFnStartWhen, txFnEndWhen}}; const txHandlerTableData gTxChooseTableData = { // Other {0, 0, txFnStartElementError, 0}, // LRE {0, 0, txFnStartElementError, 0}, // Text txFnTextError}; const txElementHandler gTxParamElementHandlers[] = { {kNameSpaceID_XSLT, "param", txFnStartParam, txFnEndParam}}; const txHandlerTableData gTxParamTableData = { // Other {0, 0, txFnStartElementContinueTemplate, 0}, // LRE {0, 0, txFnStartElementContinueTemplate, 0}, // Text txFnTextContinueTemplate}; const txElementHandler gTxImportElementHandlers[] = { {kNameSpaceID_XSLT, "import", txFnStartImport, txFnEndImport}}; const txHandlerTableData gTxImportTableData = { // Other {0, 0, txFnStartElementContinueTopLevel, 0}, // LRE {0, 0, txFnStartOtherTop, txFnEndOtherTop}, // XXX what should we do here? // Text txFnTextIgnore // XXX what should we do here? }; const txElementHandler gTxAttributeSetElementHandlers[] = { {kNameSpaceID_XSLT, "attribute", txFnStartAttribute, txFnEndAttribute}}; const txHandlerTableData gTxAttributeSetTableData = { // Other {0, 0, txFnStartElementError, 0}, // LRE {0, 0, txFnStartElementError, 0}, // Text txFnTextError}; const txElementHandler gTxFallbackElementHandlers[] = { {kNameSpaceID_XSLT, "fallback", txFnStartFallback, txFnEndFallback}}; const txHandlerTableData gTxFallbackTableData = { // Other {0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore}, // LRE {0, 0, txFnStartElementSetIgnore, txFnEndElementSetIgnore}, // Text txFnTextIgnore}; /** * txHandlerTable */ txHandlerTable::txHandlerTable(const HandleTextFn aTextHandler, const txElementHandler* aLREHandler, const txElementHandler* aOtherHandler) : mTextHandler(aTextHandler), mLREHandler(aLREHandler), mOtherHandler(aOtherHandler) {} nsresult txHandlerTable::init(const txElementHandler* aHandlers, uint32_t aCount) { nsresult rv = NS_OK; uint32_t i; for (i = 0; i < aCount; ++i) { RefPtr nameAtom = NS_Atomize(aHandlers->mLocalName); txExpandedName name(aHandlers->mNamespaceID, nameAtom); rv = mHandlers.add(name, aHandlers); NS_ENSURE_SUCCESS(rv, rv); ++aHandlers; } return NS_OK; } const txElementHandler* txHandlerTable::find(int32_t aNamespaceID, nsAtom* aLocalName) { txExpandedName name(aNamespaceID, aLocalName); const txElementHandler* handler = mHandlers.get(name); if (!handler) { handler = mOtherHandler; } return handler; } #define INIT_HANDLER(_name) \ gTx##_name##Handler = new txHandlerTable( \ gTx##_name##TableData.mTextHandler, &gTx##_name##TableData.mLREHandler, \ &gTx##_name##TableData.mOtherHandler); \ if (!gTx##_name##Handler) return false #define INIT_HANDLER_WITH_ELEMENT_HANDLERS(_name) \ INIT_HANDLER(_name); \ \ rv = gTx##_name##Handler->init(gTx##_name##ElementHandlers, \ ArrayLength(gTx##_name##ElementHandlers)); \ if (NS_FAILED(rv)) return false #define SHUTDOWN_HANDLER(_name) \ delete gTx##_name##Handler; \ gTx##_name##Handler = nullptr // static bool txHandlerTable::init() { nsresult rv = NS_OK; INIT_HANDLER_WITH_ELEMENT_HANDLERS(Root); INIT_HANDLER(Embed); INIT_HANDLER_WITH_ELEMENT_HANDLERS(Top); INIT_HANDLER(Ignore); INIT_HANDLER_WITH_ELEMENT_HANDLERS(Template); INIT_HANDLER(Text); INIT_HANDLER_WITH_ELEMENT_HANDLERS(ApplyTemplates); INIT_HANDLER_WITH_ELEMENT_HANDLERS(CallTemplate); INIT_HANDLER(Variable); INIT_HANDLER_WITH_ELEMENT_HANDLERS(ForEach); INIT_HANDLER(TopVariable); INIT_HANDLER_WITH_ELEMENT_HANDLERS(Choose); INIT_HANDLER_WITH_ELEMENT_HANDLERS(Param); INIT_HANDLER_WITH_ELEMENT_HANDLERS(Import); INIT_HANDLER_WITH_ELEMENT_HANDLERS(AttributeSet); INIT_HANDLER_WITH_ELEMENT_HANDLERS(Fallback); return true; } // static void txHandlerTable::shutdown() { SHUTDOWN_HANDLER(Root); SHUTDOWN_HANDLER(Embed); SHUTDOWN_HANDLER(Top); SHUTDOWN_HANDLER(Ignore); SHUTDOWN_HANDLER(Template); SHUTDOWN_HANDLER(Text); SHUTDOWN_HANDLER(ApplyTemplates); SHUTDOWN_HANDLER(CallTemplate); SHUTDOWN_HANDLER(Variable); SHUTDOWN_HANDLER(ForEach); SHUTDOWN_HANDLER(TopVariable); SHUTDOWN_HANDLER(Choose); SHUTDOWN_HANDLER(Param); SHUTDOWN_HANDLER(Import); SHUTDOWN_HANDLER(AttributeSet); SHUTDOWN_HANDLER(Fallback); }