Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsRepeatElement.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  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsAutoPtr.h"
00040 #include "nsCOMPtr.h"
00041 #include "nsINameSpaceManager.h"
00042 #include "nsISchema.h"
00043 #include "nsIServiceManager.h"
00044 #include "nsMemory.h"
00045 #include "nsString.h"
00046 #include "nsSubstring.h"
00047 
00048 #include "nsIDOM3EventTarget.h"
00049 #include "nsIDOM3Node.h"
00050 #include "nsIDOMDocument.h"
00051 #include "nsIDOMElement.h"
00052 #include "nsIDOMNodeList.h"
00053 #include "nsIDOMXPathResult.h"
00054 #include "nsIContent.h"
00055 #include "nsIBindingManager.h"
00056 
00057 #include "nsXFormsControlStub.h"
00058 #include "nsIXFormsContextControl.h"
00059 #include "nsIXFormsRepeatElement.h"
00060 #include "nsIXFormsRepeatUIElement.h"
00061 #include "nsIXFormsRepeatItemElement.h"
00062 #include "nsXFormsAtoms.h"
00063 #include "nsXFormsModelElement.h"
00064 #include "nsXFormsUtils.h"
00065 #include "nsXFormsDelegateStub.h"
00066 
00067 #ifdef DEBUG
00068 //#define DEBUG_XF_REPEAT
00069 #endif
00070 
00189 class nsXFormsRepeatElement : public nsXFormsDelegateStub,
00190                               public nsIXFormsRepeatElement
00191 {
00192 protected:
00200   PRUint32 mCurrentIndex;
00201 
00205   PRUint32 mMaxIndex;
00206 
00208   nsCOMPtr<nsIXFormsRepeatElement> mParent;
00209 
00215   PRUint32 mLevel;
00216 
00220   PRUint32 mCurrentRowCount;
00221   
00225   PRPackedBool mAddingChildren;
00226 
00230   PRPackedBool mIsParent;
00231 
00238   PRPackedBool mIsAttributeBased;
00239 
00248   nsCOMPtr<nsIDOMNode> mAttrBasedRepeatNode;
00249 
00253   nsCOMPtr<nsIXFormsRepeatElement> mCurrentRepeat;
00254 
00258   nsCOMArray<nsIXFormsControl>     mIndexUsers;
00259   
00269   nsresult GetIntAttr(const nsAString &aName,
00270                       PRInt32         *aVal,
00271                       const PRUint16   aType);
00272 
00281   nsresult SetChildIndex(PRUint32 aPosition,
00282                          PRBool   aState,
00283                          PRBool   aIsRefresh = PR_FALSE);
00284 
00292   nsresult ResetInnerRepeats(nsIDOMNode *aNode,
00293                              PRBool      aIsRefresh);
00294 
00302   nsresult CloneNode(nsIDOMNode *aSrc, nsIDOMNode **aTarget);
00303 
00304   PRBool IsBindingAttribute(const nsIAtom *aAttr) const;
00305 
00312   void SanitizeIndex(PRUint32 *aIndex, PRBool aIsScroll = PR_FALSE);
00313 
00318   nsresult UnrollRows(nsIDOMXPathResult *aNodeset);
00319 
00323   already_AddRefed<nsIDOMElement> GetAnonymousContent();
00324 
00330   nsresult InsertTemplateContent(nsIDOMNode *aNode);
00331 
00332   // nsXFormsControlStub override
00333   virtual void AfterSetAttribute(nsIAtom *aName);
00334 
00342   nsRepeatState UpdateRepeatState(nsIDOMNode *aParent);
00343 
00344 public:
00345   NS_DECL_ISUPPORTS_INHERITED
00346 
00347   // nsIXTFBindableElement overrides
00348   NS_IMETHOD OnCreated(nsIXTFBindableElementWrapper *aWrapper);
00349 
00350   // nsIXTFElement overrides
00351   NS_IMETHOD OnDestroyed();
00352   NS_IMETHOD BeginAddingChildren();
00353   NS_IMETHOD DoneAddingChildren();
00354 
00355   // nsIXFormsControl
00356   NS_IMETHOD Bind(PRBool *aContextChanged);
00357   NS_IMETHOD Refresh();
00358   NS_IMETHOD TryFocus(PRBool* aOK);
00359   NS_IMETHOD IsEventTarget(PRBool *aOK);
00360   NS_IMETHOD GetUsesSingleNodeBinding(PRBool *aUsesSNB);
00361 
00362   // nsIXFormsRepeatElement
00363   NS_DECL_NSIXFORMSREPEATELEMENT
00364 
00365   // nsXFormsRepeatElement
00366   nsXFormsRepeatElement() :
00367     mCurrentIndex(0),
00368     mMaxIndex(0),
00369     mLevel(1),
00370     mCurrentRowCount(0),
00371     mAddingChildren(PR_FALSE),
00372     mIsParent(PR_FALSE),
00373     mIsAttributeBased(PR_FALSE)
00374     {}
00375 
00376 #ifdef DEBUG_smaug
00377   virtual const char* Name() { return "repeat"; }
00378 #endif
00379 };
00380 
00381 NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsRepeatElement,
00382                              nsXFormsDelegateStub,
00383                              nsIXFormsRepeatElement)
00384 
00385 // nsIXTFBindableElement
00386 NS_IMETHODIMP
00387 nsXFormsRepeatElement::OnCreated(nsIXTFBindableElementWrapper *aWrapper)
00388 {
00389 #ifdef DEBUG_XF_REPEAT
00390   printf("nsXFormsRepeatElement::OnCreated()\n");
00391 #endif
00392 
00393   nsresult rv = nsXFormsDelegateStub::OnCreated(aWrapper);
00394   NS_ENSURE_SUCCESS(rv, rv);
00395 
00396   aWrapper->SetNotificationMask(kStandardNotificationMask |
00397                                 nsIXTFElement::NOTIFY_BEGIN_ADDING_CHILDREN |
00398                                 nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN);
00399 
00400   return NS_OK;
00401 }
00402 
00403 // nsIXTFElement
00404 NS_IMETHODIMP
00405 nsXFormsRepeatElement::OnDestroyed()
00406 {
00407   mIndexUsers.Clear();
00408 
00409   return nsXFormsDelegateStub::OnDestroyed();
00410 }
00411 
00412 NS_IMETHODIMP
00413 nsXFormsRepeatElement::BeginAddingChildren()
00414 {
00415   mAddingChildren = PR_TRUE;
00416   
00417   return NS_OK;
00418 }
00419 
00420 NS_IMETHODIMP
00421 nsXFormsRepeatElement::DoneAddingChildren()
00422 {
00423   mAddingChildren = PR_FALSE;
00424 
00425   return NS_OK;
00426 }
00427 
00428 // nsIXFormsRepeatElement
00429 
00430 NS_IMETHODIMP
00431 nsXFormsRepeatElement::SetIndex(PRUint32 *aIndex,
00432                                 PRBool    aIsRefresh)
00433 {
00434   NS_ENSURE_ARG(aIndex);
00435 #ifdef DEBUG_XF_REPEAT
00436   printf("\tSetindex to %d (current: %d, max: %d), aIsRefresh=%d\n",
00437          *aIndex, mCurrentIndex, mMaxIndex, aIsRefresh);
00438 #endif
00439 
00440   nsresult rv;
00441 
00442   // Set repeat-index
00443   if (mIsParent) {
00444     NS_ASSERTION(mCurrentRepeat,
00445                  "How can we be a repeat parent without a child?");
00446     // We're the parent of nested repeats, set through the correct repeat
00447     return mCurrentRepeat->SetIndex(aIndex, aIsRefresh);
00448   }
00449   
00450   // Do nothing if we are not showing anything
00451   if (mMaxIndex == 0) {
00452     SanitizeIndex(aIndex, PR_TRUE);
00453     mCurrentIndex = *aIndex;
00454     return NS_OK;
00455   }
00456 
00457   if (aIsRefresh && !mCurrentIndex) {
00458     // If we are refreshing, get existing index value from parent
00459     NS_ASSERTION(mParent,
00460                  "SetIndex with aIsRefresh == PR_TRUE for a non-nested repeat?!");
00461     rv = mParent->GetIndex(aIndex);
00462     NS_ENSURE_SUCCESS(rv, rv);
00463   }
00464 
00465   // Check min. and max. value
00466   SanitizeIndex(aIndex, PR_TRUE);
00467 
00468   // Do nothing if setting to existing value
00469   if (!aIsRefresh && mCurrentIndex && *aIndex == mCurrentIndex)
00470     return NS_OK;
00471   
00472   
00473 #ifdef DEBUG_XF_REPEAT
00474   printf("\tWill set index to %d\n",
00475          *aIndex);
00476 #endif
00477 
00478   // Set the repeat-index
00479   rv = SetChildIndex(*aIndex, PR_TRUE, aIsRefresh);
00480   NS_ENSURE_SUCCESS(rv, rv);
00481   
00482   // Unset previous repeat-index
00483   if (mCurrentIndex) {
00484     // We had the previous selection, unset directly
00485     SetChildIndex(mCurrentIndex, PR_FALSE, aIsRefresh);
00486   }
00487   
00488   if (mParent) {
00489     // Selection is in another repeat, inform parent (it will inform the
00490     // previous owner of its new state)
00491     rv = mParent->SetCurrentRepeat(this, *aIndex);
00492     NS_ENSURE_SUCCESS(rv, rv);
00493   }
00494 
00495   // Set current index to new value
00496   mCurrentIndex = *aIndex;
00497 
00498   // Inform of index change
00499   mParent ? mParent->IndexHasChanged() : IndexHasChanged();
00500 
00501   return NS_OK;
00502 }
00503 
00504 NS_IMETHODIMP
00505 nsXFormsRepeatElement::GetIndex(PRUint32 *aIndex)
00506 {
00507   NS_ENSURE_ARG(aIndex);
00508   if (mIsParent) {
00509     if (!mCurrentRepeat) {
00510       *aIndex = 0;
00511       return NS_OK;
00512     }
00513     return mCurrentRepeat->GetIndex(aIndex);
00514   }
00515 
00516   *aIndex = mCurrentIndex;
00517   return NS_OK;
00518 }
00519 
00520 NS_IMETHODIMP
00521 nsXFormsRepeatElement::Deselect(void)
00522 {
00523   if (!mCurrentIndex)
00524     return NS_OK;
00525   
00526   nsresult rv = SetChildIndex(mCurrentIndex, PR_FALSE);
00527   if (NS_SUCCEEDED(rv)) {
00528     mCurrentIndex = 0;
00529   }
00530   return rv;
00531 }
00532 
00533 NS_IMETHODIMP
00534 nsXFormsRepeatElement::GetStartingIndex(PRUint32 *aRes)
00535 {
00536   NS_ENSURE_ARG(aRes);
00537 
00538   nsresult rv = GetIntAttr(NS_LITERAL_STRING("startindex"),
00539                            (PRInt32*) aRes,
00540                            nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER);
00541   if (NS_FAILED(rv)) {
00542     *aRes = 1;
00543   }
00544   SanitizeIndex(aRes);
00545 
00546   return NS_OK;
00547 }
00548 
00549 
00550 NS_IMETHODIMP
00551 nsXFormsRepeatElement::SetCurrentRepeat(nsIXFormsRepeatElement *aRepeat,
00552                                         PRUint32                aIndex)
00553 {
00554   // Deselect the previous owner
00555   if (mCurrentRepeat && aRepeat != mCurrentRepeat) {
00556     nsresult rv = mCurrentRepeat->Deselect();
00557     NS_ENSURE_SUCCESS(rv, rv);
00558   }
00559   mCurrentRepeat = aRepeat;
00560 
00561   // Check aIndex. If it is 0, we should intialize to the starting index
00562   if (!aIndex) {
00563     GetStartingIndex(&aIndex);
00564   }
00565   mCurrentIndex = aIndex;
00566 
00567   return NS_OK;
00568 }
00569 
00570 NS_IMETHODIMP
00571 nsXFormsRepeatElement::GetCurrentRepeatRow(nsIDOMNode **aRow)
00572 {
00573   if (mCurrentRepeat) {
00574     // nested repeats
00575     return mCurrentRepeat->GetCurrentRepeatRow(aRow);
00576   }
00577 
00578   nsCOMPtr<nsIDOMElement> anon = GetAnonymousContent();
00579   NS_ENSURE_STATE(anon);
00580 
00581   nsCOMPtr<nsIDOMNodeList> children;
00582   anon->GetChildNodes(getter_AddRefs(children));
00583   NS_ENSURE_STATE(children);
00584 
00585   nsCOMPtr<nsIDOMNode> child;
00586   children->Item(mCurrentIndex - 1, // Indexes are 1-based, the DOM is 0-based
00587                  getter_AddRefs(child));
00588   NS_IF_ADDREF(*aRow = child);
00589   return NS_OK;
00590 }
00591 
00592 NS_IMETHODIMP
00593 nsXFormsRepeatElement::AddIndexUser(nsIXFormsControl *aControl)
00594 {
00595   nsresult rv = NS_OK;
00596   if (mIndexUsers.IndexOf(aControl) == -1 && !mIndexUsers.AppendObject(aControl))
00597     rv = NS_ERROR_FAILURE;
00598   
00599   return rv;
00600 }
00601 
00602 NS_IMETHODIMP
00603 nsXFormsRepeatElement::RemoveIndexUser(nsIXFormsControl *aControl)
00604 {
00605   return mIndexUsers.RemoveObject(aControl) ? NS_OK : NS_ERROR_FAILURE;
00606 }
00607 
00608 NS_IMETHODIMP
00609 nsXFormsRepeatElement::IndexHasChanged()
00610 {
00611   NS_ENSURE_STATE(mModel);
00614 
00615   // copy the index array, as index users might add/remove themselves when
00616   // they are rebound and refreshed().
00617   nsCOMArray<nsIXFormsControl> indexes(mIndexUsers);
00618 
00619   nsresult rv;
00620   for (PRInt32 i = 0; i < indexes.Count(); ++i) {
00621     nsCOMPtr<nsIXFormsControl> control = indexes[i];
00622     rv = mModel->ForceRebind(control);
00623     NS_ENSURE_SUCCESS(rv, rv);
00624   }
00625 
00626   return NS_OK;
00627 }
00628 
00629 // NB: CloneNode() assumes that this always succeeds
00630 NS_IMETHODIMP
00631 nsXFormsRepeatElement::GetIsParent(PRBool *aIsParent)
00632 {
00633   NS_ENSURE_ARG(aIsParent);
00634   *aIsParent = mIsParent;
00635   return NS_OK;
00636 }
00637 
00638 // NB: CloneNode() assumes that this always succeeds
00639 NS_IMETHODIMP
00640 nsXFormsRepeatElement::SetIsParent(PRBool aIsParent)
00641 {
00642   mIsParent = aIsParent;
00643   return NS_OK;
00644 }
00645 
00646 // NB: CloneNode() assumes that this always succeeds
00647 NS_IMETHODIMP
00648 nsXFormsRepeatElement::SetParent(nsIXFormsRepeatElement *aParent)
00649 {
00650   mParent = aParent;
00651   // We're an inner repeat owned by a parent, let it control whether we are
00652   // selected or not.
00653   Deselect();
00654   return NS_OK;
00655 }
00656 
00657 NS_IMETHODIMP
00658 nsXFormsRepeatElement::GetParent(nsIXFormsRepeatElement **aParent)
00659 {
00660   NS_ENSURE_ARG_POINTER(aParent);
00661   NS_IF_ADDREF(*aParent = mParent);
00662   return NS_OK;
00663 }
00664 
00665 NS_IMETHODIMP
00666 nsXFormsRepeatElement::SetLevel(PRUint32 aLevel)
00667 {
00668   mLevel = aLevel;
00669   return NS_OK;
00670 }
00671 
00672 NS_IMETHODIMP
00673 nsXFormsRepeatElement::GetLevel(PRUint32 *aLevel)
00674 {
00675   NS_ENSURE_ARG(aLevel);
00676   *aLevel = mLevel;
00677   return NS_OK;
00678 }
00679 
00680 NS_IMETHODIMP
00681 nsXFormsRepeatElement::HandleNodeInsert(nsIDOMNode *aNode)
00682 {
00683   nsCOMPtr<nsIDOMElement> anon = GetAnonymousContent();
00684   if (!anon) {
00685     return NS_OK;
00686   }
00687 
00688   nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(aNode));
00689   NS_ENSURE_STATE(node);
00690 
00691   // XXX, badness^2: If it is a insert we have to refresh before we can
00692   // figure out whether the node is in our nodeset... refactor this so
00693   // repeat actually gets the nodeset in Bind() and then uses it refresh,
00694   // then we can "just" re-evaluate the nodeset, and only refresh if the
00695   // node actually hits this repeat
00696 
00697   // XXX, moreover it is also wrong to refresh at this point. It will happen
00698   // in insert processing (and possibly deferred...)
00699   nsresult rv = Refresh();
00700   NS_ENSURE_SUCCESS(rv, rv);
00701 
00702   nsCOMPtr<nsIDOMNode> child;
00703   anon->GetFirstChild(getter_AddRefs(child));
00704 
00705   PRUint32 index = 1;
00706   while (child) {
00707     nsCOMPtr<nsIXFormsContextControl> context(do_QueryInterface(child));
00708     NS_ASSERTION(context,
00709                  "repeat child not implementing nsIXFormsContextControl?!");
00710 
00711     nsAutoString modelID;
00712     PRInt32 position, size;
00713     nsCOMPtr<nsIDOMNode> boundNode;
00714     rv = context->GetContext(modelID, getter_AddRefs(boundNode), &position,
00715                              &size);
00716     NS_ENSURE_SUCCESS(rv, rv);
00717     PRBool sameNode = PR_FALSE;
00718     node->IsSameNode(boundNode, &sameNode);
00719     if (sameNode) {
00720       rv = SetIndex(&index, PR_FALSE);
00721       NS_ENSURE_SUCCESS(rv, rv);
00722 
00723       break;
00724     }
00725 
00726     nsCOMPtr<nsIDOMNode> tmp;
00727     child->GetNextSibling(getter_AddRefs(tmp));
00728     child.swap(tmp);
00729     ++index;
00730   }
00731 
00732   return NS_OK;
00733 }
00734 
00735 // nsXFormsControl
00736 
00737 NS_IMETHODIMP
00738 nsXFormsRepeatElement::Bind(PRBool *aContextChanged)
00739 {
00740 #ifdef DEBUG_XF_REPEAT
00741   printf("nsXFormsRepeatElement::Bind()\n");
00742 #endif
00743 
00744   NS_ENSURE_ARG(aContextChanged);
00745 
00746   if (!nsXFormsUtils::IsDocumentReadyForBind(mElement)) {
00747     nsXFormsModelElement::DeferElementBind(this);
00748     *aContextChanged = PR_FALSE;
00749     return NS_OK_XFORMS_DEFERRED;
00750   }
00751 
00752   nsresult rv = BindToModel();
00753   NS_ENSURE_SUCCESS(rv, rv);
00754 
00755   *aContextChanged = PR_TRUE;
00756 
00757   return NS_OK;
00758 }
00759 
00760 nsresult
00761 nsXFormsRepeatElement::InsertTemplateContent(nsIDOMNode *aNode)
00762 {
00763   NS_ENSURE_ARG_POINTER(aNode);
00764 
00765   nsresult rv;
00766 
00767   // If we are attribute based we need to clone our parent (the element with
00768   // the repeat-* attributes), and insert that as the first child.
00769   if (mIsAttributeBased) {
00770     nsCOMPtr<nsIDOMNode> clonedNode;
00771     rv = mAttrBasedRepeatNode->CloneNode(PR_TRUE, getter_AddRefs(clonedNode));
00772     NS_ENSURE_SUCCESS(rv, rv);
00773 
00774     nsCOMPtr<nsIDOMNode> stubNode;
00775     return aNode->AppendChild(clonedNode, getter_AddRefs(stubNode));
00776   }
00777 
00778   nsCOMPtr<nsIDOMDocument> domDocument;
00779   mElement->GetOwnerDocument(getter_AddRefs(domDocument));
00780   NS_ENSURE_STATE(domDocument);
00781 
00782   nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
00783   nsCOMPtr<nsIBindingManager> bindingMgr(document->BindingManager());
00784   NS_ENSURE_STATE(bindingMgr);
00785 
00786   nsCOMPtr<nsIContent> content(do_QueryInterface(mElement));
00787   NS_ENSURE_STATE(content);
00788 
00789   // Get a list of all explicit children, including any children that may have
00790   // been inserted via XBL insertion points.
00791   nsCOMPtr<nsIDOMNodeList> children;
00792   bindingMgr->GetContentListFor(content, getter_AddRefs(children));
00793 
00794   PRUint32 length = 0;
00795   children->GetLength(&length);
00796 
00797   for (PRUint32 i = 0; i < length; i++) {
00798     nsCOMPtr<nsIDOMNode> node;
00799     rv = children->Item(i, getter_AddRefs(node));
00800     NS_ENSURE_SUCCESS(rv, rv);
00801 
00802     nsCOMPtr<nsIDOMNode> clonedNode;
00803     rv = CloneNode(node, getter_AddRefs(clonedNode));
00804     NS_ENSURE_SUCCESS(rv, rv);
00805 
00806     nsCOMPtr<nsIDOMNode> stubNode;
00807     rv = aNode->AppendChild(clonedNode, getter_AddRefs(stubNode));
00808     NS_ENSURE_SUCCESS(rv, rv);
00809   }
00810 
00811   return NS_OK;
00812 }
00813 
00814 // XXX: If repeat is moved to a new position, we need to clear the unrolled
00815 // content and clear mAttrBasedRepeatNode.
00816 nsresult
00817 nsXFormsRepeatElement::UnrollRows(nsIDOMXPathResult *aNodeset)
00818 {
00819 #ifdef DEBUG_XF_REPEAT
00820   printf("nsXFormsRepeatElement::UnrollRows()\n");
00821 #endif
00822   NS_ENSURE_STATE(mElement);
00823 
00824   nsCOMPtr<nsIDOMElement> anon = GetAnonymousContent();
00825   if (!anon) {
00826     return NS_OK;
00827   }
00828 
00829   nsresult rv;
00830   if (!aNodeset || !mModel) {
00831     mMaxIndex = 0;
00832   } else {
00833     PRUint32 contextSize;
00834     rv = aNodeset->GetSnapshotLength(&contextSize);
00835     NS_ENSURE_SUCCESS(rv, rv);
00836     mMaxIndex = contextSize;
00837   }
00838 
00839 #ifdef DEBUG_XF_REPEAT
00840   printf("\tmMaxIndex: %d, mCurrentRowCount: %d\n",
00841          mMaxIndex, mCurrentRowCount);
00842 #endif
00843 
00844   // STEP 1: Remove rows
00845   nsCOMPtr<nsIDOMNode> tmp;
00846   if (mMaxIndex < mCurrentRowCount) {
00847     for (PRUint32 i = mMaxIndex; i < mCurrentRowCount; ++i) {
00848       nsCOMPtr<nsIDOMNode> lastChild;
00849       rv = anon->GetLastChild(getter_AddRefs(lastChild));
00850       NS_ENSURE_SUCCESS(rv, rv);
00851     
00852       rv = anon->RemoveChild(lastChild, getter_AddRefs(tmp));
00853       NS_ENSURE_SUCCESS(rv, rv);
00854     }
00855   } else if (mMaxIndex > mCurrentRowCount) {
00856     // STEP 2: Add rows
00857     nsCOMPtr<nsIDOMDocument> domDoc;
00858     rv = anon->GetOwnerDocument(getter_AddRefs(domDoc));
00859     NS_ENSURE_SUCCESS(rv, rv);
00860     for (PRUint32 i = mCurrentRowCount; i < mMaxIndex; ++i) {
00861       // Create <contextcontainer>
00862       nsCOMPtr<nsIDOMElement> container;
00863       rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
00864                                    NS_LITERAL_STRING("contextcontainer"),
00865                                    getter_AddRefs(container));
00866       NS_ENSURE_SUCCESS(rv, rv);
00867   
00868       container->SetAttribute(NS_LITERAL_STRING("class"),
00869                               NS_LITERAL_STRING("xf-repeat-item"));
00870 
00871       rv = anon->AppendChild(container, getter_AddRefs(tmp));
00872       NS_ENSURE_SUCCESS(rv, rv);
00873     }
00874   }
00875 
00876   // Repeat has no content
00877   if (!mMaxIndex) {
00878     mCurrentRowCount = 0;
00879     return NS_OK;
00880   }
00881   
00882   // STEP 3: Update context on rows
00883   nsCOMPtr<nsIDOMNode> child;
00884   rv = anon->GetFirstChild(getter_AddRefs(child));
00885   NS_ENSURE_SUCCESS(rv, rv);
00886 
00887   for (PRUint32 i = 0; i < mMaxIndex; ++i) {
00888     NS_ASSERTION(child, "Unrolled content does not match index size?!");
00889     // Get context node
00890     nsCOMPtr<nsIDOMNode> contextNode;
00891     rv = aNodeset->SnapshotItem(i, getter_AddRefs(contextNode));
00892     NS_ENSURE_SUCCESS(rv, rv);
00893 
00894     // Set context node, position, and size
00895     nsCOMPtr<nsIXFormsContextControl> childContext = do_QueryInterface(child);
00896     NS_ASSERTION(childContext,
00897                  "content child not implementing nsIXFormsContextControl?!");
00898 
00899     rv = childContext->SetContext(contextNode, i + 1, mMaxIndex);
00900     NS_ENSURE_SUCCESS(rv, rv);
00901 
00902     // Next child
00903     rv = child->GetNextSibling(getter_AddRefs(tmp));
00904     NS_ENSURE_SUCCESS(rv, rv);
00905     tmp.swap(child);
00906   }
00907 
00908   // Rows deleted, nothing more to do
00909   if (mCurrentRowCount >= mMaxIndex) {
00910     mCurrentRowCount = mMaxIndex;
00911     return NS_OK;
00912   }
00913 
00914   // STEP 4: Insert template content into newly created rows
00915 
00916   // If we are attribute based, prepare a clone of our parent
00917   if (mIsAttributeBased && !mAttrBasedRepeatNode) {
00918     nsCOMPtr<nsIContent> content(do_QueryInterface(mElement));
00919     NS_ENSURE_STATE(content);
00920 
00921     nsCOMPtr<nsIContent> templateContent = content->GetBindingParent();
00922     NS_ENSURE_STATE(templateContent);
00923 
00924     nsCOMPtr<nsIDOMNode> templateNode(do_QueryInterface(templateContent));
00925     NS_ENSURE_STATE(templateNode);
00926 
00927     rv = CloneNode(templateNode, getter_AddRefs(mAttrBasedRepeatNode));
00928     NS_ENSURE_SUCCESS(rv, rv);
00929 
00930     nsCOMPtr<nsIDOMElement> clonedElm(do_QueryInterface(mAttrBasedRepeatNode));
00931     NS_ENSURE_STATE(clonedElm);
00932 
00933     // Remove all possible repeat-* attributes
00934     clonedElm->RemoveAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
00935                                  NS_LITERAL_STRING("repeat-model"));
00936     clonedElm->RemoveAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
00937                                  NS_LITERAL_STRING("repeat-bind"));
00938     clonedElm->RemoveAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
00939                                  NS_LITERAL_STRING("repeat-nodeset"));
00940     clonedElm->RemoveAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
00941                                  NS_LITERAL_STRING("repeat-startindex"));
00942     clonedElm->RemoveAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
00943                                  NS_LITERAL_STRING("repeat-number"));
00944   }
00945 
00946   nsCOMPtr<nsIDOMNodeList> containerList;
00947   rv = anon->GetChildNodes(getter_AddRefs(containerList));
00948   NS_ENSURE_SUCCESS(rv, rv);
00949   for (PRUint32 j = mCurrentRowCount; j < mMaxIndex; ++j) {
00950     nsCOMPtr<nsIDOMNode> container;
00951     rv = containerList->Item(j, getter_AddRefs(container));
00952     NS_ENSURE_SUCCESS(rv, rv);
00953 
00954     rv = InsertTemplateContent(container);
00955     NS_ENSURE_SUCCESS(rv, rv);
00956   }
00957   
00958   mCurrentRowCount = mMaxIndex;
00959   return NS_OK;
00960 }
00961 
00962 NS_IMETHODIMP
00963 nsXFormsRepeatElement::Refresh()
00964 {
00965 #ifdef DEBUG_XF_REPEAT
00966   printf("nsXFormsRepeatElement::Refresh()\n");
00967 #endif
00968 
00969   if (mAddingChildren || mIsParent)
00970     return NS_OK;
00971 
00972   nsPostRefresh postRefresh = nsPostRefresh();
00973 
00974   PRUint32 oldIndex = mCurrentIndex;
00975 
00983 
00984 
00985   // Get the nodeset we are bound to
00986   nsresult rv;
00987   nsCOMPtr<nsIDOMXPathResult> result;
00988   rv = ProcessNodeBinding(NS_LITERAL_STRING("nodeset"),
00989                           nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
00990                           getter_AddRefs(result));
00991   NS_ENSURE_SUCCESS(rv, rv);
00992 
00993   // Unroll the repeat rows
00994   rv = UnrollRows(result);
00995   NS_ENSURE_SUCCESS(rv, rv);
00996 
00997   // Maintain the index
00998   if (mCurrentIndex || !mMaxIndex) {
00999     // Somebody might have been fooling around with our children since last
01000     // refresh (either using delete or through script). Or we might have an
01001     // empty nodeset. So fix the index value.
01002     SanitizeIndex(&mCurrentIndex);
01003   } else if (mMaxIndex) {
01004     // repeat-index has not been initialized, set it.
01005     if (!mParent) {
01006       GetStartingIndex(&mCurrentIndex);
01007       // Inform listeners of initial index value
01008       IndexHasChanged();
01009     } else if (mLevel > 1) {
01010       // Set repeat-index for inner repeats. If parent <contextcontainer/>
01011       // element is selected then mCurrentIndex is setted on starting index.
01012 
01013       nsCOMPtr<nsIDOMNode> temp = mElement;
01014       nsCOMPtr<nsIDOMNode> parent;
01015       nsCOMPtr<nsIXFormsRepeatItemElement> context;
01016 
01017       while (!context) {
01018         rv = temp->GetParentNode(getter_AddRefs(parent));
01019         NS_ENSURE_SUCCESS(rv, rv);
01020         if (!parent)
01021           break;
01022         context = do_QueryInterface(parent);
01023         temp.swap(parent);
01024       }
01025 
01026       if (context) {
01027         PRBool hasIndex = PR_FALSE;
01028         context->GetIndexState(&hasIndex);
01029         if (hasIndex) {
01030           PRUint32 index = 0;
01031           GetStartingIndex(&index);
01032           SetIndex(&index, PR_FALSE);
01033         }
01034       }
01035       return NS_OK;
01036     }
01037   }
01038 
01039   // If we have the repeat-index, set it.
01040   if (mCurrentIndex) {
01041     SetChildIndex(mCurrentIndex, PR_TRUE, PR_TRUE);
01042   }
01043 
01044   if (mCurrentIndex != oldIndex) {
01045     mParent ? mParent->IndexHasChanged() : IndexHasChanged();
01046   }
01047 
01048   return NS_OK;
01049 }
01050 
01051 // nsXFormsRepeatElement
01052 
01053 nsresult
01054 nsXFormsRepeatElement::SetChildIndex(PRUint32 aPosition,
01055                                      PRBool   aState,
01056                                      PRBool   aIsRefresh)
01057 {
01058 #ifdef DEBUG_XF_REPEAT
01059   printf("\tTrying to set index #%d to state '%d', aIsRefresh=%d\n",
01060          aPosition, aState, aIsRefresh);
01061 #endif
01062 
01063   nsCOMPtr<nsIDOMElement> anon = GetAnonymousContent();
01064   if (!anon)
01065     return NS_OK;
01066 
01067   nsCOMPtr<nsIDOMNodeList> children;
01068   anon->GetChildNodes(getter_AddRefs(children));
01069   NS_ENSURE_STATE(children);
01070 
01071   PRUint32 index = aPosition - 1; // Indexes are 1-based, the DOM is 0-based;
01072 
01073   nsCOMPtr<nsIDOMNode> child;
01074   children->Item(index,
01075                  getter_AddRefs(child));
01076   nsCOMPtr<nsIXFormsRepeatItemElement> repeatItem(do_QueryInterface(child));
01077   NS_ASSERTION(repeatItem,
01078                "repeat child not implementing nsIXFormsRepeatItemElement?!");
01079 
01080   nsresult rv;
01081   PRBool curState;
01082   rv = repeatItem->GetIndexState(&curState);
01083   NS_ENSURE_SUCCESS(rv, rv);
01084 
01085   if (curState != aState) {
01086     rv = repeatItem->SetIndexState(aState);
01087     NS_ENSURE_SUCCESS(rv, rv);
01088 
01089     if (aState) {
01090       // Reset inner repeats
01091       rv = ResetInnerRepeats(child, aIsRefresh);
01092       NS_ENSURE_SUCCESS(rv, rv);
01093     }
01094   }
01095 
01096   return NS_OK;
01097 }
01098 
01099 void
01100 nsXFormsRepeatElement::SanitizeIndex(PRUint32 *aIndex, PRBool aIsScroll)
01101 {
01102   if (!aIndex)
01103     return;
01104 #ifdef DEBUG_XF_REPEAT
01105   printf("nsXFormsRepeatElement::SanitizeIndex()\n");
01106   printf("\tCurrent index: %d\n", *aIndex);
01107 #endif
01108   if (*aIndex < 1) {
01109     *aIndex = mMaxIndex ? 1 : 0;
01110     if (aIsScroll)
01111       nsXFormsUtils::DispatchEvent(mElement, eEvent_ScrollFirst);
01112   } else if (*aIndex > mMaxIndex) {
01113     *aIndex = mMaxIndex;
01114     if (aIsScroll)
01115       nsXFormsUtils::DispatchEvent(mElement, eEvent_ScrollLast);
01116   }
01117 
01118 #ifdef DEBUG_XF_REPEAT
01119   printf("\tNew index: %d\n", *aIndex);
01120 #endif
01121 }
01122 
01123 nsresult
01124 nsXFormsRepeatElement::ResetInnerRepeats(nsIDOMNode *aNode,
01125                                          PRBool      aIsRefresh)
01126 {
01127 #ifdef DEBUG_XF_REPEAT
01128   printf("\taIsRefresh: %d\n",
01129          aIsRefresh);
01130 #endif
01131 
01132   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
01133 
01134   if (!element)
01135     return NS_ERROR_FAILURE;
01136 
01137   nsCOMPtr<nsIDOMNodeList> nodeList;
01138   nsresult rv;
01139 
01140   nsCOMPtr<nsIDOMElement> anon = GetAnonymousContent();
01141   NS_ENSURE_STATE(anon);
01142 
01143   rv = element->GetElementsByTagNameNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
01144                                        NS_LITERAL_STRING("repeat"),
01145                                        getter_AddRefs(nodeList));
01146   NS_ENSURE_SUCCESS(rv, rv);
01147 
01148   PRUint32 childCount = 0;
01149   nodeList->GetLength(&childCount);
01150   nsCOMPtr<nsIDOMNode> node;
01151   nsCOMPtr<nsIXFormsRepeatElement> repeat;
01152   for (PRUint32 i = 0; i < childCount; ++i) {
01153     nodeList->Item(i, getter_AddRefs(node));
01154     repeat = do_QueryInterface(node);
01155     NS_ENSURE_STATE(repeat);
01156     PRUint32 level;
01157     repeat->GetLevel(&level);
01158     if (level == mLevel + 1) {
01159       PRUint32 index;
01160       repeat->GetStartingIndex(&index);
01161       repeat->SetIndex(&index, aIsRefresh);
01162     }
01163   }
01164   
01165   return NS_OK;
01166 }
01167 
01168 nsresult
01169 nsXFormsRepeatElement::CloneNode(nsIDOMNode  *aSrc,
01170                                  nsIDOMNode **aTarget)
01171 {
01172   NS_ENSURE_ARG(aSrc);
01173   NS_ENSURE_ARG_POINTER(aTarget);
01174 
01175   // Clone aSrc
01176   nsresult rv;
01177   rv = aSrc->CloneNode(PR_FALSE, aTarget);
01178   NS_ENSURE_SUCCESS(rv, rv);
01179 
01180   // Check whether we have cloned a repeat
01181   if (nsXFormsUtils::IsXFormsElement(aSrc, NS_LITERAL_STRING("repeat"))) {
01182     nsCOMPtr<nsIXFormsRepeatElement> repSource = do_QueryInterface(aSrc);
01183     NS_ENSURE_STATE(repSource);
01184     nsCOMPtr<nsIXFormsRepeatElement> repClone = do_QueryInterface(*aTarget);
01185     NS_ENSURE_STATE(repClone);
01186 
01187     // Find top-most parent of these repeats
01188     nsCOMPtr<nsIXFormsRepeatElement> parent = repSource;
01189     nsCOMPtr<nsIXFormsRepeatElement> temp;
01190 
01191     rv = parent->GetParent(getter_AddRefs(temp));
01192     NS_ENSURE_SUCCESS(rv, rv);
01193     while (temp) {
01194       temp.swap(parent);
01195       rv = parent->GetParent(getter_AddRefs(temp));
01196       NS_ENSURE_SUCCESS(rv, rv);
01197     }
01198 
01199     // Set parent and level on clone
01200     PRUint32 level;
01201     repSource->GetLevel(&level);
01202     repClone->SetLevel(level + 1);
01203     repClone->SetParent(parent);
01204 
01205     // Inform parent of new status, if it does not know already
01206     PRBool isParent;
01207     parent->GetIsParent(&isParent);
01208     if (!isParent) {
01209       rv = parent->SetCurrentRepeat(repClone, 0);
01210       NS_ENSURE_SUCCESS(rv, rv);
01211       parent->SetIsParent(PR_TRUE);
01212     }
01213   }
01214 
01215   // Clone children of aSrc
01216   nsCOMPtr<nsIDOMNode> tmp;
01217   nsCOMPtr<nsIDOMNodeList> childNodes;
01218   aSrc->GetChildNodes(getter_AddRefs(childNodes));
01219 
01220   PRUint32 count = 0;
01221   if (childNodes)
01222     childNodes->GetLength(&count);
01223 
01224   for (PRUint32 i = 0; i < count; ++i) {
01225     nsCOMPtr<nsIDOMNode> child;
01226     childNodes->Item(i, getter_AddRefs(child));
01227     
01228     if (child) {
01229       nsCOMPtr<nsIDOMNode> clone;
01230       CloneNode(child, getter_AddRefs(clone));
01231       if (clone) {
01232         rv = (*aTarget)->AppendChild(clone, getter_AddRefs(tmp));
01233         NS_ENSURE_SUCCESS(rv, rv);
01234       }
01235     }
01236   }
01237 
01238   return NS_OK;
01239 }
01240 
01241 NS_IMETHODIMP
01242 nsXFormsRepeatElement::TryFocus(PRBool *aOK)
01243 {
01244   if (!mCurrentIndex) {
01245     *aOK = PR_FALSE;
01246     return NS_OK;
01247   }
01248 
01249   nsCOMPtr<nsIDOMElement> anon = GetAnonymousContent();
01250   if (!anon)
01251     return NS_OK;
01252 
01258   nsCOMPtr<nsIDOMNodeList> children;
01259   anon->GetChildNodes(getter_AddRefs(children));
01260   NS_ENSURE_STATE(children);
01261 
01262   nsCOMPtr<nsIDOMNode> child;
01263   children->Item(mCurrentIndex - 1, // Indexes are 1-based, the DOM is 0-based
01264                  getter_AddRefs(child));
01265   nsCOMPtr<nsIXFormsControl> control = do_QueryInterface(child);
01266   NS_ENSURE_STATE(control);
01267 
01268   return control->TryFocus(aOK);
01269 }
01270 
01271 NS_IMETHODIMP
01272 nsXFormsRepeatElement::IsEventTarget(PRBool *aOK)
01273 {
01274   *aOK = PR_FALSE;
01275   return NS_OK;
01276 }
01277 
01278 NS_IMETHODIMP
01279 nsXFormsRepeatElement::GetUsesSingleNodeBinding(PRBool *aUsesSNB)
01280 {
01281   NS_ENSURE_ARG_POINTER(aUsesSNB);
01282   *aUsesSNB = PR_FALSE;
01283   return NS_OK;
01284 }
01285 
01290 nsresult
01291 nsXFormsRepeatElement::GetIntAttr(const nsAString &aName,
01292                                   PRInt32         *aVal,
01293                                   const PRUint16   aType)
01294 {
01295   nsresult rv = NS_OK;
01296   
01297   NS_ENSURE_ARG_POINTER(aVal);
01298 
01299   nsAutoString attrVal;
01300   mElement->GetAttribute(aName, attrVal);
01301 
01304   if (attrVal.IsEmpty()) {
01305     return NS_ERROR_NOT_AVAILABLE;
01306   }
01307 
01308   PRInt32 errCode;
01311   *aVal = attrVal.ToInteger(&errCode);
01312   NS_ENSURE_TRUE(errCode == 0, NS_ERROR_FAILURE);
01313 
01316   switch (aType) {
01317   case nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER:
01318     if (*aVal < 0) {
01319      rv = NS_ERROR_FAILURE;
01320     }
01321     break;
01322   
01323   case nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER:
01324     if (*aVal <= 0) {
01325       rv = NS_ERROR_FAILURE;
01326     }
01327     break;
01328 
01329   case nsISchemaBuiltinType::BUILTIN_TYPE_ANYTYPE:
01330     break;
01331 
01332   default:
01333     rv = NS_ERROR_INVALID_ARG; // or NOT_IMPLEMENTED?
01334     break;
01335   }
01336   
01337   return rv;
01338 }
01339 
01340 PRBool
01341 nsXFormsRepeatElement::IsBindingAttribute(const nsIAtom *aAttr) const
01342 {
01343   if (aAttr == nsXFormsAtoms::bind ||
01344       aAttr == nsXFormsAtoms::nodeset  ||
01345       aAttr == nsXFormsAtoms::model) {
01346     return PR_TRUE;
01347   }
01348   
01349   return PR_FALSE;
01350 }
01351 
01352 void
01353 nsXFormsRepeatElement::AfterSetAttribute(nsIAtom *aName)
01354 {
01355   if (aName == nsXFormsAtoms::attrBased) {
01356     PRBool isAttributeBased;
01357     nsresult rv =
01358       mElement->HasAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_MOZ_XFORMS_TYPE),
01359                                NS_LITERAL_STRING("attrBased"),
01360                                &isAttributeBased);
01361     if (NS_SUCCEEDED(rv))
01362       mIsAttributeBased = isAttributeBased;
01363   }
01364 
01365   nsXFormsDelegateStub::AfterSetAttribute(aName);
01366 }
01367 
01368 already_AddRefed<nsIDOMElement>
01369 nsXFormsRepeatElement::GetAnonymousContent()
01370 {
01371   nsIDOMElement* anon = nsnull;
01372   nsCOMPtr<nsIXFormsRepeatUIElement> uiElement(do_QueryInterface(mElement));
01373   if (uiElement) {
01374     // addrefs
01375     uiElement->GetAnonymousRepeatContent(&anon);
01376   }
01377   return anon;
01378 }
01379 
01380 nsRepeatState
01381 nsXFormsRepeatElement::UpdateRepeatState(nsIDOMNode *aParent)
01382 {
01383   // If the repeat is attribute based, then it is generated by anonymous
01384   // content.  In that case its repeat type is eType_NotApplicable.  Otherwise,
01385   // calculate the repeat state using the normal logic.
01386   nsRepeatState repeatState = eType_Unknown;
01387 
01388   if (aParent) {
01389     PRBool isAttributeBased = PR_FALSE;
01390     nsresult rv =
01391       mElement->HasAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_MOZ_XFORMS_TYPE),
01392                                NS_LITERAL_STRING("attrBased"),
01393                                &isAttributeBased);
01394     if (NS_SUCCEEDED(rv)) {
01395       // if repeat is attribute based, it won't go through the normal
01396       // attribute change processing, so should set the mIsAttributeBased
01397       // variable here
01398       mIsAttributeBased = isAttributeBased;
01399       if (isAttributeBased) {
01400         repeatState = eType_NotApplicable;
01401         SetRepeatState(repeatState);
01402         return repeatState;
01403       }
01404     }
01405   }
01406 
01407   return nsXFormsControlStubBase::UpdateRepeatState(aParent);
01408 }
01409 
01410 // Factory
01411 NS_HIDDEN_(nsresult)
01412 NS_NewXFormsRepeatElement(nsIXTFElement **aResult)
01413 {
01414   *aResult = new nsXFormsRepeatElement();
01415   if (!*aResult)
01416     return NS_ERROR_OUT_OF_MEMORY;
01417 
01418   NS_ADDREF(*aResult);
01419   return NS_OK;
01420 }