Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsInsertDeleteElement.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 XForms code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Novell, Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2005
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Allan Beaufour <abeaufour@novell.com>
00024  *  Merle Sterling <msterlin@us.ibm.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or 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 "nsIDOMAttr.h"
00041 #include "nsIDOMEvent.h"
00042 #include "nsIDOMNode.h"
00043 #include "nsIDOMElement.h"
00044 #include "nsIDOMDocument.h"
00045 #include "nsIDOMNodeList.h"
00046 #include "nsIDOMNamedNodeMap.h"
00047 #include "nsIXFormsRepeatElement.h"
00048 #include "nsIXFormsControl.h"
00049 #include "nsIXFormsContextInfo.h"
00050 
00051 #include "nsString.h"
00052 
00053 #include "nsIInstanceElementPrivate.h"
00054 #include "nsXFormsActionModuleBase.h"
00055 #include "nsXFormsActionElement.h"
00056 #include "nsXFormsUtils.h"
00057 #include "nsIDOM3Node.h"
00058 
00059 #include "math.h"
00060 
00061 #ifdef DEBUG
00062 //#define DEBUG_XF_INSERTDELETE
00063 #endif
00064 
00068 class nsXFormsInsertDeleteElement : public nsXFormsActionModuleBase
00069 {
00070 private:
00071   PRBool mIsInsert;
00072 
00073   enum Location {
00074     eLocation_After,
00075     eLocation_Before,
00076     eLocation_FirstChild
00077   };
00078 
00079   // Context Info for events.
00080   nsCOMArray<nsIXFormsContextInfo> mContextInfo;
00081 
00089   nsresult GetFirstNodeOfType(nsCOMArray<nsIDOMNode> *aNodes,
00090                               PRUint16 aNodeType,
00091                               nsIDOMNode **aResult);
00092 
00101   nsresult InsertNode(nsIDOMNode *aTargetNode, nsIDOMNode *aNewNode,
00102                       Location aLocation, nsIDOMNode **aResNode);
00103 
00104   nsresult RefreshRepeats(nsCOMArray<nsIDOMNode> *aNodes);
00105 
00106 public:
00107 
00109   nsXFormsInsertDeleteElement(PRBool aIsInsert) :
00110     mIsInsert(aIsInsert)
00111     {}
00112 
00113   NS_IMETHOD
00114   HandleAction(nsIDOMEvent* aEvent, nsIXFormsActionElement *aParentAction);
00115 
00123   nsresult HandleSingleAction(nsIDOMEvent* aEvent,
00124                               nsIXFormsActionElement *aParentAction)
00125   {
00126     return NS_OK;
00127   }
00128 };
00129 
00130 NS_IMETHODIMP
00131 nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent            *aEvent,
00132                                           nsIXFormsActionElement *aParentAction)
00133 {
00134   // Keep element alive while iterating.
00135   nsCOMPtr<nsIDOMElement> element = mElement;
00136   if (!element)
00137     return NS_OK;
00138 
00139   mCurrentEvent = aEvent;
00140 
00141   nsresult rv;
00142 
00143   // Set the maximum run time for the loop (in microseconds).
00144   PRTime microseconds = nsXFormsUtils::waitLimit * PR_USEC_PER_SEC;
00145   PRTime runTime = 0, start = PR_Now();
00146   while (PR_TRUE) {
00147     //
00148     // Step 1 (Insert or Delete): Determine the insert/delete context.
00149     //
00150     // If the bind attribute is present or if the context attribute is not given,
00151     // the insert context is the in-scope evaluation context. Otherwise, the
00152     // XPath expression provided by the context attribute is evaluated using the
00153     // in-scope evaluation context, and the first node rule is applied to obtain
00154     // the insert context.
00155     //
00156     nsCOMPtr<nsIDOMXPathResult> contextNodeset;
00157     nsCOMPtr<nsIDOMNode> contextNode;
00158     PRUint32 contextNodesetSize = 0;
00159     PRInt32 contextPosition;
00160 
00161     // Get the in-scope evaluation context. The last parameter to GetNodeContext
00162     // indicates whether it should try to get the context using @bind. We want to
00163     // ignore @bind because it should not change the in-scope evaluation context
00164     // for insert.
00165     nsCOMPtr<nsIModelElementPrivate> model;
00166     nsCOMPtr<nsIDOMElement> bindElement;
00167     nsCOMPtr<nsIXFormsControl> parentControl;
00168     PRBool outerBind;
00169     rv = nsXFormsUtils::GetNodeContext(element,
00170                                        nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
00171                                        getter_AddRefs(model),
00172                                        getter_AddRefs(bindElement),
00173                                        &outerBind,
00174                                        getter_AddRefs(parentControl),
00175                                        getter_AddRefs(contextNode),
00176                                        &contextPosition,
00177                                        (PRInt32*)&contextNodesetSize,
00178                                        PR_FALSE);
00179 
00180     NS_ENSURE_SUCCESS(rv, rv);
00181 
00182     // Determine if the context node is specified via @context.
00183     // If @bind is present, @context is ignored.
00184     nsAutoString bindExpr;
00185     nsAutoString contextExpr;
00186     element->GetAttribute(NS_LITERAL_STRING("bind"), bindExpr);
00187     element->GetAttribute(NS_LITERAL_STRING("context"), contextExpr);
00188 
00189     if (bindExpr.IsEmpty() && !contextExpr.IsEmpty()) {
00190       // @context is specified and overrides the in-scope evaluation context.
00191       // The insert context is the result of evaluating @context in the current
00192       // in-scope evaluation context.
00193       rv = nsXFormsUtils::EvaluateXPath(contextExpr, contextNode, element,
00194                                         nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
00195                                         getter_AddRefs(contextNodeset));
00196       NS_ENSURE_SUCCESS(rv, rv);
00197 
00198       // The insert/delete action is terminated with no effect if the context
00199       // is the empty node-set.
00200       if (contextNodeset) {
00201         rv = contextNodeset->GetSnapshotLength(&contextNodesetSize);
00202         NS_ENSURE_SUCCESS(rv, rv);
00203     
00204         if (contextNodesetSize < 1)
00205           return NS_OK;
00206     
00207         // Context node is the first node in the nodeset.
00208         contextNodeset->SnapshotItem(0, getter_AddRefs(contextNode));
00209         contextPosition = 1;
00210       }
00211     }
00212 
00213     // The insert/delete action is terminated with no effect if the context
00214     // is the empty node-set.
00215     if (!contextNode)
00216       return NS_OK;
00217 
00218     // The insert action is terminated with no effect if the context attribute
00219     // is given and the insert context does not evaluate to an element node.
00220     if (mIsInsert && !contextExpr.IsEmpty()) {
00221       PRUint16 nodeType;
00222       contextNode->GetNodeType(&nodeType);
00223       if (nodeType != nsIDOMNode::ELEMENT_NODE)
00224         return NS_OK;
00225     }
00226 
00227     // Test the `if` and `while` attributes to determine whether this action
00228     // can be performed and should be repeated.  As the insert and delete
00229     // actions can change their context, we do this testing here instead of
00230     // relying on the default logic from our parent class' `HandleAction`
00231     // (or `DoHandleAction`) definition.
00232     PRBool usesWhile;
00233     if (!nsXFormsActionModuleBase::CanPerformAction(element, &usesWhile,
00234                                                     contextNode,
00235                                                     contextNodesetSize,
00236                                                     contextPosition)) {
00237       return NS_OK;
00238     }
00239   
00240     //
00241     // Step 2 (Insert or Delete): Determine the node-set binding.
00242     //
00243     // If the bind attribute is present, it directly determines the Node Set
00244     // Binding node-set. If a nodeset attribute is present, it is evaluated
00245     // within the context to determine the Node Set Binding node-set.
00246     //
00247     // A NodeSet binding attribute (@bind or @nodeset) is required unless
00248     // the context attribute is present.
00249     //
00250     nsCOMPtr<nsIDOMXPathResult> nodeset;
00251     PRUint32 nodesetSize = 0;
00252     nsAutoString nodesetExpr;
00253 
00254     if (bindExpr.IsEmpty()) {
00255       element->GetAttribute(NS_LITERAL_STRING("nodeset"), nodesetExpr);
00256       if (!nodesetExpr.IsEmpty()) {
00257         // Evaluate the nodeset attribute within the insert context.
00258         rv = nsXFormsUtils::EvaluateXPath(nodesetExpr, contextNode, element,
00259                                           nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
00260                                           getter_AddRefs(nodeset));
00261         NS_ENSURE_SUCCESS(rv, rv);
00262       }
00263     } else {
00264       PRBool usesModelBinding;
00265       rv = nsXFormsUtils::EvaluateNodeBinding(element,
00266                                               nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
00267                                               EmptyString(),
00268                                               EmptyString(),
00269                                               nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
00270                                               getter_AddRefs(model),
00271                                               getter_AddRefs(nodeset),
00272                                               &usesModelBinding);
00273       NS_ENSURE_SUCCESS(rv, rv);
00274 
00275       if (!model)
00276         return NS_OK;
00277     }
00278 
00279     if (nodeset) {
00280       rv = nodeset->GetSnapshotLength(&nodesetSize);
00281       NS_ENSURE_SUCCESS(rv, rv);
00282     }
00283 
00284     // The insert action is terminated with no effect if the context attribute
00285     // is not given and the Node Set Binding node-set is the empty node-set.
00286     //
00287     // The delete action is terminated with no effect if the Node Set Binding
00288     // node-set is the empty node-set.
00289     if (!nodeset || nodesetSize < 1) {
00290       if (!mIsInsert || (mIsInsert && contextExpr.IsEmpty()))
00291         return NS_OK;
00292     }
00293 
00294     //
00295     // Step 3 (Insert): Determine the origin node-set.
00296     //
00297     nsCOMPtr<nsIDOMXPathResult> originNodeset;
00298     nsCOMPtr<nsIDOMXPathResult> originNode;
00299     PRUint32 originNodesetSize = 0;
00300 
00301     if (mIsInsert) {
00302       // If the origin attribute is not given and the Node Set Binding node-set
00303       // is empty, then the origin node-set is the empty node-set. Otherwise,
00304       // if the origin attribute is not given, then the origin node-set consists
00305       // of the last node of the Node Set Binding node-set (which we will obtain
00306       // just before performing the insert).
00307       //
00308       // If the origin attribute is given, the origin node-set is the result of
00309       // the evaluation of the origin attribute in the insert context.
00310       nsAutoString origin;
00311       element->GetAttribute(NS_LITERAL_STRING("origin"), origin);
00312 
00313       if (!origin.IsEmpty()) {
00314         rv = nsXFormsUtils::EvaluateXPath(origin, contextNode, element,
00315                                           nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
00316                                           getter_AddRefs(originNodeset));
00317         NS_ENSURE_SUCCESS(rv, rv);
00318 
00319         // The insert action is terminated with no effect if the origin node-set
00320         // is the empty node-set.
00321         if (!originNodeset)
00322           return NS_OK;
00323 
00324         rv = originNodeset->GetSnapshotLength(&originNodesetSize);
00325         NS_ENSURE_SUCCESS(rv, rv);
00326 
00327         // The insert action is terminated with no effect if the origin node-set
00328         // is the empty node-set.
00329         if (originNodesetSize < 1)
00330           return NS_OK;
00331 
00332         // Context Info: 'origin-nodes'
00333         nsCOMPtr<nsXFormsContextInfo> contextInfo =
00334           new nsXFormsContextInfo(element);
00335         NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00336         contextInfo->SetNodesetValue("origin-nodes", originNodeset);
00337         mContextInfo.AppendObject(contextInfo);
00338       }
00339     }
00340 
00341     //
00342     // Step 4 (Insert), Step 3 (Delete):
00343     // Determine the insert/delete location node.
00344     //
00345     // Insert: If the Node Set Binding node-set is not specified or empty, the
00346     // insert location node is the insert context node. Otherwise, if the at
00347     // attribute is not given, then the insert location node is the last node
00348     // of the Node Set Binding node-set. Otherwise, an insert location node is
00349     // determined from the at attribute.
00350     //
00351     // Delete: If the at attribute is not specified, there is no delete location.
00352     // Otherwise, the delete location is determined by evaluating the XPath
00353     // expression specified by the at attribute.
00354     //
00355 
00356     nsCOMPtr<nsIDOMNode> locationNode;
00357     PRUint32 atInt = 0;
00358     double atDoub = 0;
00359 
00360     nsAutoString atExpr;
00361     element->GetAttribute(NS_LITERAL_STRING("at"), atExpr);
00362           
00363     if (mIsInsert) {
00364       if (!nodeset || nodesetSize < 1) {
00365          // The insert location node is the insert context node.
00366          locationNode = contextNode;
00367       } else if (atExpr.IsEmpty()) {
00368         // The insert location node is the last node of the Node Set Binding
00369         // node-set.
00370         nodeset->SnapshotItem(nodesetSize - 1, getter_AddRefs(locationNode));
00371         NS_ENSURE_STATE(locationNode);
00372       }
00373     }
00374 
00375     if (!locationNode) {
00376       // For insert, we have a nodeset and got past the special cases of an empty
00377       // nodeset or no @at expression so the insert location node is determined by
00378       // @at.
00379       //
00380       // For delete, the delete location is determined by the @at expression if
00381       // present; otherwise there is no delete location and each node in the
00382       // Node Set Binding node-set is deleted, unless the node is the root
00383       // document element of an instance.
00384       if (!atExpr.IsEmpty()) {
00385         // The evaluation context node is the first node in document order of
00386         // the Node Set Binding node-set.
00387         nsCOMPtr<nsIDOMNode> evalContextNode;
00388         nodeset->SnapshotItem(0, getter_AddRefs(evalContextNode));
00389 
00390         // The context size is the size of the Node Set Binding node-set and
00391         // the context position is 1.
00392         nsCOMPtr<nsIDOMXPathResult> xpRes;
00393         rv = nsXFormsUtils::EvaluateXPath(atExpr, evalContextNode, element,
00394                                           nsIDOMXPathResult::NUMBER_TYPE,
00395                                           getter_AddRefs(xpRes), 1, nodesetSize);
00396 
00397         if (xpRes) {
00398           rv = xpRes->GetNumberValue(&atDoub);
00399           NS_ENSURE_SUCCESS(rv, rv);
00400         }
00401 
00402         // Determine the insert/delete location.
00403         if (atDoub < 1) {
00404           atInt = 1;
00405         } else {
00406           // If the location is greater than the nodeset size or NaN,
00407           // the location is the end of the nodeset.
00408           // XXX: Need to check for NaN but isnan() is not portable.
00409           atInt = (PRInt32) floor(atDoub+0.5);
00410           if (atInt > nodesetSize)
00411             atInt = nodesetSize;
00412         }
00413 
00414         // The location node is the node in the Node Set Binding node-set at
00415         // the position given by the location.
00416         nodeset->SnapshotItem(atInt - 1, getter_AddRefs(locationNode));
00417         NS_ENSURE_STATE(locationNode);
00418       }
00419     }
00420 
00421     // Context Info: 'insert-location-node' or 'delete-location'
00422     nsCOMPtr<nsXFormsContextInfo> contextInfo;
00423     if (mIsInsert) {
00424       contextInfo = new nsXFormsContextInfo(element);
00425       NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00426       contextInfo->SetNodeValue("insert-location-node", locationNode);
00427       mContextInfo.AppendObject(contextInfo);
00428     } else {
00429       contextInfo = new nsXFormsContextInfo(element);
00430       NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00431       contextInfo->SetNumberValue("delete-location", atInt);
00432       mContextInfo.AppendObject(contextInfo);
00433     }
00434 
00435     //
00436     // Step 5 (Insert): Each node in the origin node-set is cloned in the
00437     // order it appears in the origin node-set. If the origin node-set is empty
00438     // (Step 3), the origin node-set consists of the last node of the Node Set
00439     // Binding node-set.
00440     //
00441     // The clones are deep copies of the original nodes except the contents of
00442     // nodes of type xsd:ID are modified to remain as unique values in the
00443     // instance data after the clones are inserted.
00444     //
00445     // XXX: Need to modify the contents of nodes of type xsd:ID to remain
00446     // unique.
00447 
00448     nsCOMArray<nsIDOMNode> cloneNodes;
00449     nsCOMPtr<nsIDOMXPathResult> cloneNodeset;
00450     PRUint32 cloneNodesetSize = 0;
00451 
00452     if (mIsInsert) {
00453       nsCOMPtr<nsIDOMNode> prototypeNode, newNode;
00454       PRUint32 cloneIndex;
00455       
00456       // Get prototype node(s) and clone.
00457       if (originNodesetSize < 1) {
00458         // Origin nodeset is empty. Clone the last node of nodeset.
00459         cloneNodeset = nodeset;
00460         cloneNodesetSize = nodesetSize;
00461         cloneIndex = nodesetSize - 1;
00462       } else {
00463         // Clone all the nodes in the origin node-set.
00464         cloneNodeset = originNodeset;
00465         cloneNodesetSize = originNodesetSize;
00466         cloneIndex = 0;
00467       }
00468       // Context Info: 'inserted-nodes'
00469       nsCOMPtr<nsXFormsContextInfo> contextInfo =
00470         new nsXFormsContextInfo(element);
00471       NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00472       contextInfo->SetNodesetValue("inserted-nodes", cloneNodeset);
00473       mContextInfo.AppendObject(contextInfo);
00474 
00475       cloneNodeset->SnapshotItem(cloneIndex, getter_AddRefs(prototypeNode));
00476       NS_ENSURE_STATE(prototypeNode);
00477 
00478       // The prototypeNode (node to be cloned) and the locationNode (node to
00479       // which the clone will be inserted) may belong to different instances.
00480       nsCOMPtr<nsIDOMDocument> originDoc, locationDoc;
00481       prototypeNode->GetOwnerDocument(getter_AddRefs(originDoc));
00482       NS_ENSURE_STATE(originDoc);
00483       locationNode->GetOwnerDocument(getter_AddRefs(locationDoc));
00484       NS_ENSURE_STATE(locationDoc);
00485 
00486       while ((cloneIndex < cloneNodesetSize) && prototypeNode) {
00487         if (!SameCOMIdentity(originDoc, locationDoc)) {
00488           locationDoc->ImportNode(prototypeNode, PR_TRUE, getter_AddRefs(newNode));
00489         } else {
00490           prototypeNode->CloneNode(PR_TRUE, getter_AddRefs(newNode));
00491         }
00492         NS_ENSURE_STATE(newNode);
00493         cloneNodes.AppendObject(newNode);
00494 
00495         // Get the next node in the node-set.
00496         ++cloneIndex;
00497         cloneNodeset->SnapshotItem(cloneIndex, getter_AddRefs(prototypeNode));
00498       }
00499     }
00500 
00501     //
00502     // Step 6 and 7 (Insert): Determine the target location (Steps 6a-d) and
00503     // insert all of the nodes that were cloned in Step 5.
00504     //
00505     // Step 4 (Delete): Delete the nodes.
00506     //
00507     
00508     nsCOMPtr<nsIDOMDocument> locationDoc;
00509     nsCOMPtr<nsIDOMElement> locationDocElement;
00510     nsCOMPtr<nsIDOMNode> parentNode, newNode, resNode, instNode;
00511     if (mIsInsert) {
00512       // The cloned node or nodes are inserted in the order they were cloned at
00513       // their target location depending on their node type.
00514       nsCOMPtr<nsIDOMNode> newNode;
00515       
00516       for (PRInt32 i = 0; i < cloneNodes.Count(); ++i) {
00517         // Node to be inserted.
00518         newNode = cloneNodes[i];
00519 
00520         // Get the node type of the insert node and location node.
00521         PRUint16 newNodeType, locationNodeType;
00522         newNode->GetNodeType(&newNodeType);
00523         locationNode->GetNodeType(&locationNodeType);
00524 
00525         // Step 6a - If the Node Set Binding node-set is not specified or empty
00526         // OR Step 6b - If the Node Set Binding node-set is specified and not
00527         // empty and the type of the cloned node is different from the type of
00528         // the insert location node, the target location depends on the node
00529         // type of the cloned node.
00530         //
00531         // If the cloned node is an attribute, then the target location is before
00532         // the first attribute of the insert location node. If the cloned node is
00533         // not an attribute, then the target location is before the first child
00534         // of the insert location node.
00535         if ((!nodeset || nodesetSize < 1) ||
00536             (nodeset && nodesetSize > 0 && newNodeType != locationNodeType)) {
00537           Location location = eLocation_Before;
00538           nsCOMPtr<nsIDOMNode> targetNode;
00539           if (newNodeType != nsIDOMNode::ATTRIBUTE_NODE) {
00540             // Target location is before the first child of location node. If the
00541             // location node is empty (has no children), it remains the location
00542             // node and the new node will become the first child of the location
00543             // node.
00544             locationNode->GetFirstChild(getter_AddRefs(targetNode));
00545             if (!targetNode) {
00546               // New node will become first child of locationNode.
00547               location = eLocation_FirstChild;
00548               targetNode = locationNode;
00549             }
00550           }
00551           InsertNode(targetNode, newNode, location, getter_AddRefs(resNode));
00552         } else {
00553             // Step 6c - If insert location node is the root element of an
00554             // instance, then that instance root element location is the target
00555             // location and the cloned node replaces the instance element. If
00556             // there is more than one cloned node to insert, only the first node
00557             // that does not cause a conflict is considered.
00558             //
00559             locationNode->GetOwnerDocument(getter_AddRefs(locationDoc));
00560             NS_ENSURE_STATE(locationDoc);
00561             locationDoc->GetDocumentElement(getter_AddRefs(locationDocElement));
00562 
00563             if (SameCOMIdentity(locationNode, locationDocElement)) {
00564               // Step 7 - Replace the instance element with the first element
00565               // node of the cloned node(s).
00566               nsCOMPtr<nsIDOMNode> insertNode;
00567               GetFirstNodeOfType(&cloneNodes, nsIDOMNode::ELEMENT_NODE,
00568                                  getter_AddRefs(insertNode));
00569               if (insertNode) {
00570                 nsCOMPtr<nsIDOMNode> child;
00571                 locationDoc->RemoveChild(locationNode, getter_AddRefs(child));
00572                 locationDoc->AppendChild(insertNode, getter_AddRefs(resNode));
00573                 // Done...because we only consider the first node that does
00574                 // not cause a conflict.
00575                 break;
00576               }
00577             } else {
00578               // Step 6d - the target location is immediately before or after the
00579               // insert location node, based on the position attribute setting or
00580               // its default.
00581               PRBool insertAfter = PR_TRUE;
00582               nsAutoString position;
00583               element->GetAttribute(NS_LITERAL_STRING("position"), position);
00584               if (!position.IsEmpty()) {
00585                 if (position.EqualsLiteral("before")) {
00586                   insertAfter = PR_FALSE;
00587                 } else if (!position.EqualsLiteral("after")) {
00588                   // This is not a valid document...
00589                   return NS_ERROR_FAILURE;
00590                 }
00591               }
00592               // Context Info: 'position'.
00593               nsCOMPtr<nsXFormsContextInfo> contextInfo =
00594                 new nsXFormsContextInfo(element);
00595               NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00596               contextInfo->SetStringValue("position", position);
00597               mContextInfo.AppendObject(contextInfo);
00598 
00599               InsertNode(locationNode, newNode,
00600                          insertAfter ? eLocation_After: eLocation_Before,
00601                          getter_AddRefs(resNode));
00602             }
00603           }
00604         }
00605         rv = nsXFormsUtils::GetInstanceNodeForData(resNode, getter_AddRefs(instNode));
00606         NS_ENSURE_SUCCESS(rv, rv);
00607 
00608       // Step 8: Set indexes for repeats
00609       rv = RefreshRepeats(&cloneNodes);
00610       NS_ENSURE_SUCCESS(rv, rv);
00611 
00612     } else {
00613       // Delete
00614       // If there is no delete location, each node in the Node Set Binding
00615       // node-set is deleted, unless the node is the root document element of an
00616       // instance.
00617       //
00618       // If there is a delete location, the node at the delete location in the
00619       // Node Set Binding node-set is deleted, unless the node is the root
00620       // document element of an instance.
00621       PRBool didDelete = PR_FALSE;
00622 
00623       PRUint32 deleteIndex, deleteCount;
00624 
00625       if (!locationNode) {
00626         // Delete all the nodes in the node-set.
00627         deleteIndex = 0;
00628         deleteCount = nodesetSize;
00629       } else {
00630         // Delete the node at the delete location.
00631         deleteIndex = atInt - 1;
00632         deleteCount = atInt;
00633       }
00634       // Context Info: 'deleted-nodes'
00635       nsCOMPtr<nsXFormsContextInfo> contextInfo =
00636         new nsXFormsContextInfo(element);
00637       NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00638       contextInfo->SetNodesetValue("deleted-nodes", nodeset);
00639       mContextInfo.AppendObject(contextInfo);
00640 
00641       nodeset->SnapshotItem(deleteIndex, getter_AddRefs(locationNode));
00642       NS_ENSURE_STATE(locationNode);
00643 
00644       locationNode->GetOwnerDocument(getter_AddRefs(locationDoc));
00645       NS_ENSURE_STATE(locationDoc);
00646 
00647       rv = nsXFormsUtils::GetInstanceNodeForData(locationNode, getter_AddRefs(instNode));
00648       NS_ENSURE_SUCCESS(rv, rv);
00649 
00650       locationDoc->GetDocumentElement(getter_AddRefs(locationDocElement));
00651       while ((deleteIndex < deleteCount) && locationNode) {
00652         // Delete the node(s) unless the delete location is the root document
00653         // element of an instance.
00654         PRUint16 locationNodeType;
00655         locationNode->GetNodeType(&locationNodeType);
00656         if (locationNodeType == nsIDOMNode::ATTRIBUTE_NODE) {
00657           nsCOMPtr<nsIDOMElement> ownerElement;
00658           nsCOMPtr<nsIDOMAttr> attrNode(do_QueryInterface(locationNode));
00659           attrNode->GetOwnerElement(getter_AddRefs(ownerElement));
00660           NS_ENSURE_STATE(ownerElement);
00661 
00662           nsCOMPtr<nsIDOMAttr> resAttr;
00663           ownerElement->RemoveAttributeNode(attrNode, getter_AddRefs(resAttr));
00664           resNode = locationNode;
00665 
00666           // Deleted at least one node so delete will not terminate.
00667           didDelete = PR_TRUE;
00668         } else {
00669           if (!SameCOMIdentity(locationNode, locationDocElement)) {
00670             locationNode->GetParentNode(getter_AddRefs(parentNode));
00671             NS_ENSURE_STATE(parentNode);
00672 
00673             rv = parentNode->RemoveChild(locationNode, getter_AddRefs(resNode));
00674             NS_ENSURE_SUCCESS(rv, rv);
00675 
00676             // Deleted at least one node so delete will not terminate.
00677             didDelete = PR_TRUE;
00678           }
00679         }
00680         // Get the next node in the node-set.
00681         ++deleteIndex;
00682         nodeset->SnapshotItem(deleteIndex, getter_AddRefs(locationNode));
00683       }
00684 
00685       // The delete action is terminated with no effect if no node is deleted.
00686       if (!didDelete)
00687         return NS_OK;
00688     }
00689     NS_ENSURE_STATE(resNode);
00690 
00691     // Dispatch xforms-insert/delete event to the instance node we have modified
00692     // data for
00693     rv = nsXFormsUtils::DispatchEvent(instNode,
00694                                       mIsInsert ? eEvent_Insert : eEvent_Delete,
00695                                       nsnull, nsnull, &mContextInfo);
00696     NS_ENSURE_SUCCESS(rv, rv);
00697 
00698     // Dispatch refreshing events to the model
00699     if (aParentAction) {
00700       aParentAction->SetRebuild(model, PR_TRUE);
00701       aParentAction->SetRecalculate(model, PR_TRUE);
00702       aParentAction->SetRevalidate(model, PR_TRUE);
00703       aParentAction->SetRefresh(model, PR_TRUE);
00704     } else {
00705       rv = model->RequestRebuild();
00706       NS_ENSURE_SUCCESS(rv, rv);
00707       rv = model->RequestRecalculate();
00708       NS_ENSURE_SUCCESS(rv, rv);
00709       rv = model->RequestRevalidate();
00710       NS_ENSURE_SUCCESS(rv, rv);
00711       rv = model->RequestRefresh();
00712       NS_ENSURE_SUCCESS(rv, rv);
00713     }
00714 
00715     // Repeat this action if it can iterate and if it uses the `while`
00716     // attribute (the expression of which must have evaluated to true to
00717     // arrive here).
00718     if (!CanIterate() || !usesWhile) {
00719       return NS_OK;
00720     }
00721 
00722     // See if we've exceeded our time limit, and if so, prompt the user to
00723     // determine if she wants to cancel the loop.
00724     LL_SUB(runTime, PR_Now(), start);
00725     if (microseconds <= 0 || runTime < microseconds) {
00726       continue;
00727     }
00728 
00729     // The remaining part of the loop prompts the user about cancelling the
00730     // loop, and is only executed if we've gone over the time limit.
00731     PRBool stopWaiting = nsXFormsUtils::AskStopWaiting(element);
00732 
00733     if (stopWaiting) {
00734       // Stop the loop
00735       return NS_OK;
00736     } else {
00737       start = PR_Now();
00738     }
00739   }
00740 
00741   return NS_OK;
00742 }
00743 
00744 
00745 nsresult
00746 nsXFormsInsertDeleteElement::GetFirstNodeOfType(nsCOMArray<nsIDOMNode> *aNodes,
00747                                                 PRUint16 aNodeType,
00748                                                 nsIDOMNode **aResult)
00749 {
00750   nsCOMPtr<nsIDOMNode> currentNode;
00751 
00752   for (PRInt32 i = 0; i < aNodes->Count(); ++i) {
00753     currentNode = aNodes->ObjectAt(i);
00754     PRUint16 nodeType;
00755     currentNode->GetNodeType(&nodeType);
00756     if (nodeType == aNodeType) {
00757       NS_IF_ADDREF(*aResult = currentNode);
00758       break;
00759     }
00760   }
00761 
00762   return NS_OK;
00763 }
00764 
00765 nsresult
00766 nsXFormsInsertDeleteElement::InsertNode(nsIDOMNode *aTargetNode,
00767                                         nsIDOMNode *aNewNode,
00768                                         Location   aLocation,
00769                                         nsIDOMNode **aResNode)
00770 {
00771   NS_ENSURE_ARG(aTargetNode);
00772   NS_ENSURE_ARG(aNewNode);
00773   NS_ENSURE_ARG_POINTER(aResNode);
00774 
00775   // Make sure the result node is null in case we encounter a condition
00776   // where the node cannot be inserted and is skipped.
00777   *aResNode = nsnull;
00778 
00779   // Step 7 - The new node is inserted at the target location depending on its
00780   // node type. If the cloned node is a duplicate of another attribute in its
00781   // parent element, then the duplicate attribute is first removed. If a cloned
00782   // node cannot be placed at the target location due to a node type conflict,
00783   // then the insertion for that particular clone node is ignored.
00784   nsCOMPtr<nsIDOMNode> resNode;
00785   
00786   PRUint16 targetNodeType, newNodeType;
00787   aTargetNode->GetNodeType(&targetNodeType);
00788   aNewNode->GetNodeType(&newNodeType);
00789   
00790   if (newNodeType == nsIDOMNode::ATTRIBUTE_NODE) {
00791     // Can add an attribute to an element node or the owning element
00792     // of an attribute node.
00793     nsCOMPtr<nsIDOMElement> ownerElement;
00794 
00795     if (targetNodeType == nsIDOMNode::ELEMENT_NODE) {
00796       ownerElement = do_QueryInterface(aTargetNode);
00797     } else if (targetNodeType == nsIDOMNode::ATTRIBUTE_NODE) {
00798       nsCOMPtr<nsIDOMAttr> targetAttrNode(do_QueryInterface(aTargetNode));
00799       targetAttrNode->GetOwnerElement(getter_AddRefs(ownerElement));
00800     }
00801     NS_ENSURE_STATE(ownerElement);
00802 
00803     // Check for a duplicate attribute.
00804     nsAutoString attrNamespace, attrName, attrValue;
00805     aNewNode->GetNamespaceURI(attrNamespace);
00806     aNewNode->GetLocalName(attrName);
00807     aNewNode->GetNodeValue(attrValue);
00808 
00809     PRBool hasAttribute = PR_FALSE;
00810     ownerElement->HasAttributeNS(attrNamespace, attrName, &hasAttribute);
00811     if (hasAttribute) {
00812       ownerElement->RemoveAttributeNS(attrNamespace, attrName);
00813     }
00814     ownerElement->SetAttributeNS(attrNamespace, attrName, attrValue);
00815     resNode = aTargetNode;
00816     resNode.swap(*aResNode);
00817 
00818   } else {
00819     // New node will be inserted at location aLocation.
00820     nsCOMPtr<nsIDOMNode> targetNode = aTargetNode;
00821 
00822     nsCOMPtr<nsIDOMNode> parentNode;
00823     targetNode->GetParentNode(getter_AddRefs(parentNode));
00824     NS_ENSURE_STATE(parentNode);
00825 
00826     if (aLocation == eLocation_FirstChild) {
00827       aTargetNode->AppendChild(aNewNode, getter_AddRefs(resNode));
00828       resNode.swap(*aResNode);
00829     } else {
00830       if (aLocation == eLocation_After) {
00831         // If we're at the end of the nodeset, this returns nsnull, which is
00832         // fine, because InsertBefore then inserts at the end of the nodeset.
00833         aTargetNode->GetNextSibling(getter_AddRefs(targetNode));
00834       }
00835       parentNode->InsertBefore(aNewNode, targetNode,
00836                                getter_AddRefs(resNode));
00837       resNode.swap(*aResNode);
00838     }
00839   }
00840   
00841   return NS_OK;
00842 }
00843 
00844 nsresult
00845 nsXFormsInsertDeleteElement::RefreshRepeats(nsCOMArray<nsIDOMNode> *aNodes)
00846 {
00847   // XXXbeaufour: only check repeats belonging to the same model...
00848   // possibly use mFormControls? Should be quicker than searching through
00849   // entire document!! mModel->GetControls("repeat"); Would also possibly
00850   // save a QI?
00851 
00852   nsCOMPtr<nsIDOMDocument> document;
00853 
00854   nsresult rv = mElement->GetOwnerDocument(getter_AddRefs(document));
00855   NS_ENSURE_STATE(document);
00856 
00857   nsCOMPtr<nsIDOMNodeList> repeatNodes;
00858   document->GetElementsByTagNameNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
00859                                    NS_LITERAL_STRING("repeat"),
00860                                    getter_AddRefs(repeatNodes));
00861   NS_ENSURE_STATE(repeatNodes);
00862 
00863   // work over each node and if the node contains the inserted element
00864   PRUint32 nodeCount;
00865   rv = repeatNodes->GetLength(&nodeCount);
00866   NS_ENSURE_SUCCESS(rv, rv);
00867 
00868   for (PRUint32 node = 0; node < nodeCount; ++node) {
00869     nsCOMPtr<nsIDOMNode> repeatNode;
00870 
00871     rv = repeatNodes->Item(node, getter_AddRefs(repeatNode));
00872     nsCOMPtr<nsIXFormsRepeatElement> repeatEl(do_QueryInterface(repeatNode));
00873     NS_ENSURE_STATE(repeatEl);
00874 
00875     for (PRInt32 i = 0; i < aNodes->Count(); ++i) {
00876       nsCOMPtr<nsIDOMNode> newNode = aNodes->ObjectAt(i);
00877       rv = repeatEl->HandleNodeInsert(newNode);
00878       NS_ENSURE_SUCCESS(rv, rv);
00879     }
00880   }
00881 
00882   return NS_OK;
00883 }
00884  
00885 
00886 NS_HIDDEN_(nsresult)
00887 NS_NewXFormsInsertElement(nsIXTFElement **aResult)
00888 {
00889   *aResult = new nsXFormsInsertDeleteElement(PR_TRUE);
00890   if (!*aResult)
00891     return NS_ERROR_OUT_OF_MEMORY;
00892 
00893   NS_ADDREF(*aResult);
00894   return NS_OK;
00895 }
00896 
00897 
00898 NS_HIDDEN_(nsresult)
00899 NS_NewXFormsDeleteElement(nsIXTFElement **aResult)
00900 {
00901   *aResult = new nsXFormsInsertDeleteElement(PR_FALSE);
00902   if (!*aResult)
00903     return NS_ERROR_OUT_OF_MEMORY;
00904 
00905   NS_ADDREF(*aResult);
00906   return NS_OK;
00907 }