[XForms] support xforms 1.1 insert action. Bug 368583, p=msterlin r=olli+aaronr

This commit is contained in:
aaronr%us.ibm.com 2007-07-02 23:10:57 +00:00
Родитель 2ab9f5784f
Коммит 5046215e9f
1 изменённых файлов: 570 добавлений и 121 удалений

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Allan Beaufour <abeaufour@novell.com>
* Merle Sterling <msterlin@us.ibm.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -36,11 +37,13 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsIDOMAttr.h"
#include "nsIDOMEvent.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
#include "nsIDOMDocument.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMNamedNodeMap.h"
#include "nsIXFormsRepeatElement.h"
#include "nsIXFormsControl.h"
@ -74,7 +77,29 @@ class nsXFormsInsertDeleteElement : public nsXFormsActionModuleBase
private:
PRBool mIsInsert;
nsresult RefreshRepeats(nsIDOMNode *aNode);
/** Get the first node of a given type in aNodes.
*
* @param aNodes array of nodes
* @param aNodeType type of node to find
*
* @return aResult node of type aNodeType
*/
nsresult GetFirstNodeOfType(nsCOMArray<nsIDOMNode> *aNodes,
PRUint16 aNodeType,
nsIDOMNode **aResult);
/** Insert a node.
*
* @param aTargetNode target location node
* @param aNewNode node to insert
* @param aInsertAfter insert before or after target?
*
* @return aResult result node
*/
nsresult InsertNode(nsIDOMNode *aTargetNode, nsIDOMNode *aNewNode,
PRBool aInsertAfter, nsIDOMNode **aResNode);
nsresult RefreshRepeats(nsCOMArray<nsIDOMNode> *aNodes);
public:
NS_DECL_NSIXFORMSACTIONMODULEELEMENT
@ -92,148 +117,471 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent,
if (!mElement)
return NS_OK;
// First we need to get the three attributes: @nodeset, @at, and possibly
// @position
//
// 1) Get @nodeset
nsresult rv;
nsCOMPtr<nsIModelElementPrivate> model;
nsCOMPtr<nsIDOMXPathResult> nodeset;
PRBool usesModelBinding;
rv = nsXFormsUtils::EvaluateNodeBinding(mElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
NS_LITERAL_STRING("nodeset"),
EmptyString(),
nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
getter_AddRefs(model),
getter_AddRefs(nodeset),
&usesModelBinding);
NS_ENSURE_SUCCESS(rv, rv);
if (!model || !nodeset)
return NS_OK;
PRUint32 setSize;
rv = nodeset->GetSnapshotLength(&setSize);
NS_ENSURE_SUCCESS(rv, rv);
// Unfortunately we cannot insert into an empty set, as there's nothing to
// clone.
if (setSize < 1)
return NS_OK;
//
// 2) Get @at
nsAutoString atExpr;
mElement->GetAttribute(NS_LITERAL_STRING("at"), atExpr);
if (atExpr.IsEmpty())
return NS_OK;
// Context node is first node in nodeset
// Step 1 (Insert or Delete): Determine the insert/delete context.
//
// If the bind attribute is present, it is evaluated to determine the
// in-scope evaluation context and the context attribute is ignored;
// otherwise, the context attribute is evaluated and overrides the
// in-scope evaluation context.
//
// A NodeSet binding attribute (@bind or @nodeset) is required unless
// the context attribute is present.
//
nsCOMPtr<nsIDOMXPathResult> contextNodeset;
nsCOMPtr<nsIDOMNode> contextNode;
nodeset->SnapshotItem(0, getter_AddRefs(contextNode));
PRUint32 contextNodesetSize = 0;
nsCOMPtr<nsIDOMXPathResult> at;
rv = nsXFormsUtils::EvaluateXPath(atExpr, contextNode, mElement,
nsIDOMXPathResult::NUMBER_TYPE,
getter_AddRefs(at), 1, setSize);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString bindExpr;
nsAutoString contextExpr;
nsAutoString contextStr;
if (!at)
return NS_OK;
PRUint32 atInt = 0;
double atDoub;
rv = at->GetNumberValue(&atDoub);
NS_ENSURE_SUCCESS(rv, rv);
/// XXX we need to check for NaN, and select last row. but isnan() is not
/// portable :(
if (atDoub < 1) {
atInt = 1;
// Determine if the context node is specified via @bind or @context.
// If @bind is present, @context is ignored.
mElement->GetAttribute(NS_LITERAL_STRING("bind"), bindExpr);
if (!bindExpr.IsEmpty()) {
contextStr.AssignLiteral("bind");
} else {
atInt = (PRInt32) floor(atDoub + 0.5);
if (atInt > setSize)
atInt = setSize;
}
//
// 3) Get @position (if \<insert\>)
nsAutoString position;
PRBool insertAfter = PR_FALSE;
if (mIsInsert) {
mElement->GetAttribute(NS_LITERAL_STRING("position"), position);
if (position.EqualsLiteral("after")) {
insertAfter = PR_TRUE;
} else if (!position.EqualsLiteral("before")) {
// This is not a valid document...
return NS_ERROR_ABORT;
mElement->GetAttribute(NS_LITERAL_STRING("context"), contextExpr);
if (!contextExpr.IsEmpty()) {
contextStr.AssignLiteral("context");
}
}
if (!contextStr.IsEmpty()) {
// Context node is specified via either @bind or @context.
rv = nsXFormsUtils::EvaluateNodeBinding(mElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
contextStr,
EmptyString(),
nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
getter_AddRefs(model),
getter_AddRefs(contextNodeset),
&usesModelBinding);
NS_ENSURE_SUCCESS(rv, rv);
if (!model)
return NS_OK;
// The insert/delete action is terminated with no effect if the context
// is the empty node-set.
if (contextNodeset) {
rv = contextNodeset->GetSnapshotLength(&contextNodesetSize);
NS_ENSURE_SUCCESS(rv, rv);
if (contextNodesetSize < 1)
return NS_OK;
// Context node is the first node in the nodeset.
contextNodeset->SnapshotItem(0, getter_AddRefs(contextNode));
}
} else {
// Neither @bind nor @context. Get the in-scope evaluation context.
nsCOMPtr<nsIDOMElement> bindElement;
nsCOMPtr<nsIXFormsControl> parentControl;
PRBool outerBind;
rv = nsXFormsUtils::GetNodeContext(mElement,
nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR,
getter_AddRefs(model),
getter_AddRefs(bindElement),
&outerBind,
getter_AddRefs(parentControl),
getter_AddRefs(contextNode));
NS_ENSURE_SUCCESS(rv, rv);
// The insert/delete action is terminated with no effect if the context
// is the empty node-set.
if (!model || !contextNode)
return NS_OK;
}
// The insert action is terminated with no effect if the context attribute
// is given and the insert context does not evaluate to an element node.
if (mIsInsert && !contextExpr.IsEmpty()) {
PRUint16 nodeType;
contextNode->GetNodeType(&nodeType);
if (nodeType != nsIDOMNode::ELEMENT_NODE)
return NS_OK;
}
//
// Step 2 (Insert or Delete): Determine the node-set binding.
//
// If the bind attribute is present, it directly determines the Node Set
// Binding node-set. If a nodeset attribute is present, it is evaluated
// within the context to determine the Node Set Binding node-set.
//
nsCOMPtr<nsIDOMXPathResult> nodeset;
PRUint32 nodesetSize = 0;
if (bindExpr.IsEmpty()) {
nsAutoString nodesetExpr;
mElement->GetAttribute(NS_LITERAL_STRING("nodeset"), nodesetExpr);
if (!nodesetExpr.IsEmpty()) {
// Evaluate the nodeset attribute within the context.
rv = nsXFormsUtils::EvaluateXPath(nodesetExpr, contextNode, mElement,
nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
getter_AddRefs(nodeset));
NS_ENSURE_SUCCESS(rv, rv);
rv = nodeset->GetSnapshotLength(&nodesetSize);
NS_ENSURE_SUCCESS(rv, rv);
}
// The insert action is terminated with no effect if the context attribute
// is not given and the Node Set Binding node-set is the empty node-set.
//
// The delete action is terminated with no effect if the Node Set Binding
// node-set is the empty node-set.
if (!nodeset || nodesetSize < 1) {
if (!mIsInsert || (mIsInsert && contextExpr.IsEmpty()))
return NS_OK;
}
} else {
// Nodeset was determined by @bind.
nodeset = contextNodeset;
nodesetSize = contextNodesetSize;
}
//
// Step 3 (Insert): Determine the origin node-set.
//
nsCOMPtr<nsIDOMXPathResult> originNodeset;
nsCOMPtr<nsIDOMXPathResult> originNode;
PRUint32 originNodesetSize = 0;
if (mIsInsert) {
// If the origin attribute is not given and the Node Set Binding node-set
// is empty, then the origin node-set is the empty node-set. Otherwise,
// if the origin attribute is not given, then the origin node-set consists
// of the last node of the Node Set Binding node-set (which we will obtain
// just before performing the insert).
//
// If the origin attribute is given, the origin node-set is the result of
// the evaluation of the origin attribute in the insert context.
nsAutoString origin;
mElement->GetAttribute(NS_LITERAL_STRING("origin"), origin);
if (!origin.IsEmpty()) {
rv = nsXFormsUtils::EvaluateXPath(origin, contextNode, mElement,
nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
getter_AddRefs(originNodeset));
NS_ENSURE_SUCCESS(rv, rv);
// The insert action is terminated with no effect if the origin node-set
// is the empty node-set.
if (!originNodeset)
return NS_OK;
rv = originNodeset->GetSnapshotLength(&originNodesetSize);
NS_ENSURE_SUCCESS(rv, rv);
// The insert action is terminated with no effect if the origin node-set
// is the empty node-set.
if (originNodesetSize < 1)
return NS_OK;
}
}
//
// Step 4 (Insert), Step 3 (Delete):
// Determine the insert/delete location node.
//
// Insert: If the Node Set Binding node-set is not specified or empty, the
// insert location node is the insert context node. Otherwise, if the at
// attribute is not given, then the insert location node is the last node
// of the Node Set Binding node-set. Otherwise, an insert location node is
// determined from the at attribute.
//
// Delete: If the at attribute is not specified, there is no delete location.
// Otherwise, the delete location is determined by evaluating the XPath
// expression specified by the at attribute.
//
nsCOMPtr<nsIDOMNode> locationNode;
PRUint32 atInt = 0;
double atDoub = 0;
nsAutoString atExpr;
mElement->GetAttribute(NS_LITERAL_STRING("at"), atExpr);
if (mIsInsert) {
if (!nodeset || nodesetSize < 1) {
// The insert location node is the insert context node.
locationNode = contextNode;
} else if (atExpr.IsEmpty()) {
// The insert location node is the last node of the Node Set Binding
// node-set.
nodeset->SnapshotItem(nodesetSize - 1, getter_AddRefs(locationNode));
NS_ENSURE_STATE(locationNode);
}
}
if (!locationNode) {
// For insert, we have a nodeset and got past the special cases of an empty
// nodeset or no @at expression so the insert location node is determined by
// @at.
//
// For delete, the delete location is determined by the @at expression if
// present; otherwise there is no delete location and each node in the
// Node Set Binding node-set is deleted, unless the node is the root
// document element of an instance.
if (!atExpr.IsEmpty()) {
// The evaluation context node is the first node in document order of
// the Node Set Binding node-set.
nsCOMPtr<nsIDOMNode> evalContextNode;
nodeset->SnapshotItem(0, getter_AddRefs(evalContextNode));
// The context size is the size of the Node Set Binding node-set and
// the context position is 1.
nsCOMPtr<nsIDOMXPathResult> xpRes;
rv = nsXFormsUtils::EvaluateXPath(atExpr, evalContextNode, mElement,
nsIDOMXPathResult::NUMBER_TYPE,
getter_AddRefs(xpRes), 1, nodesetSize);
if (xpRes) {
rv = xpRes->GetNumberValue(&atDoub);
NS_ENSURE_SUCCESS(rv, rv);
}
// Determine the insert/delete location.
if (atDoub < 1) {
atInt = 1;
} else {
// If the location is greater than the nodeset size or NaN,
// the location is the end of the nodeset.
// XXX: Need to check for NaN but isnan() is not portable.
atInt = (PRInt32) floor(atDoub+0.5);
if (atInt > nodesetSize)
atInt = nodesetSize;
}
// The location node is the node in the Node Set Binding node-set at
// the position given by the location.
nodeset->SnapshotItem(atInt - 1, getter_AddRefs(locationNode));
NS_ENSURE_STATE(locationNode);
}
}
//
// Step 5 (Insert): Each node in the origin node-set is cloned in the
// order it appears in the origin node-set. If the origin node-set is empty
// (Step 3), the origin node-set consists of the last node of the Node Set
// Binding node-set.
//
// The clones are deep copies of the original nodes except the contents of
// nodes of type xsd:ID are modified to remain as unique values in the
// instance data after the clones are inserted.
//
// XXX: Need to modify the contents of nodes of type xsd:ID to remain
// unique.
nsCOMArray<nsIDOMNode> cloneNodes;
nsCOMPtr<nsIDOMXPathResult> cloneNodeset;
PRUint32 cloneNodesetSize = 0;
if (mIsInsert) {
nsCOMPtr<nsIDOMNode> prototypeNode, newNode;
PRUint32 cloneIndex;
// Get prototype node(s) and clone.
if (originNodesetSize < 1) {
// Origin nodeset is empty. Clone the last node of nodeset.
cloneNodeset = nodeset;
cloneNodesetSize = nodesetSize;
cloneIndex = nodesetSize - 1;
} else {
// Clone all the nodes in the origin node-set.
cloneNodeset = originNodeset;
cloneNodesetSize = originNodesetSize;
cloneIndex = 0;
}
cloneNodeset->SnapshotItem(cloneIndex, getter_AddRefs(prototypeNode));
NS_ENSURE_STATE(prototypeNode);
// The prototypeNode (node to be cloned) and the locationNode (node to
// which the clone will be inserted) may belong to different instances.
nsCOMPtr<nsIDOMDocument> originDoc, locationDoc;
prototypeNode->GetOwnerDocument(getter_AddRefs(originDoc));
NS_ENSURE_STATE(originDoc);
locationNode->GetOwnerDocument(getter_AddRefs(locationDoc));
NS_ENSURE_STATE(locationDoc);
while ((cloneIndex < cloneNodesetSize) && prototypeNode) {
if (!SameCOMIdentity(originDoc, locationDoc)) {
locationDoc->ImportNode(prototypeNode, PR_TRUE, getter_AddRefs(newNode));
} else {
prototypeNode->CloneNode(PR_TRUE, getter_AddRefs(newNode));
}
NS_ENSURE_STATE(newNode);
cloneNodes.AppendObject(newNode);
// Get the next node in the node-set.
++cloneIndex;
cloneNodeset->SnapshotItem(cloneIndex, getter_AddRefs(prototypeNode));
}
}
//
// Step 6 and 7 (Insert): Determine the target location (Steps 6a-d) and
// insert all of the nodes that were cloned in Step 5.
//
// Step 4 (Delete): Delete the nodes.
//
#ifdef DEBUG_XF_INSERTDELETE
nsCOMPtr<nsIDOMDocument> locationDoc;
nsCOMPtr<nsIDOMElement> locationDocElement;
nsCOMPtr<nsIDOMNode> parentNode, newNode, resNode;
if (mIsInsert) {
printf("Will try to INSERT node _%s_ index %d (set size: %d)\n",
NS_ConvertUTF16toUTF8(position).get(),
atInt,
setSize);
} else {
printf("Will try to DELETE node at index %d (set size: %d)\n",
atInt,
setSize);
}
#endif
// Find location
nsCOMPtr<nsIDOMNode> location;
nodeset->SnapshotItem(atInt - 1, getter_AddRefs(location));
NS_ENSURE_STATE(location);
nsCOMPtr<nsIDOMNode> parent;
location->GetParentNode(getter_AddRefs(parent));
NS_ENSURE_STATE(parent);
if (mIsInsert && insertAfter) {
nsCOMPtr<nsIDOMNode> temp;
// If we're at the end of the nodeset, this returns nsnull, which is fine,
// because InsertBefore then inserts at the end of the nodeset
location->GetNextSibling(getter_AddRefs(temp));
location.swap(temp);
}
nsCOMPtr<nsIDOMNode> resNode;
if (mIsInsert) {
// Get prototype and clone it (last member of nodeset)
nsCOMPtr<nsIDOMNode> prototype;
nodeset->SnapshotItem(setSize - 1, getter_AddRefs(prototype));
NS_ENSURE_STATE(prototype);
// The cloned node or nodes are inserted in the order they were cloned at
// their target location depending on their node type.
nsCOMPtr<nsIDOMNode> newNode;
prototype->CloneNode(PR_TRUE, getter_AddRefs(newNode));
NS_ENSURE_STATE(newNode);
for (PRInt32 i = 0; i < cloneNodes.Count(); ++i) {
// Node to be inserted.
newNode = cloneNodes[i];
parent->InsertBefore(newNode,
location,
getter_AddRefs(resNode));
NS_ENSURE_STATE(resNode);
// Get the node type of the insert node and location node.
PRUint16 newNodeType, locationNodeType;
newNode->GetNodeType(&newNodeType);
locationNode->GetNodeType(&locationNodeType);
// Set indexes for repeats
rv = RefreshRepeats(resNode);
// Step 6a - If the Node Set Binding node-set is not specified or empty
// OR Step 6b - If the Node Set Binding node-set is specified and not
// empty and the type of the cloned node is different from the type of
// the insert location node, the target location depends on the node
// type of the cloned node.
//
// If the cloned node is an attribute, then the target location is before
// the first attribute of the insert location node. If the cloned node is
// not an attribute, then the target location is before the first child
// of the insert location node.
if (!nodeset ||
(nodeset && nodesetSize > 1 && newNodeType != locationNodeType)) {
if (newNodeType != nsIDOMNode::ATTRIBUTE_NODE) {
// target location is before first child of location node.
nsCOMPtr<nsIDOMNode> targetNode;
locationNode->GetFirstChild(getter_AddRefs(targetNode));
locationNode.swap(targetNode);
}
InsertNode(locationNode, newNode, PR_FALSE, getter_AddRefs(resNode));
} else {
// Step 6c - If insert location node is the root element of an
// instance, then that instance root element location is the target
// location and the cloned node replaces the instance element. If
// there is more than one cloned node to insert, only the first node
// that does not cause a conflict is considered.
//
locationNode->GetOwnerDocument(getter_AddRefs(locationDoc));
NS_ENSURE_STATE(locationDoc);
locationDoc->GetDocumentElement(getter_AddRefs(locationDocElement));
if (SameCOMIdentity(locationNode, locationDocElement)) {
// Step 7 - Replace the instance element with the first element
// node of the cloned node(s).
nsCOMPtr<nsIDOMNode> insertNode;
GetFirstNodeOfType(&cloneNodes, nsIDOMNode::ELEMENT_NODE,
getter_AddRefs(insertNode));
if (insertNode) {
nsCOMPtr<nsIDOMNode> child;
locationDoc->RemoveChild(locationNode, getter_AddRefs(child));
locationDoc->AppendChild(insertNode, getter_AddRefs(resNode));
// Done...because we only consider the first node that does
// not cause a conflict.
break;
}
} else {
// Step 6d - the target location is immediately before or after the
// insert location node, based on the position attribute setting or
// its default.
PRBool insertAfter = PR_TRUE;
nsAutoString position;
mElement->GetAttribute(NS_LITERAL_STRING("position"), position);
if (!position.IsEmpty()) {
if (position.EqualsLiteral("before")) {
insertAfter = PR_FALSE;
} else if (!position.EqualsLiteral("after")) {
// This is not a valid document...
return NS_ERROR_FAILURE;
}
}
InsertNode(locationNode, newNode, insertAfter, getter_AddRefs(resNode));
}
}
}
// Step 8: Set indexes for repeats
rv = RefreshRepeats(&cloneNodes);
NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = parent->RemoveChild(location, getter_AddRefs(resNode));
NS_ENSURE_SUCCESS(rv, rv);
// Delete
// If there is no delete location, each node in the Node Set Binding
// node-set is deleted, unless the node is the root document element of an
// instance.
//
// If there is a delete location, the node at the delete location in the
// Node Set Binding node-set is deleted, unless the node is the root
// document element of an instance.
PRBool didDelete = PR_FALSE;
PRUint32 deleteIndex, deleteCount;
if (!locationNode) {
// Delete all the nodes in the node-set.
deleteIndex = 0;
deleteCount = nodesetSize;
} else {
// Delete the node at the delete location.
deleteIndex = atInt - 1;
deleteCount = atInt;
}
nodeset->SnapshotItem(deleteIndex, getter_AddRefs(locationNode));
NS_ENSURE_STATE(locationNode);
locationNode->GetOwnerDocument(getter_AddRefs(locationDoc));
NS_ENSURE_STATE(locationDoc);
locationDoc->GetDocumentElement(getter_AddRefs(locationDocElement));
while ((deleteIndex < deleteCount) && locationNode) {
// Delete the node(s) unless the delete location is the root document
// element of an instance.
if (!SameCOMIdentity(locationNode, locationDocElement)) {
locationNode->GetParentNode(getter_AddRefs(parentNode));
NS_ENSURE_STATE(parentNode);
rv = parentNode->RemoveChild(locationNode, getter_AddRefs(resNode));
NS_ENSURE_SUCCESS(rv, rv);
// Get the next node in the node-set.
++deleteIndex;
nodeset->SnapshotItem(deleteIndex, getter_AddRefs(locationNode));
// Deleted at least one node so delete will not terminate.
didDelete = PR_TRUE;
}
}
// The delete action is terminated with no effect if no node is deleted.
if (!didDelete)
return NS_OK;
}
NS_ENSURE_STATE(resNode);
// Dispatch xforms-insert/delete event to the instance node we have modified
// data for
nsCOMPtr<nsIDOMNode> instNode;
rv = nsXFormsUtils::GetInstanceNodeForData(resNode, getter_AddRefs(instNode));
rv = nsXFormsUtils::GetInstanceNodeForData(locationNode, getter_AddRefs(instNode));
NS_ENSURE_SUCCESS(rv, rv);
rv = nsXFormsUtils::DispatchEvent(instNode,
mIsInsert ? eEvent_Insert : eEvent_Delete);
NS_ENSURE_SUCCESS(rv, rv);
@ -260,7 +608,105 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent,
nsresult
nsXFormsInsertDeleteElement::RefreshRepeats(nsIDOMNode *aNode)
nsXFormsInsertDeleteElement::GetFirstNodeOfType(nsCOMArray<nsIDOMNode> *aNodes,
PRUint16 aNodeType,
nsIDOMNode **aResult)
{
nsCOMPtr<nsIDOMNode> currentNode;
for (PRInt32 i = 0; i < aNodes->Count(); ++i) {
currentNode = aNodes->ObjectAt(i);
PRUint16 nodeType;
currentNode->GetNodeType(&nodeType);
if (nodeType == aNodeType) {
NS_IF_ADDREF(*aResult = currentNode);
break;
}
}
return NS_OK;
}
nsresult
nsXFormsInsertDeleteElement::InsertNode(nsIDOMNode *aTargetNode,
nsIDOMNode *aNewNode,
PRBool aInsertAfter,
nsIDOMNode **aResNode)
{
NS_ENSURE_ARG(aTargetNode);
NS_ENSURE_ARG(aNewNode);
NS_ENSURE_ARG_POINTER(aResNode);
// Make sure the result node is null in case we encounter a condition
// where the node cannot be inserted and is skipped.
*aResNode = nsnull;
// Step 7 - The new node is inserted at the target location depending on its
// node type. If the cloned node is a duplicate of another attribute in its
// parent element, then the duplicate attribute is first removed. If a cloned
// node cannot be placed at the target location due to a node type conflict,
// then the insertion for that particular clone node is ignored.
nsCOMPtr<nsIDOMNode> resNode;
PRUint16 targetNodeType, newNodeType;
aTargetNode->GetNodeType(&targetNodeType);
aNewNode->GetNodeType(&newNodeType);
if (newNodeType == nsIDOMNode::ATTRIBUTE_NODE) {
// Can add an attribute to an element node or the owning element
// of an attribute node.
nsCOMPtr<nsIDOMElement> ownerElement;
nsCOMPtr<nsIDOMAttr> attrNode(do_QueryInterface(aNewNode));
if (targetNodeType == nsIDOMNode::ELEMENT_NODE) {
ownerElement = do_QueryInterface(aTargetNode);
} else if (targetNodeType == nsIDOMNode::ATTRIBUTE_NODE) {
attrNode->GetOwnerElement(getter_AddRefs(ownerElement));
}
NS_ENSURE_STATE(ownerElement);
// Check for a duplicate attribute.
nsAutoString attrName, attrValue;
attrNode->GetName(attrName);
attrNode->GetValue(attrValue);
PRBool hasAttribute = PR_FALSE;
ownerElement->HasAttribute(attrName, &hasAttribute);
if (hasAttribute) {
ownerElement->RemoveAttribute(attrName);
}
ownerElement->SetAttribute(attrName, attrValue);
resNode = aTargetNode;
resNode.swap(*aResNode);
} else {
// New node is not an attribute so it can only be inserted to an
// element node.
if (targetNodeType == nsIDOMNode::ELEMENT_NODE) {
// New node will be inserted either before or after targetNode.
nsCOMPtr<nsIDOMNode> targetNode;
targetNode = aTargetNode;
nsCOMPtr<nsIDOMNode> parentNode;
targetNode->GetParentNode(getter_AddRefs(parentNode));
NS_ENSURE_STATE(parentNode);
if (aInsertAfter) {
// If we're at the end of the nodeset, this returns nsnull, which is
// fine, because InsertBefore then inserts at the end of the nodeset.
aTargetNode->GetNextSibling(getter_AddRefs(targetNode));
}
parentNode->InsertBefore(aNewNode, targetNode,
getter_AddRefs(resNode));
resNode.swap(*aResNode);
}
}
return NS_OK;
}
nsresult
nsXFormsInsertDeleteElement::RefreshRepeats(nsCOMArray<nsIDOMNode> *aNodes)
{
// XXXbeaufour: only check repeats belonging to the same model...
// possibly use mFormControls? Should be quicker than searching through
@ -290,8 +736,11 @@ nsXFormsInsertDeleteElement::RefreshRepeats(nsIDOMNode *aNode)
nsCOMPtr<nsIXFormsRepeatElement> repeatEl(do_QueryInterface(repeatNode));
NS_ENSURE_STATE(repeatEl);
rv = repeatEl->HandleNodeInsert(aNode);
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < aNodes->Count(); ++i) {
nsCOMPtr<nsIDOMNode> newNode = aNodes->ObjectAt(i);
rv = repeatEl->HandleNodeInsert(newNode);
NS_ENSURE_SUCCESS(rv, rv);
}
}
return NS_OK;