/* -*- 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 "txStylesheetCompiler.h" #include #include "mozilla/ArrayUtils.h" #include "mozilla/UniquePtr.h" #include "mozilla/UniquePtrExtensions.h" #include "nsGkAtoms.h" #include "nsServiceManagerUtils.h" #include "nsTArray.h" #include "nsWhitespaceTokenizer.h" #include "txExprParser.h" #include "txInstructions.h" #include "txLog.h" #include "txPatternParser.h" #include "txStringUtils.h" #include "txStylesheet.h" #include "txStylesheetCompileHandlers.h" #include "txToplevelItems.h" #include "txURIUtils.h" #include "txXSLTFunctions.h" using namespace mozilla; using mozilla::dom::ReferrerPolicy; txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI, ReferrerPolicy aReferrerPolicy, txACompileObserver* aObserver) : txStylesheetCompilerState(aObserver) { mStatus = init(aStylesheetURI, aReferrerPolicy, nullptr, nullptr); } txStylesheetCompiler::txStylesheetCompiler(const nsAString& aStylesheetURI, txStylesheet* aStylesheet, txListIterator* aInsertPosition, ReferrerPolicy aReferrerPolicy, txACompileObserver* aObserver) : txStylesheetCompilerState(aObserver) { mStatus = init(aStylesheetURI, aReferrerPolicy, aStylesheet, aInsertPosition); } void txStylesheetCompiler::setBaseURI(const nsString& aBaseURI) { NS_ASSERTION(mObjectStack.size() == 1 && !mObjectStack.peek(), "Execution already started"); if (NS_FAILED(mStatus)) { return; } mElementContext->mBaseURI = aBaseURI; } nsresult txStylesheetCompiler::startElement(int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount) { if (NS_FAILED(mStatus)) { // ignore content after failure // XXX reevaluate once expat stops on failure return NS_OK; } nsresult rv = flushCharacters(); NS_ENSURE_SUCCESS(rv, rv); // look for new namespace mappings bool hasOwnNamespaceMap = false; int32_t i; for (i = 0; i < aAttrCount; ++i) { txStylesheetAttr* attr = aAttributes + i; if (attr->mNamespaceID == kNameSpaceID_XMLNS) { rv = ensureNewElementContext(); NS_ENSURE_SUCCESS(rv, rv); if (!hasOwnNamespaceMap) { mElementContext->mMappings = new txNamespaceMap(*mElementContext->mMappings); hasOwnNamespaceMap = true; } if (attr->mLocalName == nsGkAtoms::xmlns) { mElementContext->mMappings->mapNamespace(nullptr, attr->mValue); } else { mElementContext->mMappings->mapNamespace(attr->mLocalName, attr->mValue); } } } return startElementInternal(aNamespaceID, aLocalName, aPrefix, aAttributes, aAttrCount); } nsresult txStylesheetCompiler::startElement(const char16_t* aName, const char16_t** aAttrs, int32_t aAttrCount) { if (NS_FAILED(mStatus)) { // ignore content after failure // XXX reevaluate once expat stops on failure return NS_OK; } nsresult rv = flushCharacters(); NS_ENSURE_SUCCESS(rv, rv); UniquePtr atts; if (aAttrCount > 0) { atts = MakeUnique(aAttrCount); } bool hasOwnNamespaceMap = false; int32_t i; for (i = 0; i < aAttrCount; ++i) { rv = XMLUtils::splitExpatName( aAttrs[i * 2], getter_AddRefs(atts[i].mPrefix), getter_AddRefs(atts[i].mLocalName), &atts[i].mNamespaceID); NS_ENSURE_SUCCESS(rv, rv); atts[i].mValue.Append(aAttrs[i * 2 + 1]); RefPtr prefixToBind; if (atts[i].mPrefix == nsGkAtoms::xmlns) { prefixToBind = atts[i].mLocalName; } else if (atts[i].mNamespaceID == kNameSpaceID_XMLNS) { prefixToBind = nsGkAtoms::_empty; } if (prefixToBind) { rv = ensureNewElementContext(); NS_ENSURE_SUCCESS(rv, rv); if (!hasOwnNamespaceMap) { mElementContext->mMappings = new txNamespaceMap(*mElementContext->mMappings); hasOwnNamespaceMap = true; } rv = mElementContext->mMappings->mapNamespace(prefixToBind, atts[i].mValue); NS_ENSURE_SUCCESS(rv, rv); } } RefPtr prefix, localname; int32_t namespaceID; rv = XMLUtils::splitExpatName(aName, getter_AddRefs(prefix), getter_AddRefs(localname), &namespaceID); NS_ENSURE_SUCCESS(rv, rv); return startElementInternal(namespaceID, localname, prefix, atts.get(), aAttrCount); } nsresult txStylesheetCompiler::startElementInternal( int32_t aNamespaceID, nsAtom* aLocalName, nsAtom* aPrefix, txStylesheetAttr* aAttributes, int32_t aAttrCount) { nsresult rv = NS_OK; int32_t i; for (i = mInScopeVariables.Length() - 1; i >= 0; --i) { ++mInScopeVariables[i].mLevel; } // Update the elementcontext if we have special attributes for (i = 0; i < aAttrCount; ++i) { txStylesheetAttr* attr = aAttributes + i; // id if (mEmbedStatus == eNeedEmbed && attr->mLocalName == nsGkAtoms::id && attr->mNamespaceID == kNameSpaceID_None && attr->mValue.Equals(mTarget)) { // We found the right ID, signal to compile the // embedded stylesheet. mEmbedStatus = eInEmbed; } // xml:space if (attr->mNamespaceID == kNameSpaceID_XML && attr->mLocalName == nsGkAtoms::space) { rv = ensureNewElementContext(); NS_ENSURE_SUCCESS(rv, rv); if (TX_StringEqualsAtom(attr->mValue, nsGkAtoms::preserve)) { mElementContext->mPreserveWhitespace = true; } else if (TX_StringEqualsAtom(attr->mValue, nsGkAtoms::_default)) { mElementContext->mPreserveWhitespace = false; } else { return NS_ERROR_XSLT_PARSE_FAILURE; } } // extension-element-prefixes if ((attr->mNamespaceID == kNameSpaceID_XSLT && attr->mLocalName == nsGkAtoms::extensionElementPrefixes && aNamespaceID != kNameSpaceID_XSLT) || (attr->mNamespaceID == kNameSpaceID_None && attr->mLocalName == nsGkAtoms::extensionElementPrefixes && aNamespaceID == kNameSpaceID_XSLT && (aLocalName == nsGkAtoms::stylesheet || aLocalName == nsGkAtoms::transform))) { rv = ensureNewElementContext(); NS_ENSURE_SUCCESS(rv, rv); nsWhitespaceTokenizer tok(attr->mValue); while (tok.hasMoreTokens()) { int32_t namespaceID = mElementContext->mMappings->lookupNamespaceWithDefault( tok.nextToken()); if (namespaceID == kNameSpaceID_Unknown) return NS_ERROR_XSLT_PARSE_FAILURE; mElementContext->mInstructionNamespaces.AppendElement(namespaceID); } attr->mLocalName = nullptr; } // version if ((attr->mNamespaceID == kNameSpaceID_XSLT && attr->mLocalName == nsGkAtoms::version && aNamespaceID != kNameSpaceID_XSLT) || (attr->mNamespaceID == kNameSpaceID_None && attr->mLocalName == nsGkAtoms::version && aNamespaceID == kNameSpaceID_XSLT && (aLocalName == nsGkAtoms::stylesheet || aLocalName == nsGkAtoms::transform))) { rv = ensureNewElementContext(); NS_ENSURE_SUCCESS(rv, rv); if (attr->mValue.EqualsLiteral("1.0")) { mElementContext->mForwardsCompatibleParsing = false; } else { mElementContext->mForwardsCompatibleParsing = true; } } } // Find the right elementhandler and execute it bool isInstruction = false; int32_t count = mElementContext->mInstructionNamespaces.Length(); for (i = 0; i < count; ++i) { if (mElementContext->mInstructionNamespaces[i] == aNamespaceID) { isInstruction = true; break; } } const txElementHandler* handler; do { handler = isInstruction ? mHandlerTable->find(aNamespaceID, aLocalName) : mHandlerTable->mLREHandler; rv = (handler->mStartFunction)(aNamespaceID, aLocalName, aPrefix, aAttributes, aAttrCount, *this); } while (rv == NS_XSLT_GET_NEW_HANDLER); NS_ENSURE_SUCCESS(rv, rv); if (!fcp()) { for (i = 0; i < aAttrCount; ++i) { txStylesheetAttr& attr = aAttributes[i]; if (attr.mLocalName && (attr.mNamespaceID == kNameSpaceID_XSLT || (aNamespaceID == kNameSpaceID_XSLT && attr.mNamespaceID == kNameSpaceID_None))) { // XXX ErrorReport: unknown attribute return NS_ERROR_XSLT_PARSE_FAILURE; } } } pushPtr(const_cast(handler), eElementHandler); mElementContext->mDepth++; return NS_OK; } nsresult txStylesheetCompiler::endElement() { if (NS_FAILED(mStatus)) { // ignore content after failure // XXX reevaluate once expat stops on failure return NS_OK; } nsresult rv = flushCharacters(); NS_ENSURE_SUCCESS(rv, rv); int32_t i; for (i = mInScopeVariables.Length() - 1; i >= 0; --i) { txInScopeVariable& var = mInScopeVariables[i]; if (!--(var.mLevel)) { addInstruction(MakeUnique(var.mName)); mInScopeVariables.RemoveElementAt(i); } } const txElementHandler* handler = const_cast( static_cast(popPtr(eElementHandler))); (handler->mEndFunction)(*this); if (!--mElementContext->mDepth) { // this will delete the old object mElementContext = WrapUnique(static_cast(popObject())); } return NS_OK; } nsresult txStylesheetCompiler::characters(const nsAString& aStr) { if (NS_FAILED(mStatus)) { // ignore content after failure // XXX reevaluate once expat stops on failure return NS_OK; } mCharacters.Append(aStr); return NS_OK; } nsresult txStylesheetCompiler::doneLoading() { MOZ_LOG(txLog::xslt, LogLevel::Info, ("Compiler::doneLoading: %s\n", NS_LossyConvertUTF16toASCII(mStylesheetURI).get())); if (NS_FAILED(mStatus)) { return mStatus; } mDoneWithThisStylesheet = true; return maybeDoneCompiling(); } void txStylesheetCompiler::cancel(nsresult aError, const char16_t* aErrorText, const char16_t* aParam) { MOZ_LOG(txLog::xslt, LogLevel::Info, ("Compiler::cancel: %s, module: %d, code %d\n", NS_LossyConvertUTF16toASCII(mStylesheetURI).get(), NS_ERROR_GET_MODULE(aError), NS_ERROR_GET_CODE(aError))); if (NS_SUCCEEDED(mStatus)) { mStatus = aError; } if (mObserver) { mObserver->onDoneCompiling(this, mStatus, aErrorText, aParam); // This will ensure that we don't call onDoneCompiling twice. Also // ensures that we don't keep the observer alive longer then necessary. mObserver = nullptr; } } txStylesheet* txStylesheetCompiler::getStylesheet() { return mStylesheet; } nsresult txStylesheetCompiler::loadURI(const nsAString& aUri, const nsAString& aReferrerUri, ReferrerPolicy aReferrerPolicy, txStylesheetCompiler* aCompiler) { MOZ_LOG(txLog::xslt, LogLevel::Info, ("Compiler::loadURI forwards %s thru %s\n", NS_LossyConvertUTF16toASCII(aUri).get(), NS_LossyConvertUTF16toASCII(mStylesheetURI).get())); if (mStylesheetURI.Equals(aUri)) { return NS_ERROR_XSLT_LOAD_RECURSION; } return mObserver ? mObserver->loadURI(aUri, aReferrerUri, aReferrerPolicy, aCompiler) : NS_ERROR_FAILURE; } void txStylesheetCompiler::onDoneCompiling(txStylesheetCompiler* aCompiler, nsresult aResult, const char16_t* aErrorText, const char16_t* aParam) { if (NS_FAILED(aResult)) { cancel(aResult, aErrorText, aParam); return; } mChildCompilerList.RemoveElement(aCompiler); maybeDoneCompiling(); } nsresult txStylesheetCompiler::flushCharacters() { // Bail if we don't have any characters. The handler will detect // ignoreable whitespace if (mCharacters.IsEmpty()) { return NS_OK; } nsresult rv = NS_OK; do { rv = (mHandlerTable->mTextHandler)(mCharacters, *this); } while (rv == NS_XSLT_GET_NEW_HANDLER); NS_ENSURE_SUCCESS(rv, rv); mCharacters.Truncate(); return NS_OK; } nsresult txStylesheetCompiler::ensureNewElementContext() { // Do we already have a new context? if (!mElementContext->mDepth) { return NS_OK; } UniquePtr context(new txElementContext(*mElementContext)); pushObject(mElementContext.release()); mElementContext = std::move(context); return NS_OK; } nsresult txStylesheetCompiler::maybeDoneCompiling() { if (!mDoneWithThisStylesheet || !mChildCompilerList.IsEmpty()) { return NS_OK; } if (mIsTopCompiler) { nsresult rv = mStylesheet->doneCompiling(); if (NS_FAILED(rv)) { cancel(rv); return rv; } } if (mObserver) { mObserver->onDoneCompiling(this, mStatus); // This will ensure that we don't call onDoneCompiling twice. Also // ensures that we don't keep the observer alive longer then necessary. mObserver = nullptr; } return NS_OK; } /** * txStylesheetCompilerState */ txStylesheetCompilerState::txStylesheetCompilerState( txACompileObserver* aObserver) : mHandlerTable(nullptr), mSorter(nullptr), mDOE(false), mSearchingForFallback(false), mDisAllowed(0), mObserver(aObserver), mEmbedStatus(eNoEmbed), mIsTopCompiler(false), mDoneWithThisStylesheet(false), mNextInstrPtr(nullptr), mToplevelIterator(nullptr), mReferrerPolicy(ReferrerPolicy::_empty) { // Embedded stylesheets have another handler, which is set in // txStylesheetCompiler::init if the baseURI has a fragment identifier. mHandlerTable = gTxRootHandler; } nsresult txStylesheetCompilerState::init(const nsAString& aStylesheetURI, ReferrerPolicy aReferrerPolicy, txStylesheet* aStylesheet, txListIterator* aInsertPosition) { NS_ASSERTION(!aStylesheet || aInsertPosition, "must provide insertposition if loading subsheet"); mStylesheetURI = aStylesheetURI; mReferrerPolicy = aReferrerPolicy; // Check for fragment identifier of an embedded stylesheet. int32_t fragment = aStylesheetURI.FindChar('#') + 1; if (fragment > 0) { int32_t fragmentLength = aStylesheetURI.Length() - fragment; if (fragmentLength > 0) { // This is really an embedded stylesheet, not just a // "url#". We may want to unescape the fragment. mTarget = Substring(aStylesheetURI, (uint32_t)fragment, fragmentLength); mEmbedStatus = eNeedEmbed; mHandlerTable = gTxEmbedHandler; } } nsresult rv = NS_OK; if (aStylesheet) { mStylesheet = aStylesheet; mToplevelIterator = *aInsertPosition; mIsTopCompiler = false; } else { mStylesheet = new txStylesheet; rv = mStylesheet->init(); NS_ENSURE_SUCCESS(rv, rv); mToplevelIterator = txListIterator(&mStylesheet->mRootFrame->mToplevelItems); mToplevelIterator.next(); // go to the end of the list mIsTopCompiler = true; } mElementContext = MakeUnique(aStylesheetURI); // Push the "old" txElementContext pushObject(nullptr); return NS_OK; } txStylesheetCompilerState::~txStylesheetCompilerState() { while (!mObjectStack.isEmpty()) { delete popObject(); } } void txStylesheetCompilerState::pushHandlerTable(txHandlerTable* aTable) { pushPtr(mHandlerTable, eHandlerTable); mHandlerTable = aTable; } void txStylesheetCompilerState::popHandlerTable() { mHandlerTable = static_cast(popPtr(eHandlerTable)); } void txStylesheetCompilerState::pushSorter(txPushNewContext* aSorter) { pushPtr(mSorter, ePushNewContext); mSorter = aSorter; } void txStylesheetCompilerState::popSorter() { mSorter = static_cast(popPtr(ePushNewContext)); } void txStylesheetCompilerState::pushChooseGotoList() { pushObject(mChooseGotoList.release()); mChooseGotoList = MakeUnique(); } void txStylesheetCompilerState::popChooseGotoList() { // this will delete the old value mChooseGotoList = WrapUnique(static_cast(popObject())); } void txStylesheetCompilerState::pushObject(txObject* aObject) { mObjectStack.push(aObject); } txObject* txStylesheetCompilerState::popObject() { return static_cast(mObjectStack.pop()); } void txStylesheetCompilerState::pushPtr(void* aPtr, enumStackType aType) { #ifdef TX_DEBUG_STACK MOZ_LOG(txLog::xslt, LogLevel::Debug, ("pushPtr: 0x%x type %u\n", aPtr, aType)); #endif mTypeStack.AppendElement(aType); mOtherStack.push(aPtr); } void* txStylesheetCompilerState::popPtr(enumStackType aType) { if (mTypeStack.IsEmpty()) { MOZ_CRASH("Attempt to pop when type stack is empty"); } enumStackType type = mTypeStack.PopLastElement(); void* value = mOtherStack.pop(); #ifdef TX_DEBUG_STACK MOZ_LOG(txLog::xslt, LogLevel::Debug, ("popPtr: 0x%x type %u requested %u\n", value, type, aType)); #endif if (type != aType) { MOZ_CRASH("Expected type does not match top element type"); } return value; } void txStylesheetCompilerState::addToplevelItem(txToplevelItem* aItem) { mToplevelIterator.addBefore(aItem); } nsresult txStylesheetCompilerState::openInstructionContainer( txInstructionContainer* aContainer) { MOZ_ASSERT(!mNextInstrPtr, "can't nest instruction-containers"); mNextInstrPtr = &aContainer->mFirstInstruction; return NS_OK; } void txStylesheetCompilerState::closeInstructionContainer() { NS_ASSERTION(mGotoTargetPointers.IsEmpty(), "GotoTargets still exists, did you forget to add txReturn?"); mNextInstrPtr = 0; } txInstruction* txStylesheetCompilerState::addInstruction( UniquePtr&& aInstruction) { MOZ_ASSERT(mNextInstrPtr, "adding instruction outside container"); txInstruction* newInstr = aInstruction.get(); *mNextInstrPtr = std::move(aInstruction); mNextInstrPtr = &newInstr->mNext; uint32_t i, count = mGotoTargetPointers.Length(); for (i = 0; i < count; ++i) { *mGotoTargetPointers[i] = newInstr; } mGotoTargetPointers.Clear(); return newInstr; } nsresult txStylesheetCompilerState::loadIncludedStylesheet( const nsAString& aURI) { MOZ_LOG(txLog::xslt, LogLevel::Info, ("CompilerState::loadIncludedStylesheet: %s\n", NS_LossyConvertUTF16toASCII(aURI).get())); if (mStylesheetURI.Equals(aURI)) { return NS_ERROR_XSLT_LOAD_RECURSION; } NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED); UniquePtr item(new txDummyItem); mToplevelIterator.addBefore(item.release()); // step back to the dummy-item mToplevelIterator.previous(); txACompileObserver* observer = static_cast(this); RefPtr compiler = new txStylesheetCompiler( aURI, mStylesheet, &mToplevelIterator, mReferrerPolicy, observer); // step forward before calling the observer in case of syncronous loading mToplevelIterator.next(); mChildCompilerList.AppendElement(compiler); nsresult rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy, compiler); if (NS_FAILED(rv)) { mChildCompilerList.RemoveElement(compiler); } return rv; } nsresult txStylesheetCompilerState::loadImportedStylesheet( const nsAString& aURI, txStylesheet::ImportFrame* aFrame) { MOZ_LOG(txLog::xslt, LogLevel::Info, ("CompilerState::loadImportedStylesheet: %s\n", NS_LossyConvertUTF16toASCII(aURI).get())); if (mStylesheetURI.Equals(aURI)) { return NS_ERROR_XSLT_LOAD_RECURSION; } NS_ENSURE_TRUE(mObserver, NS_ERROR_NOT_IMPLEMENTED); txListIterator iter(&aFrame->mToplevelItems); iter.next(); // go to the end of the list txACompileObserver* observer = static_cast(this); RefPtr compiler = new txStylesheetCompiler( aURI, mStylesheet, &iter, mReferrerPolicy, observer); mChildCompilerList.AppendElement(compiler); nsresult rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy, compiler); if (NS_FAILED(rv)) { mChildCompilerList.RemoveElement(compiler); } return rv; } void txStylesheetCompilerState::addGotoTarget(txInstruction** aTargetPointer) { mGotoTargetPointers.AppendElement(aTargetPointer); } void txStylesheetCompilerState::addVariable(const txExpandedName& aName) { mInScopeVariables.AppendElement(aName); } nsresult txStylesheetCompilerState::resolveNamespacePrefix(nsAtom* aPrefix, int32_t& aID) { NS_ASSERTION(aPrefix && aPrefix != nsGkAtoms::_empty, "caller should handle default namespace ''"); aID = mElementContext->mMappings->lookupNamespace(aPrefix); return (aID != kNameSpaceID_Unknown) ? NS_OK : NS_ERROR_FAILURE; } /** * Error Function to be used for unknown extension functions. * */ class txErrorFunctionCall : public FunctionCall { public: explicit txErrorFunctionCall(nsAtom* aName) : mName(aName) {} TX_DECL_FUNCTION private: RefPtr mName; }; nsresult txErrorFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult) { *aResult = nullptr; return NS_ERROR_XPATH_BAD_EXTENSION_FUNCTION; } Expr::ResultType txErrorFunctionCall::getReturnType() { // It doesn't really matter what we return here, but it might // be a good idea to try to keep this as unoptimizable as possible return ANY_RESULT; } bool txErrorFunctionCall::isSensitiveTo(ContextSensitivity aContext) { // It doesn't really matter what we return here, but it might // be a good idea to try to keep this as unoptimizable as possible return true; } #ifdef TX_TO_STRING void txErrorFunctionCall::appendName(nsAString& aDest) { aDest.Append(mName->GetUTF16String()); } #endif static nsresult TX_ConstructXSLTFunction(nsAtom* aName, txStylesheetCompilerState* aState, FunctionCall** aFunction) { if (aName == nsGkAtoms::document) { *aFunction = new DocumentFunctionCall(aState->mElementContext->mBaseURI); } else if (aName == nsGkAtoms::key) { if (!aState->allowed(txIParseContext::KEY_FUNCTION)) { return NS_ERROR_XSLT_CALL_TO_KEY_NOT_ALLOWED; } *aFunction = new txKeyFunctionCall(aState->mElementContext->mMappings); } else if (aName == nsGkAtoms::formatNumber) { *aFunction = new txFormatNumberFunctionCall( aState->mStylesheet, aState->mElementContext->mMappings); } else if (aName == nsGkAtoms::current) { *aFunction = new CurrentFunctionCall(); } else if (aName == nsGkAtoms::unparsedEntityUri) { return NS_ERROR_NOT_IMPLEMENTED; } else if (aName == nsGkAtoms::generateId) { *aFunction = new GenerateIdFunctionCall(); } else if (aName == nsGkAtoms::systemProperty) { *aFunction = new txXSLTEnvironmentFunctionCall( txXSLTEnvironmentFunctionCall::SYSTEM_PROPERTY, aState->mElementContext->mMappings); } else if (aName == nsGkAtoms::elementAvailable) { *aFunction = new txXSLTEnvironmentFunctionCall( txXSLTEnvironmentFunctionCall::ELEMENT_AVAILABLE, aState->mElementContext->mMappings); } else if (aName == nsGkAtoms::functionAvailable) { *aFunction = new txXSLTEnvironmentFunctionCall( txXSLTEnvironmentFunctionCall::FUNCTION_AVAILABLE, aState->mElementContext->mMappings); } else { return NS_ERROR_XPATH_UNKNOWN_FUNCTION; } MOZ_ASSERT(*aFunction); return NS_OK; } extern nsresult TX_ConstructEXSLTFunction(nsAtom* aName, int32_t aNamespaceID, txStylesheetCompilerState* aState, FunctionCall** aResult); static nsresult findFunction(nsAtom* aName, int32_t aNamespaceID, txStylesheetCompilerState* aState, FunctionCall** aResult) { if (aNamespaceID == kNameSpaceID_None) { return TX_ConstructXSLTFunction(aName, aState, aResult); } return TX_ConstructEXSLTFunction(aName, aNamespaceID, aState, aResult); } extern bool TX_XSLTFunctionAvailable(nsAtom* aName, int32_t aNameSpaceID) { RefPtr compiler = new txStylesheetCompiler(u""_ns, ReferrerPolicy::_empty, nullptr); NS_ENSURE_TRUE(compiler, false); UniquePtr fnCall; return NS_SUCCEEDED( findFunction(aName, aNameSpaceID, compiler, getter_Transfers(fnCall))); } nsresult txStylesheetCompilerState::resolveFunctionCall( nsAtom* aName, int32_t aID, FunctionCall** aFunction) { *aFunction = nullptr; nsresult rv = findFunction(aName, aID, this, aFunction); if (rv == NS_ERROR_XPATH_UNKNOWN_FUNCTION && (aID != kNameSpaceID_None || fcp())) { *aFunction = new txErrorFunctionCall(aName); rv = NS_OK; } return rv; } bool txStylesheetCompilerState::caseInsensitiveNameTests() { return false; } void txStylesheetCompilerState::SetErrorOffset(uint32_t aOffset) { // XXX implement me } txElementContext::txElementContext(const nsAString& aBaseURI) : mPreserveWhitespace(false), mForwardsCompatibleParsing(true), mBaseURI(aBaseURI), mMappings(new txNamespaceMap), mDepth(0) { mInstructionNamespaces.AppendElement(kNameSpaceID_XSLT); } txElementContext::txElementContext(const txElementContext& aOther) : mPreserveWhitespace(aOther.mPreserveWhitespace), mForwardsCompatibleParsing(aOther.mForwardsCompatibleParsing), mBaseURI(aOther.mBaseURI), mMappings(aOther.mMappings), mDepth(0) { mInstructionNamespaces = aOther.mInstructionNamespaces.Clone(); }