Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsMDGEngine.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla XForms support.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Novell, Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Allan Beaufour <abeaufour@novell.com>
00024  *  David Landwehr <dlandwehr@novell.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsXFormsMDGEngine.h"
00041 
00042 #include "nsIDOMDocument.h"
00043 #include "nsIDOMNodeList.h"
00044 #include "nsIDOMText.h"
00045 #include "nsIDOMNSXPathExpression.h"
00046 #include "nsIDOMXPathResult.h"
00047 #include "nsDeque.h"
00048 #include "nsXFormsUtils.h"
00049 #include "nsDOMError.h"
00050 #include "nsIDOMElement.h"
00051 #include "nsXFormsModelElement.h"
00052 
00053 #ifdef DEBUG
00054 //#  define DEBUG_XF_MDG
00055   const char* gMIPNames[] = {"type",
00056                              "r/o",
00057                              "req",
00058                              "rel",
00059                              "calc",
00060                              "const",
00061                              "p3ptype"
00062   };
00063 #endif
00064 
00065 /* ------------------------------------ */
00066 /* --------- nsXFormsMDGNode ---------- */
00067 /* ------------------------------------ */
00068 
00069 nsXFormsMDGNode::nsXFormsMDGNode(nsIDOMNode             *aNode,
00070                                  const ModelItemPropName aType)
00071   : mDirty (PR_TRUE), mHasExpr(PR_FALSE), mContextNode(aNode),
00072     mCount(0), mType(aType), mContextSize(0), mContextPosition(0),
00073     mDynFunc(PR_FALSE), mNext(nsnull)
00074 {
00075   MOZ_COUNT_CTOR(nsXFormsMDGNode);
00076 }
00077 
00078 nsXFormsMDGNode::~nsXFormsMDGNode()
00079 {
00080   MOZ_COUNT_DTOR(nsXFormsMDGNode);
00081 }
00082 
00083 void
00084 nsXFormsMDGNode::SetExpression(nsIDOMNSXPathExpression *aExpression,
00085                                PRBool                   aDynFunc,
00086                                PRInt32                  aContextPosition,
00087                                PRInt32                  aContextSize)
00088 {
00089   mHasExpr = PR_TRUE;
00090   mDynFunc = aDynFunc;
00091   mExpression = aExpression;
00092   mContextPosition = aContextPosition;
00093   mContextSize = aContextSize;
00094 }
00095 
00096 PRBool
00097 nsXFormsMDGNode::HasExpr() const
00098 {
00099   return mHasExpr;
00100 }
00101 
00102 PRBool
00103 nsXFormsMDGNode::IsDirty() const
00104 {
00105   // A node is always dirty, if it depends on a dynamic function.
00106   return mDirty || mDynFunc;
00107 }
00108 
00109 void
00110 nsXFormsMDGNode::MarkDirty()
00111 {
00112   mDirty = PR_TRUE;
00113 }
00114 
00115 void
00116 nsXFormsMDGNode::MarkClean()
00117 {
00118   mDirty = PR_FALSE;
00119 }
00120 
00121 
00122 /* ------------------------------------ */
00123 /* -------- nsXFormsMDGEngine --------- */
00124 /* ------------------------------------ */
00125 
00126 nsXFormsMDGEngine::nsXFormsMDGEngine()
00127 : mNodesInGraph(0)
00128 {
00129   MOZ_COUNT_CTOR(nsXFormsMDGEngine);
00130 }
00131 
00132 nsXFormsMDGEngine::~nsXFormsMDGEngine()
00133 {
00134   mNodeToMDG.Enumerate(DeleteLinkedNodes, nsnull);
00135 
00136   MOZ_COUNT_DTOR(nsXFormsMDGEngine);
00137 }
00138 
00139 nsresult
00140 nsXFormsMDGEngine::Init(nsXFormsModelElement *aModel)
00141 {
00142   nsresult rv = NS_ERROR_FAILURE;
00143   if (mNodeStates.Init() && mNodeToMDG.Init()) {
00144     rv = NS_OK;
00145   }
00146 
00147   mModel = aModel;
00148 
00149   return rv;
00150 }
00151 
00152 nsresult
00153 nsXFormsMDGEngine::AddMIP(ModelItemPropName         aType,
00154                           nsIDOMNSXPathExpression  *aExpression,
00155                           nsCOMArray<nsIDOMNode>   *aDependencies,
00156                           PRBool                    aDynFunc,
00157                           nsIDOMNode               *aContextNode,
00158                           PRInt32                   aContextPos,
00159                           PRInt32                   aContextSize)
00160 {
00161   NS_ENSURE_ARG(aContextNode);
00162   
00163 #ifdef DEBUG_XF_MDG
00164   nsAutoString nodename;
00165   aContextNode->GetNodeName(nodename);
00166   printf("nsXFormsMDGEngine::AddMIP(aContextNode=%s, aExpression=%p, aDependencies=|%d|,\n",
00167          NS_ConvertUTF16toUTF8(nodename).get(),
00168          (void*) aExpression,
00169          aDependencies ? aDependencies->Count() : 0);
00170   printf("                          aContextPos=%d, aContextSize=%d, aType=%s, aDynFunc=%d)\n",
00171          aContextPos, aContextSize, gMIPNames[aType], aDynFunc);
00172 #endif
00173   nsXFormsMDGNode* newnode = GetNode(aContextNode,
00174                                      aType,
00175                                      PR_TRUE);
00176   
00177   if (!newnode) {
00178     return NS_ERROR_OUT_OF_MEMORY;
00179   }
00180   
00181   if (newnode->HasExpr()) {
00182     // MIP already in the graph. That is, there is already a MIP of the same
00183     // type for this node, which is illegal.
00184     // Example: <bind nodeset="x" required="true()" required="false()"/>
00185     return NS_ERROR_ABORT;
00186   }
00187 
00188   if (aExpression) {
00189     newnode->SetExpression(aExpression, aDynFunc, aContextPos, aContextSize);
00190   }
00191   
00192   // Add dependencies
00193   if (aDependencies) {
00194     nsCOMPtr<nsIDOMNode> dep_domnode;
00195     nsXFormsMDGNode* dep_gnode;
00196     for (PRInt32 i = 0; i < aDependencies->Count(); ++i) {
00197       dep_domnode = aDependencies->ObjectAt(i);
00198       if (!dep_domnode) {
00199         return NS_ERROR_NULL_POINTER;
00200       }
00201 
00202 #ifdef DEBUG_XF_MDG
00203       printf("\tDependency #%2d: %p\n", i, (void*) dep_domnode);
00204 #endif
00205       
00206       // Get calculate graph node for the dependency (only a calculate
00207       // property can influence another MIP).
00208       dep_gnode = GetNode(dep_domnode, eModel_calculate);
00209       if (!dep_gnode) {
00210         return NS_ERROR_OUT_OF_MEMORY;
00211       }
00212   
00213       if (dep_gnode->mType == aType && dep_gnode->mContextNode == aContextNode) {
00214         // Reference to itself, ignore
00215         continue;
00216       }
00217   
00218       // Add this node as a successor to the dependency (ie. the dependency
00219       // should be (re-)calculated before this node)
00220       dep_gnode->mSuc.AppendElement(newnode);
00221       newnode->mCount++;
00222     }
00223   }
00224 
00225   return NS_OK;
00226 }
00227 
00228 nsresult
00229 nsXFormsMDGEngine::MarkNodeAsChanged(nsIDOMNode* aContextNode)
00230 {
00231   return mMarkedNodes.AppendObject(aContextNode);
00232 }
00233 
00234 nsresult
00235 nsXFormsMDGEngine::HandleMarkedNodes(nsCOMArray<nsIDOMNode> *aArray)
00236 {
00237   NS_ENSURE_ARG_POINTER(aArray);
00238 
00239   // Handle nodes marked as changed
00240   for (PRInt32 i = 0; i < mMarkedNodes.Count(); ++i) {
00241     nsCOMPtr<nsIDOMNode> node = mMarkedNodes.ObjectAt(i);
00242     nsXFormsNodeState* ns = GetNCNodeState(node);
00243     NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE);
00244 
00245     ns->Set(kFlags_ALL_DISPATCH, PR_TRUE);
00246 
00247     // Get the node, eMode_type == get any type of node
00248     nsXFormsMDGNode* n = GetNode(node, eModel_type, PR_FALSE);
00249     if (n) {
00250       while (n) {
00251         n->MarkDirty();
00252         n = n->mNext;
00253       }
00254     } else {
00255       // Add constraint to trigger validation of node
00256       n = GetNode(node, eModel_constraint, PR_TRUE);
00257       if (!n) {
00258         return NS_ERROR_OUT_OF_MEMORY;
00259       }
00260       n->MarkDirty();
00261       NS_ENSURE_TRUE(mGraph.AppendElement(n), NS_ERROR_OUT_OF_MEMORY);
00262     }
00263 
00264     NS_ENSURE_TRUE(aArray->AppendObjects(mMarkedNodes),
00265                    NS_ERROR_OUT_OF_MEMORY);
00266 
00267   }
00268 
00269   mMarkedNodes.Clear();
00270 
00271   return NS_OK;
00272 }
00273 
00274 
00275 #ifdef DEBUG_beaufour
00276 #include <sys/types.h>
00277 #include <sys/stat.h>
00278 #include <fcntl.h>
00279 
00280 void
00281 nsXFormsMDGEngine::PrintDot(const char* aFile)
00282 {
00283   FILE *FD = stdout;
00284   if (aFile) {
00285     FD = fopen(aFile, "w");
00286   }
00287   fprintf(FD, "digraph {\n");
00288   for (PRInt32 i = 0; i < mGraph.Count(); ++i) {
00289     nsXFormsMDGNode* g = NS_STATIC_CAST(nsXFormsMDGNode*, mGraph[i]);
00290     if (g) {
00291       nsAutoString domNodeName;
00292       g->mContextNode->GetNodeName(domNodeName);
00293       
00294       if (g->IsDirty()) {
00295         fprintf(FD, "\t%s [color=red];\n",
00296                 NS_ConvertUTF16toUTF8(domNodeName).get());
00297       }
00298 
00299       for (PRInt32 j = 0; j < g->mSuc.Count(); ++j) {
00300         nsXFormsMDGNode* sucnode = NS_STATIC_CAST(nsXFormsMDGNode*,
00301                                                   g->mSuc[j]);
00302         if (sucnode) {
00303           nsAutoString sucName;
00304           sucnode->mContextNode->GetNodeName(sucName);
00305           fprintf(FD, "\t%s -> %s [label=\"%s\"];\n",
00306                   NS_ConvertUTF16toUTF8(sucName).get(),
00307                   NS_ConvertUTF16toUTF8(domNodeName).get(),
00308                   gMIPNames[sucnode->mType]);
00309         }
00310       }
00311     }
00312   }
00313   fprintf(FD, "}\n");
00314   if (FD) {
00315     fclose(FD);
00316   }
00317 }
00318 #endif
00319 
00320 nsresult
00321 nsXFormsMDGEngine::Recalculate(nsCOMArray<nsIDOMNode> *aChangedNodes)
00322 {
00323   NS_ENSURE_ARG(aChangedNodes);
00324 
00325 #ifdef DEBUG_XF_MDG
00326   printf("nsXFormsMDGEngine::Recalculate(aChangedNodes=|%d|)\n",
00327          aChangedNodes->Count());
00328 #endif
00329 
00330   // XXX: There's something wrong with the marking of nodes, as we assume that
00331   // recalculate will always be called first. bug 338146
00332   nsresult rv = HandleMarkedNodes(aChangedNodes);
00333   NS_ENSURE_SUCCESS(rv, rv);
00334   
00335   PRBool res = PR_TRUE;
00336 
00337   mFirstCalculate = mJustRebuilt;
00338 
00339 #ifdef DEBUG_XF_MDG
00340   printf("\taChangedNodes: %d\n", aChangedNodes->Count());
00341   printf("\tmNodeToMDG:    %d\n", mNodeToMDG.Count());
00342   printf("\tmNodeStates:   %d\n", mNodeStates.Count());
00343   printf("\tGraph nodes:   %d\n", mGraph.Count());
00344 #endif
00345   
00346   // Go through all dirty nodes in the graph
00347   nsXFormsMDGNode* g;
00348   for (PRInt32 i = 0; i < mGraph.Count(); ++i) {
00349     g = NS_STATIC_CAST(nsXFormsMDGNode*, mGraph[i]);
00350 
00351     if (!g) {
00352       NS_WARNING("nsXFormsMDGEngine::Calculate(): Empty node in graph!!!");
00353       continue;
00354     }
00355 
00356     NS_ASSERTION(g->mCount == 0,
00357                  "nsXFormsMDGEngine::Calculate(): Graph node with mCount != 0");
00358 
00359 #ifdef DEBUG_XF_MDG
00360     nsAutoString domNodeName;
00361     g->mContextNode->GetNodeName(domNodeName);
00362 
00363     printf("\tNode #%d: This=%p, Dirty=%d, DynFunc=%d, Type=%d, Count=%d, Suc=%d, CSize=%d, CPos=%d, Next=%p, HasExpr=%d, domnode=%s\n",
00364            i, (void*) g, g->IsDirty(), g->mDynFunc, g->mType,
00365            g->mCount, g->mSuc.Count(), g->mContextSize, g->mContextPosition,
00366            (void*) g->mNext, g->HasExpr(), NS_ConvertUTF16toUTF8(domNodeName).get());
00367 #endif
00368 
00369     // Ignore node if it is not dirty
00370     if (!g->IsDirty()) {
00371       continue;
00372     }
00373 
00374     nsXFormsNodeState* ns = GetNCNodeState(g->mContextNode);
00375     NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE);
00376     
00377     PRBool constraint = PR_TRUE;
00378     PRBool conChanged;
00379     // Find MIP-type and handle it accordingly
00380     switch (g->mType) {
00381     case eModel_calculate:
00382       if (g->HasExpr()) {
00383         nsCOMPtr<nsIDOMXPathResult> xpath_res;
00384         rv = g->mExpression->EvaluateWithContext(g->mContextNode,
00385                                                  g->mContextPosition,
00386                                                  g->mContextSize,
00387                                                  nsIDOMXPathResult::STRING_TYPE,
00388                                                  nsnull,
00389                                                  getter_AddRefs(xpath_res));
00390         NS_ENSURE_SUCCESS(rv, rv);
00391         NS_ENSURE_STATE(xpath_res);
00392         
00393         nsAutoString nodeval;
00394         rv = xpath_res->GetStringValue(nodeval);
00395         NS_ENSURE_SUCCESS(rv, rv);
00396 
00397         rv = SetNodeValueInternal(g->mContextNode,
00398                                   nodeval,
00399                                   PR_FALSE,
00400                                   PR_TRUE);
00401         if (NS_SUCCEEDED(rv)) {
00402           NS_ENSURE_TRUE(aChangedNodes->AppendObject(g->mContextNode),
00403                          NS_ERROR_FAILURE);
00404         }
00405 
00406         ns->Set(eFlag_DISPATCH_VALUE_CHANGED, PR_TRUE);
00407       }
00408 
00409       break;
00410       
00411     case eModel_constraint:
00412       if (g->HasExpr()) {
00413         rv = BooleanExpression(g, constraint);
00414         NS_ENSURE_SUCCESS(rv, rv);
00415       }
00416 
00417       conChanged = ns->IsConstraint() != constraint;
00418         // On the first calculate after a rebuild (mFirstCalculate) we also
00419         // add constraints to the set of changed nodes to trigger validation
00420         // of type information if present.
00421       if (conChanged || mFirstCalculate) {
00422         if (conChanged) {
00423           ns->Set(eFlag_CONSTRAINT, constraint);
00424           ns->Set(eFlag_DISPATCH_VALID_CHANGED, PR_TRUE);
00425         }
00426         NS_ENSURE_TRUE(aChangedNodes->AppendObject(g->mContextNode),
00427                        NS_ERROR_FAILURE);
00428       }
00429 
00430       break;
00431 
00432     case eModel_readonly:
00433       if (g->HasExpr()) {
00434         rv = ComputeMIPWithInheritance(eFlag_READONLY,
00435                                        eFlag_DISPATCH_READONLY_CHANGED,
00436                                        eFlag_INHERITED_READONLY,
00437                                        g,
00438                                        aChangedNodes);
00439         NS_ENSURE_SUCCESS(rv, rv);
00440       }
00441       break;
00442       
00443     case eModel_relevant:
00444       if (g->HasExpr()) {
00445         rv = ComputeMIPWithInheritance(eFlag_RELEVANT,
00446                                        eFlag_DISPATCH_RELEVANT_CHANGED,
00447                                        eFlag_INHERITED_RELEVANT,
00448                                        g,
00449                                        aChangedNodes);
00450         NS_ENSURE_SUCCESS(rv, rv);
00451       }
00452       break;
00453       
00454     case eModel_required:
00455       if (g->HasExpr()) {
00456         PRBool didChange;
00457         rv = ComputeMIP(eFlag_REQUIRED,
00458                         eFlag_DISPATCH_REQUIRED_CHANGED,
00459                         g,
00460                         didChange);
00461         NS_ENSURE_SUCCESS(rv, rv);
00462       
00463         if (didChange) {
00464           NS_ENSURE_TRUE(aChangedNodes->AppendObject(g->mContextNode),
00465                          NS_ERROR_FAILURE);
00466         }
00467       }
00468       break;
00469       
00470     default:
00471       NS_ERROR("There was no expression which matched\n");
00472       res = PR_FALSE;
00473       break;
00474     }
00475 
00476     // Mark successors dirty
00477     nsXFormsMDGNode* sucnode;
00478     for (PRInt32 j = 0; j < g->mSuc.Count(); ++j) {
00479       sucnode = NS_STATIC_CAST(nsXFormsMDGNode*, g->mSuc[j]);
00480       if (!sucnode) {
00481         NS_ERROR("nsXFormsMDGEngine::Calculate(): Node has NULL successor!");
00482         return NS_ERROR_FAILURE;
00483       }
00484       sucnode->MarkDirty();
00485     }
00486 
00487     g->MarkClean();
00488   }
00489   nsXFormsUtils::MakeUniqueAndSort(aChangedNodes);
00490   
00491 #ifdef DEBUG_XF_MDG
00492   printf("\taChangedNodes: %d\n", aChangedNodes->Count());
00493   printf("\tmNodeToMDG:    %d\n", mNodeToMDG.Count());
00494   printf("\tmNodeStates:   %d\n", mNodeStates.Count());
00495   printf("\tGraph nodes:   %d\n", mGraph.Count());
00496 #endif
00497 
00498   return res;
00499 }
00500 
00501 nsresult
00502 nsXFormsMDGEngine::Revalidate(nsCOMArray<nsIDOMNode> *aNodes)
00503 {
00504   NS_ENSURE_ARG(aNodes);
00505   NS_ENSURE_STATE(mModel);
00506 
00507   for (PRInt32 i = 0; i < aNodes->Count(); ++i) {
00508     nsCOMPtr<nsIDOMNode> node = aNodes->ObjectAt(i);
00509     nsXFormsNodeState* ns = GetNCNodeState(node);
00510     PRBool constraint;
00511     mModel->ValidateNode(node, &constraint);
00512     if (constraint != ns->IsConstraintSchema()) {
00513       ns->Set(eFlag_CONSTRAINT_SCHEMA, constraint);
00514       ns->Set(eFlag_DISPATCH_VALID_CHANGED, PR_TRUE);
00515     }
00516   }
00517 
00518   return NS_OK;
00519 }
00520 
00521 nsresult
00522 nsXFormsMDGEngine::Rebuild()
00523 {
00524 #ifdef DEBUG_XF_MDG
00525   printf("nsXFormsMDGEngine::Rebuild()\n");
00526 #endif
00527   nsresult rv = NS_OK;
00528   mJustRebuilt = PR_TRUE;
00529   mFirstCalculate = PR_FALSE;
00530 
00531   mGraph.Clear();
00532   mNodeStates.Clear();
00533 
00534   nsDeque sortedNodes(nsnull);
00535 
00536 #ifdef DEBUG_XF_MDG
00537   printf("\tmNodesInGraph: %d\n", mNodesInGraph);
00538   printf("\tmNodeToMDG:    %d\n", mNodeToMDG.Count());
00539   printf("\tmNodeStates:   %d\n", mNodeStates.Count());
00540 #endif
00541 
00542   // Initial scan for nsXFormsMDGNodes with no dependencies (mCount == 0)
00543   PRUint32 entries = mNodeToMDG.EnumerateRead(AddStartNodes, &sortedNodes);
00544   if (entries != mNodeToMDG.Count()) {
00545     return NS_ERROR_OUT_OF_MEMORY;
00546   }
00547 
00548 #ifdef DEBUG_XF_MDG
00549   printf("\tStartNodes:    %d\n", sortedNodes.GetSize());
00550 #endif
00551   
00552   
00553   nsXFormsMDGNode* node;
00554   while ((node = NS_STATIC_CAST(nsXFormsMDGNode*, sortedNodes.Pop()))) {
00555     for (PRInt32 i = 0; i < node->mSuc.Count(); ++i) {
00556       nsXFormsMDGNode* sucNode = NS_STATIC_CAST(nsXFormsMDGNode*,
00557                                                 node->mSuc[i]);
00558       NS_ASSERTION(sucNode, "XForms: NULL successor node");
00559 
00560       sucNode->mCount--;
00561       if (sucNode->mCount == 0) {
00562         sortedNodes.Push(sucNode);
00563       }
00564     }
00565 
00566     node->MarkDirty();
00567 
00568     if (!mGraph.AppendElement(node)) {
00569       return NS_ERROR_OUT_OF_MEMORY;
00570     }
00571   }
00572 
00573 #ifdef DEBUG_XF_MDG
00574     printf("\tmGraph.Count() = %2d\n", mGraph.Count());
00575     printf("\tmNodesInGraph  = %2d\n", mNodesInGraph);
00576 #endif
00577 
00578   if (mGraph.Count() != mNodesInGraph) {
00579     nsCOMPtr<nsIDOMElement> modelElement;
00580     if (mModel) {
00581       modelElement = mModel->GetDOMElement();
00582     }
00583     nsXFormsUtils::ReportError(NS_LITERAL_STRING("MDGLoopError"), modelElement);
00584     rv = NS_ERROR_ABORT;
00585   }
00586 
00587   mNodesInGraph = 0;
00588 
00589   return rv;
00590 }
00591 
00592 nsresult
00593 nsXFormsMDGEngine::ClearDispatchFlags()
00594 {
00595   mJustRebuilt = PR_FALSE;
00596   return AndFlags(kFlags_NOT_DISPATCH) ? NS_OK : NS_ERROR_FAILURE;
00597 }
00598 
00599 nsresult
00600 nsXFormsMDGEngine::Clear() {
00601 #ifdef DEBUG_XF_MDG
00602   printf("nsXFormsMDGEngine::Clear()\n");
00603 #endif
00604 
00605   nsresult rv = mNodeToMDG.Enumerate(DeleteLinkedNodes, nsnull);
00606   NS_ENSURE_SUCCESS(rv, rv);
00607 
00608   mNodeToMDG.Clear();
00609 
00610   mNodeStates.Clear();
00611   
00612   mGraph.Clear();
00613   
00614   mNodesInGraph = 0;
00615 
00616   return NS_OK;
00617 }
00618 
00619 nsresult
00620 nsXFormsMDGEngine::SetNodeValue(nsIDOMNode       *aContextNode,
00621                                 const nsAString  &aNodeValue,
00622                                 PRBool           *aNodeChanged)
00623 {
00624   return SetNodeValueInternal(aContextNode,
00625                               aNodeValue,
00626                               PR_TRUE,
00627                               PR_FALSE,
00628                               aNodeChanged);
00629 }
00630 
00631 nsresult
00632 nsXFormsMDGEngine::SetNodeValueInternal(nsIDOMNode       *aContextNode,
00633                                         const nsAString  &aNodeValue,
00634                                         PRBool            aMarkNode,
00635                                         PRBool            aIsCalculate,
00636                                         PRBool           *aNodeChanged)
00637 {
00638   if (aNodeChanged) {
00639     *aNodeChanged = PR_FALSE;
00640   }
00641 
00642   const nsXFormsNodeState* ns = GetNodeState(aContextNode);
00643   NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE);
00644 
00645   // If the node is read-only and not set by a @calculate MIP,
00646   // ignore the call
00647   if (ns->IsReadonly() && !aIsCalculate) {
00650     return NS_OK;
00651   }
00652 
00653   nsCOMPtr<nsIDOMNode> childNode;
00654   PRUint16 nodeType;
00655   nsresult rv = aContextNode->GetNodeType(&nodeType);
00656   NS_ENSURE_SUCCESS(rv, rv);
00657 
00658   nsAutoString oldValue;
00659   nsXFormsUtils::GetNodeValue(aContextNode, oldValue);
00660   if (oldValue.Equals(aNodeValue)) {
00661     return NS_OK;
00662   }
00663 
00664   switch(nodeType) {
00665   case nsIDOMNode::ATTRIBUTE_NODE:
00666   case nsIDOMNode::TEXT_NODE:
00667   case nsIDOMNode::CDATA_SECTION_NODE:
00668   case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
00669   case nsIDOMNode::COMMENT_NODE:
00670     rv = aContextNode->SetNodeValue(aNodeValue);
00671     NS_ENSURE_SUCCESS(rv, rv);
00672 
00673     break;
00674 
00675   case nsIDOMNode::ELEMENT_NODE:
00676 
00677     rv = aContextNode->GetFirstChild(getter_AddRefs(childNode));
00678     NS_ENSURE_SUCCESS(rv, rv);
00679 
00680     if (!childNode) {
00681       rv = CreateNewChild(aContextNode, aNodeValue);
00682       NS_ENSURE_SUCCESS(rv, rv);
00683     } else {
00684       PRUint16 childType;
00685       rv = childNode->GetNodeType(&childType);
00686       NS_ENSURE_SUCCESS(rv, rv);
00687 
00688       if (childType == nsIDOMNode::TEXT_NODE ||
00689           childType == nsIDOMNode::CDATA_SECTION_NODE) {
00690         rv = childNode->SetNodeValue(aNodeValue);
00691         NS_ENSURE_SUCCESS(rv, rv);
00692 
00693         // Remove all leading text child nodes except first one (see
00694         // nsXFormsUtils::GetNodeValue method for motivation).
00695         nsCOMPtr<nsIDOMNode> siblingNode;
00696         while (true) {
00697           rv = childNode->GetNextSibling(getter_AddRefs(siblingNode));
00698           NS_ENSURE_SUCCESS(rv, rv);
00699           if (!siblingNode)
00700             break;
00701 
00702           rv = siblingNode->GetNodeType(&childType);
00703           NS_ENSURE_SUCCESS(rv, rv);
00704           if (childType != nsIDOMNode::TEXT_NODE &&
00705               childType != nsIDOMNode::CDATA_SECTION_NODE) {
00706             break;
00707           }
00708           nsCOMPtr<nsIDOMNode> stubNode;
00709           rv = aContextNode->RemoveChild(siblingNode,
00710                                          getter_AddRefs(stubNode));
00711           NS_ENSURE_SUCCESS(rv, rv);
00712         }
00713       } else {
00714         // Not a text child, create a new one
00715         rv = CreateNewChild(aContextNode, aNodeValue, childNode);
00716         NS_ENSURE_SUCCESS(rv, rv);
00717       }
00718     }
00719 
00720     break;
00721 
00722   default:
00725     return NS_ERROR_ILLEGAL_VALUE;
00726     break;
00727   }
00728   
00729   // NB: Never reached for Readonly nodes.  
00730   if (aNodeChanged) {
00731     *aNodeChanged = PR_TRUE;
00732   }
00733   if (aMarkNode) {
00734       MarkNodeAsChanged(aContextNode);
00735   }
00736   
00737   return NS_OK;
00738 }
00739 
00740 nsresult
00741 nsXFormsMDGEngine::SetNodeContent(nsIDOMNode       *aContextNode,
00742                                   nsIDOMNode       *aContentEnvelope)
00743 {
00744   NS_ENSURE_ARG(aContextNode);
00745   NS_ENSURE_ARG(aContentEnvelope);
00746 
00747   // ok, this is tricky.  This function will REPLACE the contents of
00748   // aContextNode with the a clone of the contents of aContentEnvelope.  If
00749   // aContentEnvelope has no contents, then any contents that aContextNode
00750   // has will still be removed.  
00751 
00752   const nsXFormsNodeState* ns = GetNodeState(aContextNode);
00753   NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE);
00754 
00755   // If the node is read-only and not set by a @calculate MIP,
00756   // ignore the call
00757   if (ns->IsReadonly()) {
00760     return NS_OK;
00761   }
00762 
00763   PRUint16 nodeType;
00764   nsresult rv = aContextNode->GetNodeType(&nodeType);
00765   NS_ENSURE_SUCCESS(rv, rv);
00766 
00767   if (nodeType != nsIDOMNode::ELEMENT_NODE) {
00768     // got to return something pretty unique that we can check down the road in
00769     // order to dispatch any error events
00770     return NS_ERROR_DOM_WRONG_TYPE_ERR;
00771   }
00772 
00773   // Need to determine if the contents of the context node and content envelope
00774   // are already the same.  If so, we can avoid some unnecessary work.
00775 
00776   PRBool hasChildren1, hasChildren2, contentsEqual = PR_FALSE;
00777   nsresult rv1 = aContextNode->HasChildNodes(&hasChildren1);
00778   nsresult rv2 = aContentEnvelope->HasChildNodes(&hasChildren2);
00779   if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2) && hasChildren1 == hasChildren2) {
00780     // First test passed.  Both have the same number of children nodes.
00781     if (hasChildren1) {
00782       nsCOMPtr<nsIDOMNodeList> children1, children2;
00783       PRUint32 childrenLength1, childrenLength2;
00784   
00785       rv1 = aContextNode->GetChildNodes(getter_AddRefs(children1));
00786       rv2 = aContentEnvelope->GetChildNodes(getter_AddRefs(children2));
00787 
00788       if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2) && children1 && children2) {
00789 
00790         // Both have child nodes.
00791         rv1 = children1->GetLength(&childrenLength1);
00792         rv2 = children2->GetLength(&childrenLength2);
00793         if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2) && 
00794             (childrenLength1 == childrenLength2)) {
00795 
00796           // both have the same number of child nodes.  Now checking to see if
00797           // each of the children are equal.
00798           for (PRUint32 i = 0; i < childrenLength1; ++i) {
00799             nsCOMPtr<nsIDOMNode> child1, child2;
00800       
00801             rv1 = children1->Item(i, getter_AddRefs(child1));
00802             rv2 = children2->Item(i, getter_AddRefs(child2));
00803             if (NS_FAILED(rv1) || NS_FAILED(rv2)) {
00804               // Unexpected error.  Not as many children in the list as we
00805               // were told.
00806               return NS_ERROR_UNEXPECTED;
00807             }
00808       
00809             contentsEqual = nsXFormsUtils::AreNodesEqual(child1, child2, PR_TRUE);
00810             if (!contentsEqual) {
00811               break;
00812             }
00813           }
00814         }
00815       }
00816     } else {
00817       // neither have children
00818       contentsEqual = PR_TRUE;
00819     }
00820   }
00821 
00822   if (contentsEqual) {
00823     return NS_OK;
00824   }
00825 
00826   // remove any child nodes that aContextNode already contains
00827   nsCOMPtr<nsIDOMNode> resultNode;
00828   nsCOMPtr<nsIDOMNodeList> childList;
00829   rv = aContextNode->GetChildNodes(getter_AddRefs(childList));
00830   NS_ENSURE_SUCCESS(rv, rv);
00831   if (childList) {
00832     PRUint32 length;
00833     rv = childList->GetLength(&length);
00834     NS_ENSURE_SUCCESS(rv, rv);
00835 
00836     for (PRInt32 i = length-1; i >= 0; i--) {
00837       nsCOMPtr<nsIDOMNode> childNode;
00838       rv = childList->Item(i, getter_AddRefs(childNode));
00839       NS_ENSURE_SUCCESS(rv, rv);
00840 
00841       rv = aContextNode->RemoveChild(childNode, getter_AddRefs(resultNode));
00842       NS_ENSURE_SUCCESS(rv, rv);
00843     }
00844   }
00845 
00846   // add contents of the envelope under aContextNode
00847   nsCOMPtr<nsIDOMNode> childNode;
00848   rv = aContentEnvelope->GetFirstChild(getter_AddRefs(childNode));
00849   NS_ENSURE_SUCCESS(rv, rv);
00850 
00851   nsCOMPtr<nsIDOMDocument> document;
00852   rv = aContextNode->GetOwnerDocument(getter_AddRefs(document));
00853   NS_ENSURE_STATE(document);
00854 
00855   while (childNode) {
00856     nsCOMPtr<nsIDOMNode> importedNode;
00857     rv = document->ImportNode(childNode, PR_TRUE, getter_AddRefs(importedNode));
00858     NS_ENSURE_STATE(importedNode);
00859     rv = aContextNode->AppendChild(importedNode, getter_AddRefs(resultNode));
00860     NS_ENSURE_SUCCESS(rv, rv);
00861 
00862     rv = childNode->GetNextSibling(getter_AddRefs(resultNode));
00863     NS_ENSURE_SUCCESS(rv, rv);
00864 
00865     resultNode.swap(childNode);
00866   }
00867 
00868   // We already know that the contents have changed.  Mark the node so that
00869   // a xforms-value-changed can be dispatched.
00870   MarkNodeAsChanged(aContextNode);
00871 
00872   return NS_OK;
00873 }
00874 
00875 const nsXFormsNodeState*
00876 nsXFormsMDGEngine::GetNodeState(nsIDOMNode *aContextNode)
00877 {
00878   return GetNCNodeState(aContextNode);
00879 }
00880 
00881 /**********************************************/
00882 /*              Private functions             */
00883 /**********************************************/
00884 nsXFormsNodeState*
00885 nsXFormsMDGEngine::GetNCNodeState(nsIDOMNode *aContextNode)
00886 {
00887   nsXFormsNodeState* ns = nsnull;
00888 
00889   if (aContextNode && !mNodeStates.Get(aContextNode, &ns)) {
00890     ns = new nsXFormsNodeState(kFlags_DEFAULT |
00891                                ((mJustRebuilt && mFirstCalculate) ? kFlags_INITIAL_DISPATCH : 0));
00892     NS_ASSERTION(ns, "Could not create new nsXFormsNodeState");
00893 
00894     if (!mNodeStates.Put(aContextNode, ns)) {
00895       NS_ERROR("Could not insert new nsXFormsNodeState");
00896       delete ns;
00897       return nsnull;
00898     }    
00899     aContextNode->AddRef();
00900 
00901     // Do an initial type check, and set the validity state
00902     PRBool constraint;
00903     mModel->ValidateNode(aContextNode, &constraint);
00904     ns->Set(eFlag_CONSTRAINT_SCHEMA, constraint);
00905   }
00906   return ns;
00907 }
00908 
00909 nsresult
00910 nsXFormsMDGEngine::CreateNewChild(nsIDOMNode      *aContextNode,
00911                                   const nsAString &aNodeValue,
00912                                   nsIDOMNode      *aBeforeNode)
00913 {
00914   nsresult rv;
00915 
00916   nsCOMPtr<nsIDOMDocument> document;
00917   rv = aContextNode->GetOwnerDocument(getter_AddRefs(document));
00918   NS_ENSURE_SUCCESS(rv, rv);
00919 
00920   nsCOMPtr<nsIDOMText> textNode;
00921   rv = document->CreateTextNode(aNodeValue, getter_AddRefs(textNode));
00922   NS_ENSURE_SUCCESS(rv, rv);
00923   
00924   nsCOMPtr<nsIDOMNode> newNode;
00925   if (aBeforeNode) {
00926     rv = aContextNode->InsertBefore(textNode,
00927                                     aBeforeNode,
00928                                     getter_AddRefs(newNode));
00929   } else {
00930     rv = aContextNode->AppendChild(textNode,
00931                                    getter_AddRefs(newNode));
00932   }
00933 
00934   return rv;
00935 }
00936 
00937 PLDHashOperator
00938 nsXFormsMDGEngine::DeleteLinkedNodes(nsISupports                *aKey,
00939                                      nsAutoPtr<nsXFormsMDGNode> &aNode,
00940                                      void                       *aArg)
00941 {
00942   if (!aNode) {
00943     NS_WARNING("nsXFormsMDGEngine::DeleteLinkedNodes() called with aNode == nsnull!");
00944     return PL_DHASH_STOP;
00945   }
00946   nsXFormsMDGNode* temp;
00947   nsXFormsMDGNode* next = aNode->mNext;
00948   
00949   while (next) {
00950     temp = next;  
00951     next = next->mNext;
00952     delete temp;
00953   }
00954 
00955   return PL_DHASH_NEXT;
00956 }
00957 
00958 nsXFormsMDGNode*
00959 nsXFormsMDGEngine::GetNode(nsIDOMNode       *aDomNode,
00960                            ModelItemPropName aType,
00961                            PRBool            aCreate)
00962 {
00963   nsIDOMNode* nodeKey = aDomNode;
00964   nsXFormsMDGNode* nd = nsnull;
00965 
00966 #ifdef DEBUG_XF_MDG
00967   printf("nsXFormsMDGEngine::GetNode(aDomNode=%p, aType=%s, aCreate=%d)\n",
00968          (void*) nodeKey, gMIPNames[aType], aCreate);
00969 #endif
00970   
00971 
00972   // Find correct type
00973   if (mNodeToMDG.Get(nodeKey, &nd) && aType != eModel_type) {
00974     while (nd && aType != nd->mType) {
00975       nd = nd->mNext;
00976     }
00977   } 
00978   
00979   // Eventually create node
00980   if (!nd && aCreate){
00981     nd = new nsXFormsMDGNode(nodeKey, aType);
00982     if (!nd) {
00983       NS_ERROR("Could not allocate room for new nsXFormsMDGNode");
00984       return nsnull;
00985     }
00986     
00987 #ifdef DEBUG_XF_MDG
00988     printf("\tNode not found, create new MDGNode '%p'\n", (void*) nd);
00989 #endif
00990     // Link to existing node
00991     nsXFormsMDGNode* nd_exists;
00992     if (mNodeToMDG.Get(nodeKey, &nd_exists)) {
00993       while (nd_exists->mNext) {
00994         nd_exists = nd_exists->mNext;
00995       }
00996 #ifdef DEBUG_XF_MDG
00997       printf("\tLinking to existing MDGNode '%p'\n", (void*) nd_exists);
00998 #endif
00999       nd_exists->mNext = nd;
01000     } else {
01001       if (!mNodeToMDG.Put(nodeKey, nd)) {
01002         delete nd;
01003         NS_ERROR("Could not insert new node in HashTable!");
01004         return nsnull;
01005       }
01006       nodeKey->AddRef();
01007     }
01008 
01009     mNodesInGraph++;
01010   }
01011   return nd;
01012 }
01013 
01014 PLDHashOperator 
01015 nsXFormsMDGEngine::AddStartNodes(nsISupports     *aKey,
01016                                  nsXFormsMDGNode *aNode,
01017                                  void            *aDeque)
01018 {
01019 #ifdef DEBUG_XF_MDG
01020   printf("nsXFormsMDGEngine::AddStartNodes(aKey=n/a, aNode=%p, aDeque=%p)\n",
01021          (void*) aNode,
01022          aDeque);
01023 #endif
01024 
01025   nsDeque* deque = NS_STATIC_CAST(nsDeque*, aDeque);
01026   if (!deque) {
01027     NS_ERROR("nsXFormsMDGEngine::AddStartNodes called with NULL aDeque");
01028     return PL_DHASH_STOP;
01029   }
01030   
01031   while (aNode) {
01032     if (aNode->mCount == 0) {
01035       deque->Push(aNode);
01036     }
01037     aNode = aNode->mNext;
01038   }
01039     
01040   return PL_DHASH_NEXT;
01041 }
01042 
01043 PLDHashOperator 
01044 nsXFormsMDGEngine::AndFlag(nsISupports                  *aKey,
01045                            nsAutoPtr<nsXFormsNodeState> &aState,
01046                            void                         *aMask)
01047 {
01048   PRUint16* andMask = NS_STATIC_CAST(PRUint16*, aMask);
01049   if (!andMask) {
01050     return PL_DHASH_STOP;
01051   }
01052   *aState &= *andMask;
01053   return PL_DHASH_NEXT;
01054 }
01055 
01056 PRBool
01057 nsXFormsMDGEngine::AndFlags(PRUint16 aAndMask)
01058 {
01059   PRUint32 entries = mNodeStates.Enumerate(AndFlag, &aAndMask);
01060   return (entries == mNodeStates.Count()) ? PR_TRUE : PR_FALSE;
01061 }
01062 
01063 
01064 nsresult
01065 nsXFormsMDGEngine::BooleanExpression(nsXFormsMDGNode* aNode, PRBool& state)
01066 {
01067   NS_ENSURE_ARG_POINTER(aNode);
01068   NS_ENSURE_TRUE(aNode->mExpression, NS_ERROR_FAILURE);
01069   
01070   nsISupports* retval;
01071   nsresult rv;
01072 
01073   rv = aNode->mExpression->EvaluateWithContext(aNode->mContextNode,
01074                                                aNode->mContextPosition,
01075                                                aNode->mContextSize,
01076                                                nsIDOMXPathResult::BOOLEAN_TYPE,
01077                                                nsnull,
01078                                                &retval);
01079   NS_ENSURE_SUCCESS(rv, rv);
01080 
01081   nsCOMPtr<nsIDOMXPathResult> xpath_res = do_QueryInterface(retval);
01082   NS_ENSURE_TRUE(xpath_res, NS_ERROR_FAILURE);
01083   
01084   rv = xpath_res->GetBooleanValue(&state);
01085   
01086   return rv;
01087 }
01088 
01089 nsresult
01090 nsXFormsMDGEngine::ComputeMIP(eFlag_t          aStateFlag,
01091                               eFlag_t          aDispatchFlag,
01092                               nsXFormsMDGNode *aNode,
01093                               PRBool          &aDidChange)
01094 {
01095   NS_ENSURE_ARG(aNode);
01096 
01097   aDidChange = PR_FALSE;
01098 
01099   if (!aNode->mExpression)
01100     return NS_OK;
01101 
01102   nsXFormsNodeState* ns = GetNCNodeState(aNode->mContextNode);
01103   NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE);
01104   
01105   PRBool state;
01106   nsresult rv = BooleanExpression(aNode, state);
01107   NS_ENSURE_SUCCESS(rv, rv);
01108   
01109   PRBool cstate = ns->Test(aStateFlag);  
01110   
01111   ns->Set(aStateFlag, state);
01112 
01113   aDidChange = (state != cstate) ? PR_TRUE : PR_FALSE;
01114   if (aDidChange) {
01115     ns->Set(aDispatchFlag, PR_TRUE);
01116   }
01117   
01118   return NS_OK;
01119 }
01120 
01121 nsresult
01122 nsXFormsMDGEngine::ComputeMIPWithInheritance(eFlag_t                aStateFlag,
01123                                              eFlag_t                aDispatchFlag,
01124                                              eFlag_t                aInheritanceFlag,
01125                                              nsXFormsMDGNode        *aNode,
01126                                              nsCOMArray<nsIDOMNode> *aSet)
01127 {
01128   nsresult rv;
01129   PRBool didChange;
01130   rv = ComputeMIP(aStateFlag, aDispatchFlag, aNode, didChange);
01131   NS_ENSURE_SUCCESS(rv, rv);
01132   
01133   if (didChange) {
01134     nsXFormsNodeState* ns = GetNCNodeState(aNode->mContextNode);
01135     NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE);
01136     if (   !(aStateFlag == eFlag_READONLY && ns->Test(aInheritanceFlag))
01137         ||  (aStateFlag == eFlag_RELEVANT && ns->Test(aInheritanceFlag)) )
01138     {
01139       NS_ENSURE_TRUE(aSet->AppendObject(aNode->mContextNode),
01140                      NS_ERROR_FAILURE);
01141       rv = AttachInheritance(aSet,
01142                              aNode->mContextNode,
01143                              ns->Test(aStateFlag),
01144                              aStateFlag);
01145     }
01146   }
01147 
01148   return rv;
01149 }
01150 
01151 nsresult
01152 nsXFormsMDGEngine::AttachInheritance(nsCOMArray<nsIDOMNode> *aSet,
01153                                      nsIDOMNode             *aSrc,
01154                                      PRBool                  aState,
01155                                      eFlag_t                 aStateFlag)
01156 {
01157   NS_ENSURE_ARG(aSrc);
01158   
01159   nsCOMPtr<nsIDOMNode> node;
01160   nsresult rv;
01161   PRBool updateNode = PR_FALSE;
01162 
01163   nsCOMPtr<nsIDOMNodeList> childList;
01164   rv = aSrc->GetChildNodes(getter_AddRefs(childList));
01165   NS_ENSURE_SUCCESS(rv, rv);
01166 
01167   PRUint32 childCount;
01168   rv = childList->GetLength(&childCount);
01169   NS_ENSURE_SUCCESS(rv, rv);
01170 
01171   for (PRUint32 i = 0; i < childCount; i++) {
01172     rv = childList->Item(i, getter_AddRefs(node));
01173     NS_ENSURE_SUCCESS(rv, rv);
01174     NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
01175 
01176     nsXFormsNodeState *ns = GetNCNodeState(node);
01177     NS_ENSURE_TRUE(ns, NS_ERROR_FAILURE);
01178 
01179     PRBool curState = ns->Test(aStateFlag);    
01180 
01181     if (aStateFlag == eFlag_RELEVANT) {
01182       if (!aState) { // The nodes are getting irrelevant
01183         if (ns->Test(eFlag_INHERITED_RELEVANT) && curState) {
01184           ns->Set(eFlag_INHERITED_RELEVANT, PR_FALSE);
01185           ns->Set(eFlag_DISPATCH_RELEVANT_CHANGED, PR_TRUE);
01186           updateNode = PR_TRUE;
01187         }
01188       } else { // The nodes are becoming relevant
01189         if (curState) {
01190           // Relevant has changed from inheritance
01191           ns->Set(eFlag_DISPATCH_RELEVANT_CHANGED, PR_TRUE);
01192           ns->Set(eFlag_INHERITED_RELEVANT, PR_TRUE);
01193           updateNode = PR_TRUE;
01194         }
01195       }
01196     } else if (aStateFlag == eFlag_READONLY) {
01197       if (aState) { // The nodes are getting readonly
01198         if (!ns->Test(eFlag_INHERITED_READONLY) && curState == PR_FALSE) {
01199           ns->Set(eFlag_INHERITED_READONLY | eFlag_DISPATCH_READONLY_CHANGED,
01200                   PR_TRUE);
01201           updateNode = PR_TRUE;
01202         }
01203       } else { // The nodes are getting readwrite
01204         if (curState) {
01205           ns->Set(eFlag_DISPATCH_READONLY_CHANGED, PR_TRUE);
01206           ns->Set(eFlag_INHERITED_READONLY, PR_FALSE);
01207           updateNode = PR_TRUE;
01208         }
01209       }
01210     }
01211     
01212     if (updateNode) {
01213       rv = AttachInheritance(aSet, node, aState, aStateFlag);
01214       NS_ENSURE_SUCCESS(rv, rv);
01215       NS_ENSURE_TRUE(aSet->AppendObject(node), NS_ERROR_FAILURE);
01216       updateNode = PR_FALSE;
01217     }
01218   }
01219 
01220   return NS_OK;
01221 }
01222 
01223 nsresult
01224 nsXFormsMDGEngine::Invalidate()
01225 {
01226   nsXFormsMDGNode* g;
01227   for (PRInt32 i = 0; i < mGraph.Count(); ++i) {
01228     g = NS_STATIC_CAST(nsXFormsMDGNode*, mGraph[i]);
01229     NS_ENSURE_TRUE(g, NS_ERROR_FAILURE);
01230     g->MarkDirty();
01231   }
01232   return NS_OK;
01233 }
01234