/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla XForms support. * * The Initial Developer of the Original Code is * Novell, Inc. * Portions created by the Initial Developer are Copyright (C) 2004 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Allan Beaufour * David Landwehr * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsXFormsMDGEngine.h" #include "nsIDOMDocument.h" #include "nsIDOMNodeList.h" #include "nsIDOMText.h" #include "nsIDOMXPathExpression.h" #include "nsIDOMXPathResult.h" #include "nsDeque.h" #ifdef DEBUG //# define DEBUG_XF_MDG const char* gMIPNames[] = {"type", "r/o", "req", "rel", "calc", "const", "p3ptype" }; #endif /* ------------------------------------ */ /* --------- nsXFormsMDGNode ---------- */ /* ------------------------------------ */ MOZ_DECL_CTOR_COUNTER(nsXFormsMDGNode) nsXFormsMDGNode::nsXFormsMDGNode(nsIDOMNode *aNode, const ModelItemPropName aType) : mDirty (PR_TRUE), mHasExpr(PR_FALSE), mContextNode(aNode), mCount(0), mType(aType), mContextSize(0), mContextPosition(0), mDynFunc(PR_FALSE), mNext(nsnull) { MOZ_COUNT_CTOR(nsXFormsMDGNode); } nsXFormsMDGNode::~nsXFormsMDGNode() { MOZ_COUNT_DTOR(nsXFormsMDGNode); } void nsXFormsMDGNode::SetExpression(nsIDOMXPathExpression *aExpression, PRBool aDynFunc, PRInt32 aContextPosition, PRInt32 aContextSize) { mHasExpr = PR_TRUE; mDynFunc = aDynFunc; mExpression = aExpression; mContextPosition = aContextPosition; mContextSize = aContextSize; } PRBool nsXFormsMDGNode::HasExpr() const { return mHasExpr; } PRBool nsXFormsMDGNode::IsDirty() const { // A node is always dirty, if it depends on a dynamic function. return mDirty || mDynFunc; } void nsXFormsMDGNode::MarkDirty() { mDirty = PR_TRUE; } void nsXFormsMDGNode::MarkClean() { mDirty = PR_FALSE; } /* ------------------------------------ */ /* -------- nsXFormsMDGEngine --------- */ /* ------------------------------------ */ MOZ_DECL_CTOR_COUNTER(nsXFormsMDGEngine) nsXFormsMDGEngine::nsXFormsMDGEngine() : mNodesInGraph(0) { MOZ_COUNT_CTOR(nsXFormsMDGEngine); } nsXFormsMDGEngine::~nsXFormsMDGEngine() { mNodeToMDG.Enumerate(DeleteLinkedNodes, nsnull); MOZ_COUNT_DTOR(nsXFormsMDGEngine); } nsresult nsXFormsMDGEngine::Init() { nsresult rv = NS_ERROR_FAILURE; if (mNodeStates.Init() && mNodeToMDG.Init()) { rv = NS_OK; } return rv; } nsresult nsXFormsMDGEngine::AddMIP(ModelItemPropName aType, nsIDOMXPathExpression *aExpression, nsXFormsMDGSet *aDependencies, PRBool aDynFunc, nsIDOMNode *aContextNode, PRInt32 aContextPos, PRInt32 aContextSize) { NS_ENSURE_ARG(aContextNode); #ifdef DEBUG_XF_MDG nsAutoString nodename; aContextNode->GetNodeName(nodename); printf("nsXFormsMDGEngine::AddMIP(aContextNode=%s, aExpression=%p, aDependencies=|%d|,\n", NS_ConvertUCS2toUTF8(nodename).get(), (void*) aExpression, aDependencies->Count()); printf(" aContextPos=%d, aContextSize=%d, aType=%s, aDynFunc=%d)\n", aContextPos, aContextSize, gMIPNames[aType], aDynFunc); #endif nsXFormsMDGNode* newnode = GetNode(aContextNode, aType, PR_TRUE); if (!newnode) { return NS_ERROR_OUT_OF_MEMORY; } if (newnode->HasExpr()) { // MIP already in the graph. That is, there is already a MIP of the same // type for this node, which is illegal. // Example: return NS_ERROR_ABORT; } if (aExpression) { newnode->SetExpression(aExpression, aDynFunc, aContextPos, aContextSize); } // Add dependencies if (aDependencies) { nsCOMPtr dep_domnode; nsXFormsMDGNode* dep_gnode; for (PRInt32 i = 0; i < aDependencies->Count(); ++i) { dep_domnode = aDependencies->GetNode(i); if (!dep_domnode) { return NS_ERROR_NULL_POINTER; } dep_gnode = GetNode(dep_domnode, (ModelItemPropName) aType); if (!dep_gnode) { return NS_ERROR_OUT_OF_MEMORY; } if (aType == aType && dep_gnode->mContextNode == aContextNode) { // Reference to itself, ignore continue; } dep_gnode->mSuc.AppendElement(newnode); newnode->mCount++; } } return NS_OK; } nsresult nsXFormsMDGEngine::MarkNodeAsChanged(nsIDOMNode* aContextNode) { nsXFormsNodeState* ns = GetNCNodeState(aContextNode); NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE); ns->Set(kFlags_ALL_DISPATCH, PR_TRUE); // Get the node, eMode_type == get any type of node nsXFormsMDGNode* n = GetNode(aContextNode, eModel_type, PR_FALSE); if (n) { while (n) { n->MarkDirty(); n = n->mNext; } } else { // Add constraint to trigger validation of node n = GetNode(aContextNode, eModel_constraint, PR_TRUE); if (!n) { return NS_ERROR_OUT_OF_MEMORY; } n->MarkDirty(); NS_ENSURE_TRUE(mGraph.AppendElement(n), NS_ERROR_OUT_OF_MEMORY); } NS_ENSURE_TRUE(mMarkedNodes.AddNode(aContextNode), NS_ERROR_FAILURE); return NS_OK; } #ifdef DEBUG_beaufour #include #include #include void nsXFormsMDGEngine::PrintDot(const char* aFile) { FILE *FD = stdout; if (aFile) { FD = fopen(aFile, "w"); } fprintf(FD, "digraph {\n"); for (PRInt32 i = 0; i < mGraph.Count(); ++i) { nsXFormsMDGNode* g = NS_STATIC_CAST(nsXFormsMDGNode*, mGraph[i]); if (g) { nsAutoString domNodeName; g->mContextNode->GetNodeName(domNodeName); if (g->IsDirty()) { fprintf(FD, "\t%s [color=red];\n", NS_ConvertUCS2toUTF8(domNodeName).get()); } for (PRInt32 j = 0; j < g->mSuc.Count(); ++j) { nsXFormsMDGNode* sucnode = NS_STATIC_CAST(nsXFormsMDGNode*, g->mSuc[j]); if (sucnode) { nsAutoString sucName; sucnode->mContextNode->GetNodeName(sucName); fprintf(FD, "\t%s -> %s [label=\"%s\"];\n", NS_ConvertUCS2toUTF8(sucName).get(), NS_ConvertUCS2toUTF8(domNodeName).get(), gMIPNames[sucnode->mType]); } } } } fprintf(FD, "}\n"); if (FD) { fclose(FD); } } #endif nsresult nsXFormsMDGEngine::Recalculate(nsXFormsMDGSet* aChangedNodes) { NS_ENSURE_ARG(aChangedNodes); #ifdef DEBUG_XF_MDG printf("nsXFormsMDGEngine::Recalculcate(aChangedNodes=|%d|)\n", aChangedNodes->Count()); #endif NS_ENSURE_TRUE(aChangedNodes->AddSet(mMarkedNodes), NS_ERROR_OUT_OF_MEMORY); mMarkedNodes.Clear(); PRBool res = PR_TRUE; mFirstCalculate = mJustRebuilt; #ifdef DEBUG_XF_MDG printf("\taChangedNodes: %d\n", aChangedNodes->Count()); printf("\tmNodeToMDG: %d\n", mNodeToMDG.Count()); printf("\tmNodeStates: %d\n", mNodeStates.Count()); printf("\tGraph nodes: %d\n", mGraph.Count()); #endif // Go through all dirty nodes in the graph nsresult rv; nsXFormsMDGNode* g; for (PRInt32 i = 0; i < mGraph.Count(); ++i) { g = NS_STATIC_CAST(nsXFormsMDGNode*, mGraph[i]); if (!g) { NS_WARNING("nsXFormsMDGEngine::Calculcate(): Empty node in graph!!!"); continue; } NS_ASSERTION(g->mCount == 0, "nsXFormsMDGEngine::Calculcate(): Graph node with mCount != 0"); #ifdef DEBUG_XF_MDG nsAutoString domNodeName; g->mContextNode->GetNodeName(domNodeName); printf("\tNode #%d: This=%p, Dirty=%d, DynFunc=%d, Type=%d, Count=%d, Suc=%d, CSize=%d, CPos=%d, Next=%p, domnode=%s\n", i, (void*) g, g->IsDirty(), g->mDynFunc, g->mType, g->mCount, g->mSuc.Count(), g->mContextSize, g->mContextPosition, (void*) g->mNext, NS_ConvertUCS2toUTF8(domNodeName).get()); #endif // Ignore node if it is not dirty if (!g->IsDirty()) { continue; } nsXFormsNodeState* ns = GetNCNodeState(g->mContextNode); NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE); PRBool constraint = PR_TRUE; // Find MIP-type and handle it accordingly switch (g->mType) { case eModel_calculate: if (g->HasExpr()) { nsISupports* retval; rv = g->mExpression->Evaluate(g->mContextNode, nsIDOMXPathResult::STRING_TYPE, nsnull, &retval); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr xpath_res = do_QueryInterface(retval); NS_ENSURE_TRUE(xpath_res, NS_ERROR_OUT_OF_MEMORY); nsAutoString nodeval; rv = xpath_res->GetStringValue(nodeval); NS_ENSURE_SUCCESS(rv, rv); rv = SetNodeValue(g->mContextNode, nodeval, PR_FALSE, nsnull); if (NS_SUCCEEDED(rv)) { NS_ENSURE_TRUE(aChangedNodes->AddNode(g->mContextNode), NS_ERROR_FAILURE); } } ns->Set(eFlag_DISPATCH_VALUE_CHANGED, PR_TRUE); break; case eModel_constraint: if (g->HasExpr()) { rv = BooleanExpression(g, constraint); NS_ENSURE_SUCCESS(rv, rv); } /// /// @todo Schema validity should be checked here (XXX) if (ns->IsConstraint() != constraint) { ns->Set(eFlag_CONSTRAINT, constraint); ns->Set(eFlag_DISPATCH_VALID_CHANGED, PR_TRUE); NS_ENSURE_TRUE(aChangedNodes->AddNode(g->mContextNode), NS_ERROR_FAILURE); } break; case eModel_readonly: if (g->HasExpr()) { rv = ComputeMIPWithInheritance(eFlag_READONLY, eFlag_DISPATCH_READONLY_CHANGED, eFlag_INHERITED_READONLY, g, aChangedNodes); NS_ENSURE_SUCCESS(rv, rv); } break; case eModel_relevant: if (g->HasExpr()) { rv = ComputeMIPWithInheritance(eFlag_RELEVANT, eFlag_DISPATCH_RELEVANT_CHANGED, eFlag_INHERITED_RELEVANT, g, aChangedNodes); NS_ENSURE_SUCCESS(rv, rv); } break; case eModel_required: if (g->HasExpr()) { PRBool didChange; rv = ComputeMIP(eFlag_REQUIRED, eFlag_DISPATCH_REQUIRED_CHANGED, g, didChange); NS_ENSURE_SUCCESS(rv, rv); if (didChange) { NS_ENSURE_TRUE(aChangedNodes->AddNode(g->mContextNode), NS_ERROR_FAILURE); } } break; default: NS_ERROR("There was no expression which matched\n"); res = PR_FALSE; break; } // Mark successors dirty nsXFormsMDGNode* sucnode; for (PRInt32 j = 0; j < g->mSuc.Count(); ++j) { sucnode = NS_STATIC_CAST(nsXFormsMDGNode*, g->mSuc[j]); if (!sucnode) { NS_ERROR("nsXFormsMDGEngine::Calculate(): Node has NULL successor!"); return NS_ERROR_FAILURE; } sucnode->MarkDirty(); } g->MarkClean(); } aChangedNodes->MakeUnique(); #ifdef DEBUG_XF_MDG printf("\taChangedNodes: %d\n", aChangedNodes->Count()); printf("\tmNodeToMDG: %d\n", mNodeToMDG.Count()); printf("\tmNodeStates: %d\n", mNodeStates.Count()); printf("\tGraph nodes: %d\n", mGraph.Count()); #endif return res; } nsresult nsXFormsMDGEngine::Rebuild() { #ifdef DEBUG_XF_MDG printf("nsXFormsMDGEngine::Rebuild()\n"); #endif nsresult rv = NS_OK; mJustRebuilt = PR_TRUE; mFirstCalculate = PR_FALSE; mGraph.Clear(); mNodeStates.Clear(); nsDeque sortedNodes(nsnull); #ifdef DEBUG_XF_MDG printf("\tmNodesInGraph: %d\n", mNodesInGraph); printf("\tmNodeToMDG: %d\n", mNodeToMDG.Count()); printf("\tmNodeStates: %d\n", mNodeStates.Count()); #endif // Initial scan for nsXFormsMDGNodes with no dependencies (mCount == 0) PRUint32 entries = mNodeToMDG.EnumerateRead(AddStartNodes, &sortedNodes); if (entries != mNodeToMDG.Count()) { return NS_ERROR_OUT_OF_MEMORY; } #ifdef DEBUG_XF_MDG printf("\tStartNodes: %d\n", sortedNodes.GetSize()); #endif nsXFormsMDGNode* node; while ((node = NS_STATIC_CAST(nsXFormsMDGNode*, sortedNodes.Pop()))) { for (PRInt32 i = 0; i < node->mSuc.Count(); ++i) { nsXFormsMDGNode* sucNode = NS_STATIC_CAST(nsXFormsMDGNode*, node->mSuc[i]); NS_ASSERTION(sucNode, "XForms: NULL successor node"); sucNode->mCount--; if (sucNode->mCount == 0) { sortedNodes.Push(sucNode); } } node->MarkDirty(); if (!mGraph.AppendElement(node)) { return NS_ERROR_OUT_OF_MEMORY; } } if (mGraph.Count() != mNodesInGraph) { NS_WARNING("XForms: There are loops in the MDG\n"); rv = NS_ERROR_ABORT; } mNodesInGraph = 0; return rv; } nsresult nsXFormsMDGEngine::ClearDispatchFlags() { mJustRebuilt = PR_FALSE; return AndFlags(kFlags_NOT_DISPATCH) ? NS_OK : NS_ERROR_FAILURE; } nsresult nsXFormsMDGEngine::Clear() { #ifdef DEBUG_XF_MDG printf("nsXFormsMDGEngine::Clear()\n"); #endif nsresult rv = mNodeToMDG.Enumerate(DeleteLinkedNodes, nsnull); NS_ENSURE_SUCCESS(rv, rv); mNodeToMDG.Clear(); mNodeStates.Clear(); mGraph.Clear(); mNodesInGraph = 0; return NS_OK; } nsresult nsXFormsMDGEngine::GetNodeValue(nsIDOMNode *aContextNode, nsAString &aNodeValue) { nsresult rv; nsCOMPtr childNode; PRUint16 nodeType; rv = aContextNode->GetNodeType(&nodeType); NS_ENSURE_SUCCESS(rv, rv); switch(nodeType) { case nsIDOMNode::ATTRIBUTE_NODE: case nsIDOMNode::TEXT_NODE: case nsIDOMNode::CDATA_SECTION_NODE: case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: case nsIDOMNode::COMMENT_NODE: rv = aContextNode->GetNodeValue(aNodeValue); NS_ENSURE_SUCCESS(rv, rv); break; case nsIDOMNode::ELEMENT_NODE: rv = aContextNode->GetFirstChild(getter_AddRefs(childNode)); if (NS_FAILED(rv) || !childNode) { // No child aNodeValue.Truncate(0); } else { PRUint16 childType; rv = childNode->GetNodeType(&childType); NS_ENSURE_SUCCESS(rv, rv); if ( childType == nsIDOMNode::TEXT_NODE || childType == nsIDOMNode::CDATA_SECTION_NODE) { rv = childNode->GetNodeValue(aNodeValue); NS_ENSURE_SUCCESS(rv, rv); } else { // Not a text child aNodeValue.Truncate(0); } } break; default: /// Asked for a node which cannot have a text child /// @todo Should return more specific error? (XXX) return NS_ERROR_ILLEGAL_VALUE; break; } return NS_OK; } nsresult nsXFormsMDGEngine::SetNodeValue(nsIDOMNode *aContextNode, const nsAString &aNodeValue, PRBool aMarkNode, PRBool *aNodeChanged) { if (aNodeChanged) { *aNodeChanged = PR_FALSE; } const nsXFormsNodeState* ns = GetNodeState(aContextNode); NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE); if (ns->IsReadonly()) { /// /// @todo Better feedback for readonly nodes? (XXX) return NS_OK; } nsCOMPtr childNode; PRUint16 nodeType; nsresult rv = aContextNode->GetNodeType(&nodeType); NS_ENSURE_SUCCESS(rv, rv); nsAutoString oldValue; rv = GetNodeValue(aContextNode, oldValue); NS_ENSURE_SUCCESS(rv, rv); if (oldValue.Equals(aNodeValue)) { return NS_OK; } switch(nodeType) { case nsIDOMNode::ATTRIBUTE_NODE: case nsIDOMNode::TEXT_NODE: case nsIDOMNode::CDATA_SECTION_NODE: case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: case nsIDOMNode::COMMENT_NODE: rv = aContextNode->SetNodeValue(aNodeValue); NS_ENSURE_SUCCESS(rv, rv); break; case nsIDOMNode::ELEMENT_NODE: rv = aContextNode->GetFirstChild(getter_AddRefs(childNode)); NS_ENSURE_SUCCESS(rv, rv); if (!childNode) { rv = CreateNewChild(aContextNode, aNodeValue); NS_ENSURE_SUCCESS(rv, rv); } else { PRUint16 childType; rv = childNode->GetNodeType(&childType); NS_ENSURE_SUCCESS(rv, rv); if ( childType == nsIDOMNode::TEXT_NODE || childType == nsIDOMNode::CDATA_SECTION_NODE) { rv = childNode->SetNodeValue(aNodeValue); NS_ENSURE_SUCCESS(rv, rv); } else { // Not a text child, create a new one rv = CreateNewChild(aContextNode, aNodeValue, childNode); NS_ENSURE_SUCCESS(rv, rv); } } break; default: /// Unsupported nodeType /// @todo Should return more specific error? (XXX) return NS_ERROR_ILLEGAL_VALUE; break; } // NB: Never reached for Readonly nodes. if (aNodeChanged) { *aNodeChanged = PR_TRUE; } if (aMarkNode) { MarkNodeAsChanged(aContextNode); } return NS_OK; } const nsXFormsNodeState* nsXFormsMDGEngine::GetNodeState(nsIDOMNode *aContextNode) { return GetNCNodeState(aContextNode); } /**********************************************/ /* Private functions */ /**********************************************/ nsXFormsNodeState* nsXFormsMDGEngine::GetNCNodeState(nsIDOMNode *aContextNode) { nsXFormsNodeState* ns = nsnull; if (aContextNode && !mNodeStates.Get(aContextNode, &ns)) { ns = new nsXFormsNodeState(kFlags_DEFAULT | ((mJustRebuilt && mFirstCalculate) ? kFlags_INITIAL_DISPATCH : 0)); NS_ASSERTION(ns, "Could not create new nsXFormsNodeState"); if (!mNodeStates.Put(aContextNode, ns)) { NS_ERROR("Could not insert new nsXFormsNodeState"); delete ns; return nsnull; } aContextNode->AddRef(); } return ns; } nsresult nsXFormsMDGEngine::CreateNewChild(nsIDOMNode *aContextNode, const nsAString &aNodeValue, nsIDOMNode *aBeforeNode) { nsresult rv; nsCOMPtr document; rv = aContextNode->GetOwnerDocument(getter_AddRefs(document)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr textNode; rv = document->CreateTextNode(aNodeValue, getter_AddRefs(textNode)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr newNode; if (aBeforeNode) { rv = aContextNode->InsertBefore(textNode, aBeforeNode, getter_AddRefs(newNode)); } else { rv = aContextNode->AppendChild(textNode, getter_AddRefs(newNode)); } return rv; } PLDHashOperator nsXFormsMDGEngine::DeleteLinkedNodes(nsISupports *aKey, nsAutoPtr &aNode, void *aArg) { if (!aNode) { NS_WARNING("nsXFormsMDGEngine::DeleteLinkedNodes() called with aNode == nsnull!"); return PL_DHASH_STOP; } nsXFormsMDGNode* temp; nsXFormsMDGNode* next = aNode->mNext; while (next) { temp = next; next = next->mNext; delete temp; } return PL_DHASH_NEXT; } nsXFormsMDGNode* nsXFormsMDGEngine::GetNode(nsIDOMNode *aDomNode, ModelItemPropName aType, PRBool aCreate) { nsIDOMNode* nodeKey = aDomNode; nsXFormsMDGNode* nd = nsnull; #ifdef DEBUG_XF_MDG printf("nsXFormsMDGEngine::GetNode(aDomNode=%p, aType=%s, aCreate=%d)\n", (void*) nodeKey, gMIPNames[aType], aCreate); #endif // Find correct type if (mNodeToMDG.Get(nodeKey, &nd) && aType != eModel_type) { while (nd && aType != nd->mType) { nd = nd->mNext; } } // Eventually create node if (!nd && aCreate){ nd = new nsXFormsMDGNode(nodeKey, aType); if (!nd) { NS_ERROR("Could not allocate room for new nsXFormsMDGNode"); return nsnull; } #ifdef DEBUG_XF_MDG printf("\tNode not found, create new MDGNode '%p'\n", (void*) nd); #endif // Link to existing node nsXFormsMDGNode* nd_exists; if (mNodeToMDG.Get(nodeKey, &nd_exists)) { while (nd_exists->mNext) { nd_exists = nd_exists->mNext; } #ifdef DEBUG_XF_MDG printf("\tLinking to existing MDGNode '%p'\n", (void*) nd_exists); #endif nd_exists->mNext = nd; } else { if (!mNodeToMDG.Put(nodeKey, nd)) { delete nd; NS_ERROR("Could not insert new node in HashTable!"); return nsnull; } nodeKey->AddRef(); } mNodesInGraph++; } return nd; } PLDHashOperator nsXFormsMDGEngine::AddStartNodes(nsISupports *aKey, nsXFormsMDGNode *aNode, void *aDeque) { #ifdef DEBUG_XF_MDG printf("nsXFormsMDGEngine::AddStartNodes(aKey=n/a, aNode=%p, aDeque=%p)\n", (void*) aNode, aDeque); #endif nsDeque* deque = NS_STATIC_CAST(nsDeque*, aDeque); if (!deque) { NS_ERROR("nsXFormsMDGEngine::AddStartNodes called with NULL aDeque"); return PL_DHASH_STOP; } while (aNode) { if (aNode->mCount == 0) { /// /// @todo Is it not possible to check error condition? (XXX) deque->Push(aNode); } aNode = aNode->mNext; } return PL_DHASH_NEXT; } PLDHashOperator nsXFormsMDGEngine::AndFlag(nsISupports *aKey, nsAutoPtr &aState, void *aMask) { PRUint16* andMask = NS_STATIC_CAST(PRUint16*, aMask); if (!andMask) { return PL_DHASH_STOP; } *aState &= *andMask; return PL_DHASH_NEXT; } PRBool nsXFormsMDGEngine::AndFlags(PRUint16 aAndMask) { PRUint32 entries = mNodeStates.Enumerate(AndFlag, &aAndMask); return (entries == mNodeStates.Count()) ? PR_TRUE : PR_FALSE; } nsresult nsXFormsMDGEngine::BooleanExpression(nsXFormsMDGNode* aNode, PRBool& state) { NS_ENSURE_ARG_POINTER(aNode); NS_ENSURE_TRUE(aNode->mExpression, NS_ERROR_FAILURE); /// @todo Use aNode->contextPosition and aNode->contextSize (XXX) /// @see https://bugzilla.mozilla.org/show_bug.cgi?id=265460 nsISupports* retval; nsresult rv; rv = aNode->mExpression->Evaluate(aNode->mContextNode, nsIDOMXPathResult::BOOLEAN_TYPE, nsnull, &retval); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr xpath_res = do_QueryInterface(retval); NS_ENSURE_TRUE(xpath_res, NS_ERROR_FAILURE); rv = xpath_res->GetBooleanValue(&state); return rv; } nsresult nsXFormsMDGEngine::ComputeMIP(eFlag_t aStateFlag, eFlag_t aDispatchFlag, nsXFormsMDGNode *aNode, PRBool &aDidChange) { NS_ENSURE_ARG(aNode); aDidChange = PR_FALSE; if (!aNode->mExpression) return NS_OK; nsXFormsNodeState* ns = GetNCNodeState(aNode->mContextNode); NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE); PRBool state; nsresult rv = BooleanExpression(aNode, state); NS_ENSURE_SUCCESS(rv, rv); PRBool cstate = ns->Test(aStateFlag); ns->Set(aStateFlag, state); aDidChange = (state != cstate) ? PR_TRUE : PR_FALSE; if (aDidChange) { ns->Set(aDispatchFlag, PR_TRUE); } return NS_OK; } nsresult nsXFormsMDGEngine::ComputeMIPWithInheritance(eFlag_t aStateFlag, eFlag_t aDispatchFlag, eFlag_t aInheritanceFlag, nsXFormsMDGNode *aNode, nsXFormsMDGSet *aSet) { nsresult rv; PRBool didChange; rv = ComputeMIP(aStateFlag, aDispatchFlag, aNode, didChange); NS_ENSURE_SUCCESS(rv, rv); if (didChange) { nsXFormsNodeState* ns = GetNCNodeState(aNode->mContextNode); NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE); if ( !(aStateFlag == eFlag_READONLY && ns->Test(aInheritanceFlag)) || (aStateFlag == eFlag_RELEVANT && ns->Test(aInheritanceFlag)) ) { NS_ENSURE_TRUE(aSet->AddNode(aNode->mContextNode), NS_ERROR_FAILURE); rv = AttachInheritance(aSet, aNode->mContextNode, ns->Test(aStateFlag), aStateFlag); } } return rv; } nsresult nsXFormsMDGEngine::AttachInheritance(nsXFormsMDGSet *aSet, nsIDOMNode *aSrc, PRBool aState, eFlag_t aStateFlag) { NS_ENSURE_ARG(aSrc); nsCOMPtr node; nsresult rv; PRBool updateNode = PR_FALSE; nsCOMPtr childList; rv = aSrc->GetChildNodes(getter_AddRefs(childList)); NS_ENSURE_SUCCESS(rv, rv); PRUint32 childCount; rv = childList->GetLength(&childCount); NS_ENSURE_SUCCESS(rv, rv); for (PRUint32 i = 0; i < childCount; i++) { rv = childList->Item(i, getter_AddRefs(node)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); nsXFormsNodeState *ns = GetNCNodeState(node); NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE); PRBool curState = ns->Test(aStateFlag); if (aStateFlag == eFlag_RELEVANT) { if (!aState) { // The nodes are getting irrelevant if (ns->Test(eFlag_INHERITED_RELEVANT) && curState) { ns->Set(eFlag_INHERITED_RELEVANT, PR_FALSE); ns->Set(eFlag_DISPATCH_RELEVANT_CHANGED, PR_TRUE); updateNode = PR_TRUE; } } else { // The nodes are becoming relevant if (curState) { // Relevant has changed from inheritance ns->Set(eFlag_DISPATCH_RELEVANT_CHANGED, PR_TRUE); ns->Set(eFlag_INHERITED_RELEVANT, PR_TRUE); updateNode = PR_TRUE; } } } else if (aStateFlag == eFlag_READONLY) { if (aState) { // The nodes are getting readonly if (!ns->Test(eFlag_INHERITED_READONLY) && curState == PR_FALSE) { ns->Set(eFlag_INHERITED_READONLY | eFlag_DISPATCH_READONLY_CHANGED, PR_TRUE); updateNode = PR_TRUE; } } else { // The nodes are getting readwrite if (curState) { ns->Set(eFlag_DISPATCH_READONLY_CHANGED, PR_TRUE); ns->Set(eFlag_INHERITED_READONLY, PR_FALSE); updateNode = PR_TRUE; } } } if (updateNode) { rv = AttachInheritance(aSet, node, aState, aStateFlag); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(aSet->AddNode(node), NS_ERROR_FAILURE); updateNode = PR_FALSE; } } return NS_OK; } nsresult nsXFormsMDGEngine::Invalidate() { nsXFormsMDGNode* g; for (PRInt32 i = 0; i < mGraph.Count(); ++i) { g = NS_STATIC_CAST(nsXFormsMDGNode*, mGraph[i]); NS_ENSURE_TRUE(g, NS_ERROR_FAILURE); g->MarkDirty(); } return NS_OK; }