mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-10 02:29:43 +00:00
1135 lines
33 KiB
C++
1135 lines
33 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* 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 "mozilla/ArrayUtils.h"
|
|
#include "mozilla/Move.h"
|
|
|
|
#include "txStylesheetCompiler.h"
|
|
#include "txStylesheetCompileHandlers.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "txURIUtils.h"
|
|
#include "nsWhitespaceTokenizer.h"
|
|
#include "txStylesheet.h"
|
|
#include "txInstructions.h"
|
|
#include "txToplevelItems.h"
|
|
#include "txExprParser.h"
|
|
#include "txLog.h"
|
|
#include "txPatternParser.h"
|
|
#include "txStringUtils.h"
|
|
#include "txXSLTFunctions.h"
|
|
#include "nsICategoryManager.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsTArray.h"
|
|
|
|
using namespace mozilla;
|
|
using mozilla::net::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, nsIAtom* aLocalName,
|
|
nsIAtom* 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);
|
|
|
|
nsAutoArrayPtr<txStylesheetAttr> atts;
|
|
if (aAttrCount > 0) {
|
|
atts = new 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]);
|
|
|
|
nsCOMPtr<nsIAtom> 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);
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIAtom> 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,
|
|
aAttrCount);
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompiler::startElementInternal(int32_t aNamespaceID,
|
|
nsIAtom* aLocalName,
|
|
nsIAtom* 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;
|
|
}
|
|
}
|
|
|
|
// xml:base
|
|
if (attr->mNamespaceID == kNameSpaceID_XML &&
|
|
attr->mLocalName == nsGkAtoms::base &&
|
|
!attr->mValue.IsEmpty()) {
|
|
rv = ensureNewElementContext();
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString uri;
|
|
URIUtils::resolveHref(attr->mValue, mElementContext->mBaseURI, uri);
|
|
mElementContext->mBaseURI = uri;
|
|
}
|
|
|
|
// 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;
|
|
|
|
if (!mElementContext->mInstructionNamespaces.
|
|
AppendElement(namespaceID)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
rv = pushPtr(const_cast<txElementHandler*>(handler), eElementHandler);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
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)) {
|
|
nsAutoPtr<txInstruction> instr(new txRemoveVariable(var->mName));
|
|
rv = addInstruction(Move(instr));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mInScopeVariables.RemoveElementAt(i);
|
|
delete var;
|
|
}
|
|
}
|
|
|
|
const txElementHandler* handler =
|
|
const_cast<const txElementHandler*>
|
|
(static_cast<txElementHandler*>(popPtr(eElementHandler)));
|
|
rv = (handler->mEndFunction)(*this);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!--mElementContext->mDepth) {
|
|
// this will delete the old object
|
|
mElementContext = 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;
|
|
}
|
|
|
|
nsAutoPtr<txElementContext>
|
|
context(new txElementContext(*mElementContext));
|
|
nsresult rv = pushObject(mElementContext);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mElementContext.forget();
|
|
mElementContext = 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),
|
|
mDoneWithThisStylesheet(false),
|
|
mNextInstrPtr(nullptr),
|
|
mToplevelIterator(nullptr)
|
|
{
|
|
// 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 = new txElementContext(aStylesheetURI);
|
|
NS_ENSURE_TRUE(mElementContext->mMappings, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
// Push the "old" txElementContext
|
|
rv = pushObject(0);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
txStylesheetCompilerState::~txStylesheetCompilerState()
|
|
{
|
|
while (!mObjectStack.isEmpty()) {
|
|
delete popObject();
|
|
}
|
|
|
|
int32_t i;
|
|
for (i = mInScopeVariables.Length() - 1; i >= 0; --i) {
|
|
delete mInScopeVariables[i];
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::pushHandlerTable(txHandlerTable* aTable)
|
|
{
|
|
nsresult rv = pushPtr(mHandlerTable, eHandlerTable);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mHandlerTable = aTable;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
txStylesheetCompilerState::popHandlerTable()
|
|
{
|
|
mHandlerTable = static_cast<txHandlerTable*>(popPtr(eHandlerTable));
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::pushSorter(txPushNewContext* aSorter)
|
|
{
|
|
nsresult rv = pushPtr(mSorter, ePushNewContext);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mSorter = aSorter;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
txStylesheetCompilerState::popSorter()
|
|
{
|
|
mSorter = static_cast<txPushNewContext*>(popPtr(ePushNewContext));
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::pushChooseGotoList()
|
|
{
|
|
nsresult rv = pushObject(mChooseGotoList);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
mChooseGotoList.forget();
|
|
mChooseGotoList = new txList;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
txStylesheetCompilerState::popChooseGotoList()
|
|
{
|
|
// this will delete the old value
|
|
mChooseGotoList = static_cast<txList*>(popObject());
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::pushObject(txObject* aObject)
|
|
{
|
|
return mObjectStack.push(aObject);
|
|
}
|
|
|
|
txObject*
|
|
txStylesheetCompilerState::popObject()
|
|
{
|
|
return static_cast<txObject*>(mObjectStack.pop());
|
|
}
|
|
|
|
nsresult
|
|
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);
|
|
return mOtherStack.push(aPtr);
|
|
}
|
|
|
|
void*
|
|
txStylesheetCompilerState::popPtr(enumStackType aType)
|
|
{
|
|
uint32_t stacklen = mTypeStack.Length();
|
|
if (stacklen == 0) {
|
|
NS_RUNTIMEABORT("Attempt to pop when type stack is empty");
|
|
}
|
|
|
|
enumStackType type = mTypeStack.ElementAt(stacklen - 1);
|
|
mTypeStack.RemoveElementAt(stacklen - 1);
|
|
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) {
|
|
NS_RUNTIMEABORT("Expected type does not match top element type");
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::addToplevelItem(txToplevelItem* aItem)
|
|
{
|
|
return mToplevelIterator.addBefore(aItem);
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::openInstructionContainer(txInstructionContainer* aContainer)
|
|
{
|
|
NS_PRECONDITION(!mNextInstrPtr, "can't nest instruction-containers");
|
|
|
|
mNextInstrPtr = aContainer->mFirstInstruction.StartAssignment();
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
txStylesheetCompilerState::closeInstructionContainer()
|
|
{
|
|
NS_ASSERTION(mGotoTargetPointers.IsEmpty(),
|
|
"GotoTargets still exists, did you forget to add txReturn?");
|
|
mNextInstrPtr = 0;
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::addInstruction(nsAutoPtr<txInstruction>&& aInstruction)
|
|
{
|
|
NS_PRECONDITION(mNextInstrPtr, "adding instruction outside container");
|
|
|
|
txInstruction* newInstr = aInstruction;
|
|
|
|
*mNextInstrPtr = aInstruction.forget();
|
|
mNextInstrPtr = newInstr->mNext.StartAssignment();
|
|
|
|
uint32_t i, count = mGotoTargetPointers.Length();
|
|
for (i = 0; i < count; ++i) {
|
|
*mGotoTargetPointers[i] = newInstr;
|
|
}
|
|
mGotoTargetPointers.Clear();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
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);
|
|
|
|
nsAutoPtr<txToplevelItem> item(new txDummyItem);
|
|
NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
nsresult rv = mToplevelIterator.addBefore(item);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
item.forget();
|
|
|
|
// step back to the dummy-item
|
|
mToplevelIterator.previous();
|
|
|
|
txACompileObserver* observer = static_cast<txStylesheetCompiler*>(this);
|
|
|
|
RefPtr<txStylesheetCompiler> compiler =
|
|
new txStylesheetCompiler(aURI, mStylesheet, &mToplevelIterator,
|
|
mReferrerPolicy, observer);
|
|
NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
// step forward before calling the observer in case of syncronous loading
|
|
mToplevelIterator.next();
|
|
|
|
if (mChildCompilerList.AppendElement(compiler) == nullptr) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
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);
|
|
NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
if (mChildCompilerList.AppendElement(compiler) == nullptr) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
nsresult rv = mObserver->loadURI(aURI, mStylesheetURI, mReferrerPolicy,
|
|
compiler);
|
|
if (NS_FAILED(rv)) {
|
|
mChildCompilerList.RemoveElement(compiler);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::addGotoTarget(txInstruction** aTargetPointer)
|
|
{
|
|
if (mGotoTargetPointers.AppendElement(aTargetPointer) == nullptr) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::addVariable(const txExpandedName& aName)
|
|
{
|
|
txInScopeVariable* var = new txInScopeVariable(aName);
|
|
if (!mInScopeVariables.AppendElement(var)) {
|
|
delete var;
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::resolveNamespacePrefix(nsIAtom* 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(nsIAtom* aName)
|
|
: mName(aName)
|
|
{
|
|
}
|
|
|
|
TX_DECL_FUNCTION
|
|
|
|
private:
|
|
nsCOMPtr<nsIAtom> 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
|
|
nsresult
|
|
txErrorFunctionCall::getNameAtom(nsIAtom** aAtom)
|
|
{
|
|
NS_IF_ADDREF(*aAtom = mName);
|
|
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
static nsresult
|
|
TX_ConstructXSLTFunction(nsIAtom* aName, int32_t aNamespaceID,
|
|
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;
|
|
}
|
|
|
|
typedef nsresult (*txFunctionFactory)(nsIAtom* aName,
|
|
int32_t aNamespaceID,
|
|
txStylesheetCompilerState* aState,
|
|
FunctionCall** aResult);
|
|
struct txFunctionFactoryMapping
|
|
{
|
|
const char* const mNamespaceURI;
|
|
int32_t mNamespaceID;
|
|
txFunctionFactory mFactory;
|
|
};
|
|
|
|
extern nsresult
|
|
TX_ConstructEXSLTFunction(nsIAtom *aName,
|
|
int32_t aNamespaceID,
|
|
txStylesheetCompilerState* aState,
|
|
FunctionCall **aResult);
|
|
|
|
static txFunctionFactoryMapping kExtensionFunctions[] = {
|
|
{ "", kNameSpaceID_Unknown, TX_ConstructXSLTFunction },
|
|
{ "http://exslt.org/common", kNameSpaceID_Unknown,
|
|
TX_ConstructEXSLTFunction },
|
|
{ "http://exslt.org/sets", kNameSpaceID_Unknown,
|
|
TX_ConstructEXSLTFunction },
|
|
{ "http://exslt.org/strings", kNameSpaceID_Unknown,
|
|
TX_ConstructEXSLTFunction },
|
|
{ "http://exslt.org/math", kNameSpaceID_Unknown,
|
|
TX_ConstructEXSLTFunction },
|
|
{ "http://exslt.org/dates-and-times", kNameSpaceID_Unknown,
|
|
TX_ConstructEXSLTFunction }
|
|
};
|
|
|
|
extern nsresult
|
|
TX_ResolveFunctionCallXPCOM(const nsCString &aContractID, int32_t aNamespaceID,
|
|
nsIAtom *aName, nsISupports *aState,
|
|
FunctionCall **aFunction);
|
|
|
|
struct txXPCOMFunctionMapping
|
|
{
|
|
int32_t mNamespaceID;
|
|
nsCString mContractID;
|
|
};
|
|
|
|
static nsTArray<txXPCOMFunctionMapping> *sXPCOMFunctionMappings = nullptr;
|
|
|
|
static nsresult
|
|
findFunction(nsIAtom* aName, int32_t aNamespaceID,
|
|
txStylesheetCompilerState* aState, FunctionCall** aResult)
|
|
{
|
|
if (kExtensionFunctions[0].mNamespaceID == kNameSpaceID_Unknown) {
|
|
uint32_t i;
|
|
for (i = 0; i < ArrayLength(kExtensionFunctions); ++i) {
|
|
txFunctionFactoryMapping& mapping = kExtensionFunctions[i];
|
|
NS_ConvertASCIItoUTF16 namespaceURI(mapping.mNamespaceURI);
|
|
mapping.mNamespaceID =
|
|
txNamespaceManager::getNamespaceID(namespaceURI);
|
|
}
|
|
}
|
|
|
|
uint32_t i;
|
|
for (i = 0; i < ArrayLength(kExtensionFunctions); ++i) {
|
|
const txFunctionFactoryMapping& mapping = kExtensionFunctions[i];
|
|
if (mapping.mNamespaceID == aNamespaceID) {
|
|
return mapping.mFactory(aName, aNamespaceID, aState, aResult);
|
|
}
|
|
}
|
|
|
|
if (!sXPCOMFunctionMappings) {
|
|
sXPCOMFunctionMappings = new nsTArray<txXPCOMFunctionMapping>;
|
|
}
|
|
|
|
txXPCOMFunctionMapping *map = nullptr;
|
|
uint32_t count = sXPCOMFunctionMappings->Length();
|
|
for (i = 0; i < count; ++i) {
|
|
map = &sXPCOMFunctionMappings->ElementAt(i);
|
|
if (map->mNamespaceID == aNamespaceID) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == count) {
|
|
nsresult rv;
|
|
nsCOMPtr<nsICategoryManager> catman =
|
|
do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsAutoString namespaceURI;
|
|
rv = txNamespaceManager::getNamespaceURI(aNamespaceID, namespaceURI);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsXPIDLCString contractID;
|
|
rv = catman->GetCategoryEntry("XSLT-extension-functions",
|
|
NS_ConvertUTF16toUTF8(namespaceURI).get(),
|
|
getter_Copies(contractID));
|
|
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
|
return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
|
|
}
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
map = sXPCOMFunctionMappings->AppendElement();
|
|
if (!map) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
map->mNamespaceID = aNamespaceID;
|
|
map->mContractID = contractID;
|
|
}
|
|
|
|
return TX_ResolveFunctionCallXPCOM(map->mContractID, aNamespaceID, aName,
|
|
nullptr, aResult);
|
|
}
|
|
|
|
extern bool
|
|
TX_XSLTFunctionAvailable(nsIAtom* aName, int32_t aNameSpaceID)
|
|
{
|
|
RefPtr<txStylesheetCompiler> compiler =
|
|
new txStylesheetCompiler(EmptyString(),
|
|
mozilla::net::RP_Default, nullptr);
|
|
NS_ENSURE_TRUE(compiler, false);
|
|
|
|
nsAutoPtr<FunctionCall> fnCall;
|
|
|
|
return NS_SUCCEEDED(findFunction(aName, aNameSpaceID, compiler,
|
|
getter_Transfers(fnCall)));
|
|
}
|
|
|
|
nsresult
|
|
txStylesheetCompilerState::resolveFunctionCall(nsIAtom* 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
|
|
}
|
|
|
|
/* static */
|
|
void
|
|
txStylesheetCompilerState::shutdown()
|
|
{
|
|
delete sXPCOMFunctionMappings;
|
|
sXPCOMFunctionMappings = nullptr;
|
|
}
|
|
|
|
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;
|
|
}
|