diff --git a/extensions/xforms/nsIXFormsRepeatElement.idl b/extensions/xforms/nsIXFormsRepeatElement.idl index 0b7e548e57d..200e7060d2a 100644 --- a/extensions/xforms/nsIXFormsRepeatElement.idl +++ b/extensions/xforms/nsIXFormsRepeatElement.idl @@ -46,7 +46,7 @@ interface nsIXFormsControl; interface nsIDOMNode; -[uuid(419e780d-4f31-4aa4-bae8-a18099d77bb6)] +[uuid(41a0ea05-3fe0-48e8-8c38-96cbb544309c)] interface nsIXFormsRepeatElement : nsISupports { /** @@ -115,5 +115,19 @@ interface nsIXFormsRepeatElement : nsISupports */ void indexHasChanged(); + /** + * Retrieve the starting index for the repeat, that is the index that the + * repeat is initialized to. It is the "startindex" attribute, corrected to + * be a valid index value. + */ unsigned long getStartingIndex(); + + /** + * Handle insertion of a node, ie. eventually adjust index. Note that the + * node might not be influencing the repeat, it is the repeat's + * responsibility to check that. + * + * @param node The node that was inserted + */ + void handleNodeInsert(in nsIDOMNode node); }; diff --git a/extensions/xforms/nsXFormsInsertDeleteElement.cpp b/extensions/xforms/nsXFormsInsertDeleteElement.cpp index 5b6c9ea777b..badcc3e623c 100644 --- a/extensions/xforms/nsXFormsInsertDeleteElement.cpp +++ b/extensions/xforms/nsXFormsInsertDeleteElement.cpp @@ -41,6 +41,8 @@ #include "nsIDOMElement.h" #include "nsIDOMDocument.h" #include "nsIDOMNodeList.h" +#include "nsIXFormsRepeatElement.h" +#include "nsIXFormsControl.h" #include "nsString.h" @@ -48,6 +50,7 @@ #include "nsXFormsActionModuleBase.h" #include "nsXFormsActionElement.h" #include "nsXFormsUtils.h" +#include "nsIDOM3Node.h" #include "math.h" @@ -65,14 +68,14 @@ * @see http://www.w3.org/TR/xforms/slice4.html#evt-insert * @see https://bugzilla.mozilla.org/show_bug.cgi?id=280423 * - * @todo Any \ elements need to set their repeat-indexes properly if - * they are bound to the same nodeset. (XXX) */ class nsXFormsInsertDeleteElement : public nsXFormsActionModuleBase { private: PRBool mIsInsert; - + + nsresult RefreshRepeats(nsIDOMNode *aNode); + public: NS_DECL_NSIXFORMSACTIONMODULEELEMENT @@ -200,7 +203,6 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, nsCOMPtr resNode; if (mIsInsert) { - // // Get prototype and clone it (last member of nodeset) nsCOMPtr prototype; nodeset->SnapshotItem(setSize - 1, getter_AddRefs(prototype)); @@ -214,6 +216,11 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, location, getter_AddRefs(resNode)); NS_ENSURE_STATE(resNode); + + // Set indexes for repeats + rv = RefreshRepeats(resNode); + NS_ENSURE_SUCCESS(rv, rv); + } else { rv = parent->RemoveChild(location, getter_AddRefs(resNode)); NS_ENSURE_SUCCESS(rv, rv); @@ -221,9 +228,6 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, // Dispatch xforms-insert/delete event to the instance node we have modified // data for - nsCOMPtr modelElem = do_QueryInterface(model); - NS_ENSURE_STATE(modelElem); - nsCOMPtr instNode; rv = nsXFormsUtils::GetInstanceNodeForData(resNode, getter_AddRefs(instNode)); NS_ENSURE_SUCCESS(rv, rv); @@ -233,6 +237,8 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, NS_ENSURE_SUCCESS(rv, rv); // Dispatch refreshing events to the model + nsCOMPtr modelElem(do_QueryInterface(model)); + NS_ASSERTION(modelElem, "Model not implementing nsIDOMElement?!"); if (aParentAction) { aParentAction->SetRebuild(modelElem, PR_TRUE); aParentAction->SetRecalculate(modelElem, PR_TRUE); @@ -248,6 +254,46 @@ nsXFormsInsertDeleteElement::HandleAction(nsIDOMEvent *aEvent, return NS_OK; } + +nsresult +nsXFormsInsertDeleteElement::RefreshRepeats(nsIDOMNode *aNode) +{ + // XXXbeaufour: only check repeats belonging to the same model... + // possibly use mFormControls? Should be quicker than searching through + // entire document!! mModel->GetControls("repeat"); Would also possibly + // save a QI? + + nsCOMPtr document; + + nsresult rv = mElement->GetOwnerDocument(getter_AddRefs(document)); + NS_ENSURE_STATE(document); + + nsCOMPtr repeatNodes; + document->GetElementsByTagNameNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS), + NS_LITERAL_STRING("repeat"), + getter_AddRefs(repeatNodes)); + NS_ENSURE_STATE(repeatNodes); + + // work over each node and if the node contains the inserted element + PRUint32 nodeCount; + rv = repeatNodes->GetLength(&nodeCount); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 node = 0; node < nodeCount; ++node) { + nsCOMPtr repeatNode; + + rv = repeatNodes->Item(node, getter_AddRefs(repeatNode)); + nsCOMPtr repeatEl(do_QueryInterface(repeatNode)); + NS_ENSURE_STATE(repeatEl); + + rv = repeatEl->HandleNodeInsert(aNode); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + + NS_HIDDEN_(nsresult) NS_NewXFormsInsertElement(nsIXTFElement **aResult) { diff --git a/extensions/xforms/nsXFormsRepeatElement.cpp b/extensions/xforms/nsXFormsRepeatElement.cpp index e07ed8997c5..2a79497da1f 100644 --- a/extensions/xforms/nsXFormsRepeatElement.cpp +++ b/extensions/xforms/nsXFormsRepeatElement.cpp @@ -293,6 +293,13 @@ protected: */ void MaybeBindAndRefresh(nsIAtom *aName); + /** + * Make sure that an index value is inside the valid index range. + * + * @param aIndex The index value to sanitize + * @param aIsScroll Send scroll events if first or last index? + */ + void SanitizeIndex(PRUint32 *aIndex, PRBool aIsScroll = PR_FALSE); public: NS_DECL_ISUPPORTS_INHERITED @@ -445,32 +452,32 @@ nsXFormsRepeatElement::SetIndex(PRUint32 *aIndex, // Set repeat-index if (mIsParent) { - NS_ASSERTION(mCurrentRepeat, "How can we be a repeat parent without a child?"); + NS_ASSERTION(mCurrentRepeat, + "How can we be a repeat parent without a child?"); // We're the parent of nested repeats, set through the correct repeat return mCurrentRepeat->SetIndex(aIndex, aIsRefresh); } // Do nothing if we are not showing anything - if (mMaxIndex == 0) + if (mMaxIndex == 0) { + // 9.3.6 states that the index position becomes 0 if there are + // no elements in the repeat. + mCurrentIndex = 0; + + // XXXbeaufour: handle scroll-first/last return NS_OK; + } if (aIsRefresh && !mCurrentIndex) { // If we are refreshing, get existing index value from parent - NS_ASSERTION(mParent, "SetIndex with aIsRefresh == PR_TRUE for a non-nested repeat?!"); + NS_ASSERTION(mParent, + "SetIndex with aIsRefresh == PR_TRUE for a non-nested repeat?!"); rv = mParent->GetIndex(aIndex); NS_ENSURE_SUCCESS(rv, rv); } // Check min. and max. value - if (*aIndex < 1) { - *aIndex = 1; - if (!aIsRefresh) - nsXFormsUtils::DispatchEvent(mElement, eEvent_ScrollFirst); - } else if (*aIndex > mMaxIndex) { - *aIndex = mMaxIndex; - if (!aIsRefresh) - nsXFormsUtils::DispatchEvent(mElement, eEvent_ScrollLast); - } + SanitizeIndex(aIndex, PR_TRUE); // Do nothing if setting to existing value if (!aIsRefresh && mCurrentIndex && *aIndex == mCurrentIndex) @@ -490,7 +497,9 @@ nsXFormsRepeatElement::SetIndex(PRUint32 *aIndex, if (mCurrentIndex) { // We had the previous selection, unset directly SetChildIndex(mCurrentIndex, PR_FALSE, aIsRefresh); - } if (mParent) { + } + + if (mParent) { // Selection is in another repeat, inform parent (it will inform the // previous owner of its new state) rv = mParent->SetCurrentRepeat(this, *aIndex); @@ -542,11 +551,8 @@ nsXFormsRepeatElement::GetStartingIndex(PRUint32 *aRes) if (NS_FAILED(rv)) { *aRes = 1; } - if (*aRes < 1) { - *aRes = 1; - } else if (*aRes > mMaxIndex) { - *aRes = mMaxIndex; - } + SanitizeIndex(aRes); + return NS_OK; } @@ -677,6 +683,58 @@ nsXFormsRepeatElement::GetLevel(PRUint32 *aLevel) return NS_OK; } +NS_IMETHODIMP +nsXFormsRepeatElement::HandleNodeInsert(nsIDOMNode *aNode) +{ + NS_ENSURE_STATE(mHTMLElement); + + nsCOMPtr node(do_QueryInterface(aNode)); + NS_ENSURE_STATE(node); + + // XXX, badness^2: If it is a insert we have to refresh before we can + // figure out whether the node is in our nodeset... refactor this so + // repeat actually gets the nodeset in Bind() and then uses it refresh, + // then we can "just" re-evaluate the nodeset, and only refresh if the + // node actually hits this repeat + + // XXX, moreover it is also wrong to refresh at this point. It will happen + // in insert processing (and possibly deferred...) + nsresult rv = Refresh(); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr child; + mHTMLElement->GetFirstChild(getter_AddRefs(child)); + + PRUint32 index = 1; + while (child) { + nsCOMPtr context(do_QueryInterface(child)); + NS_ASSERTION(context, + "repeat child not implementing nsIXFormsContextControl?!"); + + nsAutoString modelID; + PRInt32 position, size; + nsCOMPtr boundNode; + rv = context->GetContext(modelID, getter_AddRefs(boundNode), &position, + &size); + NS_ENSURE_SUCCESS(rv, rv); + PRBool sameNode = PR_FALSE; + node->IsSameNode(boundNode, &sameNode); + if (sameNode) { + rv = SetIndex(&index, PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + + break; + } + + nsCOMPtr tmp; + child->GetNextSibling(getter_AddRefs(tmp)); + child.swap(tmp); + ++index; + } + + return NS_OK; +} + // nsXFormsControl NS_IMETHODIMP @@ -751,7 +809,7 @@ nsXFormsRepeatElement::Refresh() for (PRUint32 i = 1; i < mMaxIndex + 1; ++i) { // Create nsCOMPtr riElement; - rv = domDoc->CreateElementNS(NS_LITERAL_STRING("http://www.w3.org/2002/xforms"), + rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS), NS_LITERAL_STRING("contextcontainer"), getter_AddRefs(riElement)); NS_ENSURE_SUCCESS(rv, rv); @@ -782,7 +840,8 @@ nsXFormsRepeatElement::Refresh() rv = mHTMLElement->AppendChild(riElement, getter_AddRefs(domNode)); NS_ENSURE_SUCCESS(rv, rv); - // Iterate over template children, clone them, and append them to + // Iterate over template children, clone them, and append them to + // \ nsCOMPtr child; rv = mElement->GetFirstChild(getter_AddRefs(child)); NS_ENSURE_SUCCESS(rv, rv); @@ -803,7 +862,12 @@ nsXFormsRepeatElement::Refresh() } } - if (!mParent && !mCurrentIndex && mMaxIndex) { + if (mCurrentIndex) { + // somebody might have been fooling around with our children since last + // refresh (either using delete or through script, so check the index + // value + SanitizeIndex(&mCurrentIndex); + } else if (!mParent && mMaxIndex) { // repeat-index has not been initialized, set it. GetStartingIndex(&mCurrentIndex); } @@ -835,11 +899,14 @@ nsXFormsRepeatElement::SetChildIndex(PRUint32 aPosition, mHTMLElement->GetChildNodes(getter_AddRefs(children)); NS_ENSURE_STATE(children); + PRUint32 index = aPosition - 1; // Indexes are 1-based, the DOM is 0-based; + nsCOMPtr child; - children->Item(aPosition - 1, // Indexes are 1-based, the DOM is 0-based + children->Item(index, getter_AddRefs(child)); - nsCOMPtr repeatItem = do_QueryInterface(child); - NS_ENSURE_STATE(repeatItem); + nsCOMPtr repeatItem(do_QueryInterface(child)); + NS_ASSERTION(repeatItem, + "repeat child not implementing nsIXFormsRepeatItemElement?!"); nsresult rv; PRBool curState; @@ -860,6 +927,23 @@ nsXFormsRepeatElement::SetChildIndex(PRUint32 aPosition, return NS_OK; } +void +nsXFormsRepeatElement::SanitizeIndex(PRUint32 *aIndex, PRBool aIsScroll) +{ + if (!aIndex) + return; + + if (*aIndex < 1) { + *aIndex = 1; + if (aIsScroll) + nsXFormsUtils::DispatchEvent(mElement, eEvent_ScrollFirst); + } else if (*aIndex > mMaxIndex) { + *aIndex = mMaxIndex; + if (aIsScroll) + nsXFormsUtils::DispatchEvent(mElement, eEvent_ScrollLast); + } +} + nsresult nsXFormsRepeatElement::ResetInnerRepeats(nsIDOMNode *aNode, PRBool aIsRefresh)