summaryrefslogtreecommitdiffstats
path: root/dom/xslt/xslt/txStylesheetCompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/xslt/xslt/txStylesheetCompiler.cpp')
-rw-r--r--dom/xslt/xslt/txStylesheetCompiler.cpp839
1 files changed, 839 insertions, 0 deletions
diff --git a/dom/xslt/xslt/txStylesheetCompiler.cpp b/dom/xslt/xslt/txStylesheetCompiler.cpp
new file mode 100644
index 0000000000..82b6de4c79
--- /dev/null
+++ b/dom/xslt/xslt/txStylesheetCompiler.cpp
@@ -0,0 +1,839 @@
+/* -*- 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 <utility>
+
+#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<txStylesheetAttr[]> atts;
+ if (aAttrCount > 0) {
+ atts = MakeUnique<txStylesheetAttr[]>(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<nsAtom> 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<nsAtom> 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<txElementHandler*>(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<txRemoveVariable>(var.mName));
+
+ mInScopeVariables.RemoveElementAt(i);
+ }
+ }
+
+ const txElementHandler* handler = const_cast<const txElementHandler*>(
+ static_cast<txElementHandler*>(popPtr(eElementHandler)));
+ (handler->mEndFunction)(*this);
+
+ if (!--mElementContext->mDepth) {
+ // this will delete the old object
+ mElementContext = WrapUnique(static_cast<txElementContext*>(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<txElementContext> 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<txElementContext>(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<txHandlerTable*>(popPtr(eHandlerTable));
+}
+
+void txStylesheetCompilerState::pushSorter(txPushNewContext* aSorter) {
+ pushPtr(mSorter, ePushNewContext);
+ mSorter = aSorter;
+}
+
+void txStylesheetCompilerState::popSorter() {
+ mSorter = static_cast<txPushNewContext*>(popPtr(ePushNewContext));
+}
+
+void txStylesheetCompilerState::pushChooseGotoList() {
+ pushObject(mChooseGotoList.release());
+ mChooseGotoList = MakeUnique<txList>();
+}
+
+void txStylesheetCompilerState::popChooseGotoList() {
+ // this will delete the old value
+ mChooseGotoList = WrapUnique(static_cast<txList*>(popObject()));
+}
+
+void txStylesheetCompilerState::pushObject(txObject* aObject) {
+ mObjectStack.push(aObject);
+}
+
+txObject* txStylesheetCompilerState::popObject() {
+ return static_cast<txObject*>(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<txInstruction>&& 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<txToplevelItem> item(new txDummyItem);
+
+ mToplevelIterator.addBefore(item.release());
+
+ // step back to the dummy-item
+ mToplevelIterator.previous();
+
+ txACompileObserver* observer = static_cast<txStylesheetCompiler*>(this);
+
+ RefPtr<txStylesheetCompiler> 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<txStylesheetCompiler*>(this);
+
+ RefPtr<txStylesheetCompiler> 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<nsAtom> 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<txStylesheetCompiler> compiler =
+ new txStylesheetCompiler(u""_ns, ReferrerPolicy::_empty, nullptr);
+ NS_ENSURE_TRUE(compiler, false);
+
+ UniquePtr<FunctionCall> 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();
+}