diff --git a/mozilla/extensions/xforms/Makefile.in b/mozilla/extensions/xforms/Makefile.in index 1db30d5ec69..21780614dec 100644 --- a/mozilla/extensions/xforms/Makefile.in +++ b/mozilla/extensions/xforms/Makefile.in @@ -83,6 +83,8 @@ CPPSRCS = \ nsXFormsInputElement.cpp \ nsXFormsGroupElement.cpp \ nsXFormsOutputElement.cpp \ + nsXFormsRepeatElement.cpp \ + nsXFormsRepeatItemElement.cpp \ nsXFormsTriggerElement.cpp \ nsXFormsSubmissionElement.cpp \ nsXFormsStubElement.cpp \ diff --git a/mozilla/extensions/xforms/nsIXFormsContextControl.idl b/mozilla/extensions/xforms/nsIXFormsContextControl.idl index 7eba0e9aee4..1c402bc7bb3 100644 --- a/mozilla/extensions/xforms/nsIXFormsContextControl.idl +++ b/mozilla/extensions/xforms/nsIXFormsContextControl.idl @@ -59,4 +59,9 @@ interface nsIXFormsContextControl : nsISupports * only be returned if the default model has an id="". */ void getContext(out AString aModelID, out nsIDOMElement aContextNode, out long aContextPosition, out long aContextSize); + + /** + * Used by parents to set the context node for children. + */ + void setContextNode(in nsIDOMElement aContextNode); }; diff --git a/mozilla/extensions/xforms/nsXFormsAtoms.cpp b/mozilla/extensions/xforms/nsXFormsAtoms.cpp index e0cf4da6c6c..8d996902008 100644 --- a/mozilla/extensions/xforms/nsXFormsAtoms.cpp +++ b/mozilla/extensions/xforms/nsXFormsAtoms.cpp @@ -50,6 +50,7 @@ nsIAtom* nsXFormsAtoms::constraint; nsIAtom* nsXFormsAtoms::p3ptype; nsIAtom* nsXFormsAtoms::modelListProperty; nsIAtom *nsXFormsAtoms::ref; +nsIAtom *nsXFormsAtoms::nodeset; nsIAtom *nsXFormsAtoms::model; const nsStaticAtom nsXFormsAtoms::Atoms_info[] = { @@ -64,6 +65,7 @@ const nsStaticAtom nsXFormsAtoms::Atoms_info[] = { { "p3ptype", &nsXFormsAtoms::p3ptype }, { "ModelListProperty", &nsXFormsAtoms::modelListProperty }, { "ref", &nsXFormsAtoms::ref }, + { "nodeset", &nsXFormsAtoms::nodeset }, { "model", &nsXFormsAtoms::model } }; diff --git a/mozilla/extensions/xforms/nsXFormsAtoms.h b/mozilla/extensions/xforms/nsXFormsAtoms.h index 21e6bd53662..75da483dd89 100644 --- a/mozilla/extensions/xforms/nsXFormsAtoms.h +++ b/mozilla/extensions/xforms/nsXFormsAtoms.h @@ -55,6 +55,7 @@ class nsXFormsAtoms static NS_HIDDEN_(nsIAtom *) p3ptype; static NS_HIDDEN_(nsIAtom *) modelListProperty; static NS_HIDDEN_(nsIAtom *) ref; + static NS_HIDDEN_(nsIAtom *) nodeset; static NS_HIDDEN_(nsIAtom *) model; static NS_HIDDEN_(void) InitAtoms(); diff --git a/mozilla/extensions/xforms/nsXFormsElementFactory.cpp b/mozilla/extensions/xforms/nsXFormsElementFactory.cpp index 2239dd192ad..7ca1ee05da1 100644 --- a/mozilla/extensions/xforms/nsXFormsElementFactory.cpp +++ b/mozilla/extensions/xforms/nsXFormsElementFactory.cpp @@ -48,6 +48,13 @@ NS_HIDDEN_(nsresult) NS_NewXFormsInputElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsTextAreaElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsGroupElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsOutputElement(nsIXTFElement **aElement); +NS_HIDDEN_(nsresult) NS_NewXFormsRepeatElement(nsIXTFElement **aElement); + +/// @todo Having \ in the factory means that the item can be used +/// by a user in a document. Not optimal, as it is not a legal XForms tag, but +/// only a pseudo-element. But nsXFormsRepeatElement needs to be able to +/// create the tag, so is there a "backdoor way" to do that? (XXX) +NS_HIDDEN_(nsresult) NS_NewXFormsRepeatItemElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsTriggerElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsSubmitElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsLabelElement(nsIXTFElement **aElement); @@ -85,6 +92,10 @@ nsXFormsElementFactory::CreateElement(const nsAString& aTagName, return NS_NewXFormsGroupElement(aElement); if (aTagName.EqualsLiteral("output")) return NS_NewXFormsOutputElement(aElement); + if (aTagName.EqualsLiteral("repeat")) + return NS_NewXFormsRepeatElement(aElement); + if (aTagName.EqualsLiteral("repeatitem")) + return NS_NewXFormsRepeatItemElement(aElement); if (aTagName.EqualsLiteral("label")) return NS_NewXFormsLabelElement(aElement); if (aTagName.EqualsLiteral("dispatch")) diff --git a/mozilla/extensions/xforms/nsXFormsGroupElement.cpp b/mozilla/extensions/xforms/nsXFormsGroupElement.cpp index e31a5d29941..8e67dc2d3e3 100644 --- a/mozilla/extensions/xforms/nsXFormsGroupElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsGroupElement.cpp @@ -301,6 +301,12 @@ nsXFormsGroupElement::Process() } // nsIXFormsContextControl +NS_IMETHODIMP +nsXFormsGroupElement::SetContextNode(nsIDOMElement *aContextNode) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + nsresult nsXFormsGroupElement::GetContext(nsAString& aModelID, nsIDOMElement **aContextNode, diff --git a/mozilla/extensions/xforms/nsXFormsInputElement.cpp b/mozilla/extensions/xforms/nsXFormsInputElement.cpp index a8c13c6f23f..bea47b4eb19 100644 --- a/mozilla/extensions/xforms/nsXFormsInputElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsInputElement.cpp @@ -77,6 +77,7 @@ public: // nsIXTFElement overrides NS_IMETHOD OnDestroyed(); + NS_IMETHOD DocumentChanged(nsIDOMDocument *aNewDocument); NS_IMETHOD ParentChanged(nsIDOMElement *aNewParent); NS_IMETHOD WillSetAttribute(nsIAtom *aName, const nsAString &aValue); NS_IMETHOD AttributeSet(nsIAtom *aName, const nsAString &aValue); @@ -116,6 +117,7 @@ nsXFormsInputElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) { aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_WILL_SET_ATTRIBUTE | nsIXTFElement::NOTIFY_ATTRIBUTE_SET | + nsIXTFElement::NOTIFY_DOCUMENT_CHANGED | nsIXTFElement::NOTIFY_PARENT_CHANGED); nsCOMPtr node; @@ -205,11 +207,22 @@ nsXFormsInputElement::OnDestroyed() return NS_OK; } +NS_IMETHODIMP +nsXFormsInputElement::DocumentChanged(nsIDOMDocument *aNewDocument) +{ + // We need to re-evaluate our instance data binding when our document + // changes, since our context can change + if (aNewDocument) + Refresh(); + + return NS_OK; +} + NS_IMETHODIMP nsXFormsInputElement::ParentChanged(nsIDOMElement *aNewParent) { - // We need to re-evaluate our instance data binding when our parent - // changes, since xmlns declarations in effect could have changed. + // We need to re-evaluate our instance data binding when our parent changes, + // since xmlns declarations or our context could have changed. if (aNewParent) Refresh(); diff --git a/mozilla/extensions/xforms/nsXFormsOutputElement.cpp b/mozilla/extensions/xforms/nsXFormsOutputElement.cpp index 1e538b13092..d52ec685a32 100755 --- a/mozilla/extensions/xforms/nsXFormsOutputElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsOutputElement.cpp @@ -81,6 +81,7 @@ public: // nsIXTFElement overrides NS_IMETHOD OnDestroyed(); + NS_IMETHOD DocumentChanged(nsIDOMDocument *aNewDocument); NS_IMETHOD ParentChanged(nsIDOMElement *aNewParent); NS_IMETHOD WillSetAttribute(nsIAtom *aName, const nsAString &aValue); NS_IMETHOD AttributeSet(nsIAtom *aName, const nsAString &aValue); @@ -109,6 +110,7 @@ nsXFormsOutputElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) #endif aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_WILL_SET_ATTRIBUTE | nsIXTFElement::NOTIFY_ATTRIBUTE_SET | + nsIXTFElement::NOTIFY_DOCUMENT_CHANGED | nsIXTFElement::NOTIFY_PARENT_CHANGED); nsresult rv; @@ -164,11 +166,22 @@ nsXFormsOutputElement::OnDestroyed() return NS_OK; } +NS_IMETHODIMP +nsXFormsOutputElement::DocumentChanged(nsIDOMDocument *aNewDocument) +{ + // We need to re-evaluate our instance data binding when our document + // changes, since our context can change + if (aNewDocument) + Refresh(); + + return NS_OK; +} + NS_IMETHODIMP nsXFormsOutputElement::ParentChanged(nsIDOMElement *aNewParent) { - // We need to re-evaluate our instance data binding when our parent - // changes, since xmlns declarations in effect could have changed. + // We need to re-evaluate our instance data binding when our parent changes, + // since xmlns declarations or our context could have changed. if (aNewParent) { Refresh(); } diff --git a/mozilla/extensions/xforms/nsXFormsRepeatElement.cpp b/mozilla/extensions/xforms/nsXFormsRepeatElement.cpp new file mode 100644 index 00000000000..c8c0d285b1c --- /dev/null +++ b/mozilla/extensions/xforms/nsXFormsRepeatElement.cpp @@ -0,0 +1,508 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla XForms support. + * + * The Initial Developer of the Original Code is + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIXTFXMLVisualWrapper.h" + +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsINameSpaceManager.h" +#include "nsISchema.h" +#include "nsIServiceManager.h" +#include "nsMemory.h" +#include "nsString.h" +#include "nsSubstring.h" + +#include "nsIDOM3EventTarget.h" +#include "nsIDOM3Node.h" +#include "nsIDOMDOMImplementation.h" +#include "nsIDOMDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOMHTMLDivElement.h" +#include "nsIDOMXPathResult.h" + +#include "nsIXFormsControl.h" +#include "nsIXFormsContextControl.h" +#include "nsXFormsAtoms.h" +#include "nsXFormsModelElement.h" +#include "nsXFormsStubElement.h" +#include "nsXFormsUtils.h" + +#ifdef DEBUG +//#define DEBUG_XF_REPEAT +#endif + +/** + * Implementation of the XForms \ control. + * @see http://www.w3.org/TR/xforms/slice9.html#id2632123 + * + * On Refresh(), nsXFormsRepeatElement, does the following for each node in + * the nodeset the \ tag is bound to: + * + * 1) Creates a new \ (nsXFormsRepeatItemElement) + * + * 2) Clones all its children (that is children of its nsIXTFXMLVisualWrapper) + * and appends them as children to the nsXFormsRepeatItemElement + * + * 3) Sets the context node and size for the nsXFormsRepeatItemElement, so + * that children can retrieve this through nsIXFormsContextControl. + * + * 4) Inserts the nsXFormsRepeatItemElement into its visual content node + * (mHTMLElement). + * + * For example, this instance data: + *
+ * 
+ *   
+ *     val1
+ *     val2
+ *   
+ * 
+ * 
+ * + * and this repeat: + *
+ * 
+ *   Val: 
+ * 
+ * 
+ * + * will be expanded to: + *
+ * 
+ *                  (contextNode == "n[0]" and contextPosition == 1)
+ *     Val:    (that is: 'val1')
+ *   
+ *                  (contextNode == "n[1]" and contextPosition == 2)
+ *     Val:    (that is: 'val2')
+ *   
+ * 
+ * 
+ * + * Besides being a practical way to implement \, it also means that it + * is possible to CSS-style the individual "rows" in a \. + * + * @todo Support attribute based repeats (XXX), as in: + * \ + * @see http://www.w3.org/TR/xforms/index-all.html#ui.repeat.via.attrs + */ +class nsXFormsRepeatElement : public nsIXFormsControl, + public nsXFormsXMLVisualStub +{ +protected: + /** The DOM element for the node */ + nsCOMPtr mElement; + + /** The HTML representation for the node */ + nsCOMPtr mHTMLElement; + + /** Have DoneAddingChildren() been called? */ + PRBool mDoneAddingChildren; + + /** + * Retrieves an integer attribute and checks its type. + * + * @param aName The attribute to retrieve + * @param aVal The value + * @param aType The attribute (Schema) type + * @return Normal error codes, and NS_ERROR_NOT_AVAILABLE if + * the attribute was empty/nonexistant + */ + nsresult GetIntAttr(const nsAString& aName, PRInt32* aVal, const PRUint16 aType); + +public: + NS_DECL_ISUPPORTS_INHERITED + + /** Constructor */ + nsXFormsRepeatElement(); + + // nsIXTFXMLVisual overrides + NS_IMETHOD OnCreated(nsIXTFXMLVisualWrapper *aWrapper); + + // nsIXTFVisual overrides + NS_IMETHOD GetVisualContent(nsIDOMElement **aElement); + + // nsIXTFElement overrides + NS_IMETHOD OnDestroyed(); + NS_IMETHOD WillSetAttribute(nsIAtom *aName, const nsAString &aValue); + NS_IMETHOD AttributeSet(nsIAtom *aName, const nsAString &aValue); + NS_IMETHOD DoneAddingChildren(); + + // nsIXFormsControl + NS_DECL_NSIXFORMSCONTROL +}; + +NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsRepeatElement, + nsXFormsXMLVisualStub, + nsIXFormsControl) + + +nsXFormsRepeatElement::nsXFormsRepeatElement() + : mElement(nsnull) +{ +} + +// nsIXTFXMLVisual +NS_IMETHODIMP +nsXFormsRepeatElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) +{ +#ifdef DEBUG_XF_REPEAT + printf("nsXFormsRepeatElement::OnCreated(aWrapper=%p)\n", (void*) aWrapper); +#endif + + nsresult rv; + + // Initialize members + mDoneAddingChildren = PR_FALSE; + + // Get node and document + nsCOMPtr node; + rv = aWrapper->GetElementNode(getter_AddRefs(node)); + NS_ENSURE_SUCCESS(rv, rv); + mElement = node; + NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon"); + + nsCOMPtr domDoc; + rv = node->GetOwnerDocument(getter_AddRefs(domDoc)); + NS_ENSURE_SUCCESS(rv, rv); + + // Create UI element + nsCOMPtr domElement; + rv = domDoc->CreateElementNS(NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), + NS_LITERAL_STRING("div"), + getter_AddRefs(domElement)); + NS_ENSURE_SUCCESS(rv, rv); + mHTMLElement = do_QueryInterface(domElement); + NS_ENSURE_TRUE(mHTMLElement, NS_ERROR_FAILURE); + + // Setup notifications we want to receive + aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_WILL_SET_ATTRIBUTE | + nsIXTFElement::NOTIFY_ATTRIBUTE_SET | + nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN); + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsRepeatElement::GetVisualContent(nsIDOMElement **aElement) +{ +#ifdef DEBUG_XF_REPEAT + printf("nsXFormsRepeatElement::GetVisualContent()\n"); +#endif + + NS_IF_ADDREF(*aElement = mHTMLElement); + return NS_OK; +} + +// nsIXTFElement +NS_IMETHODIMP +nsXFormsRepeatElement::OnDestroyed() +{ + mHTMLElement = nsnull; + mElement = nsnull; + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsRepeatElement::WillSetAttribute(nsIAtom *aName, const nsAString &aValue) +{ +#ifdef DEBUG_XF_REPEAT + printf("nsXFormsRepeatElement::WillSetAttribute(aName=%p, aValue='%s')\n", (void*) aName, NS_ConvertUCS2toUTF8(aValue).get()); +#endif + + if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) { + nsCOMPtr modelNode = nsXFormsUtils::GetModel(mElement); + + nsCOMPtr model = do_QueryInterface(modelNode); + if (model) { + model->RemoveFormControl(this); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsRepeatElement::AttributeSet(nsIAtom *aName, const nsAString &aValue) +{ +#ifdef DEBUG_XF_REPEAT + printf("nsXFormsRepeatElement::AttributeSet(aName=%p, aValue=%s)\n", (void*) aName, NS_ConvertUCS2toUTF8(aValue).get()); +#endif + + if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::nodeset) { + Refresh(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsRepeatElement::DoneAddingChildren() +{ +#ifdef DEBUG_XF_REPEAT + printf("nsXFormsRepeatElement::DoneAddingChildren()\n"); +#endif + + mDoneAddingChildren = PR_TRUE; + + Refresh(); + + return NS_OK; +} + + +// nsXFormsControl +nsresult +nsXFormsRepeatElement::Refresh() +{ +#ifdef DEBUG_XF_REPEAT + printf("nsXFormsRepeatElement::Refresh()\n"); +#endif + + if (!mHTMLElement || !mDoneAddingChildren) + return NS_OK; + + nsresult rv; + + // Clear any existing children + nsCOMPtr cNode; + mHTMLElement->GetFirstChild(getter_AddRefs(cNode)); + while (cNode) { + nsCOMPtr retNode; + mHTMLElement->RemoveChild(cNode, getter_AddRefs(retNode)); + mHTMLElement->GetFirstChild(getter_AddRefs(cNode)); + } + + // Get the nodeset we are bound to + nsCOMPtr modelNode; + nsCOMPtr bindElement; + nsCOMPtr result = + nsXFormsUtils::EvaluateNodeBinding(mElement, + nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, + NS_LITERAL_STRING("nodeset"), + EmptyString(), + nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, + getter_AddRefs(modelNode), + getter_AddRefs(bindElement)); + + if (!result) { + return NS_OK; + } + + nsCOMPtr model = do_QueryInterface(modelNode); + + /// @todo The result should be a _homogenous_ collection (spec. 9.3.1), + /// do/can/should we check this? (XXX) + PRUint32 contextSize; + rv = result->GetSnapshotLength(&contextSize); + NS_ENSURE_SUCCESS(rv, rv); + + if (model) { + model->AddFormControl(this); + if (result && contextSize > 0) { + // Get model ID + nsCOMPtr modelElement = do_QueryInterface(modelNode); + NS_ENSURE_TRUE(modelElement, NS_ERROR_FAILURE); + nsAutoString modelID; + modelElement->GetAttribute(NS_LITERAL_STRING("id"), modelID); + + // Get attributes + PRUint32 startIndex; + rv = GetIntAttr(NS_LITERAL_STRING("startIndex"), + (PRInt32*) &startIndex, + nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER); + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_NOT_AVAILABLE) { + startIndex = 1; + } else { + return rv; + } + } + + PRUint32 number; + rv = GetIntAttr(NS_LITERAL_STRING("number"), + (PRInt32*) &number, + nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER); + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_NOT_AVAILABLE) { + number = contextSize; + } else { + return rv; + } + } + // Get DOM document + nsCOMPtr domDoc; + rv = mHTMLElement->GetOwnerDocument(getter_AddRefs(domDoc)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString strSize; + strSize.AppendInt(contextSize); + + // The spec. states the following for startIndex: "Optional 1-based + // initial value of the repeat index", I interpret this as "start + // showing element at position |startIndex|". So I iterate over the + // interval [max(1, startIndex),min(contextSize, number + startIndex)[ + for (PRUint32 i = NS_MAX((PRUint32) 1, startIndex); + i < NS_MIN(contextSize + 1, number + startIndex); + ++i) { + // Create + nsCOMPtr riElement; + rv = domDoc->CreateElementNS(NS_LITERAL_STRING("http://www.w3.org/2002/xforms"), + NS_LITERAL_STRING("repeatitem"), + getter_AddRefs(riElement)); + NS_ENSURE_SUCCESS(rv, rv); + + // Set context size, context position, and model as attributes + if (!modelID.IsEmpty()) { + riElement->SetAttribute(NS_LITERAL_STRING("model"), modelID); + } + nsAutoString strPos; strPos.AppendInt(i); + riElement->SetAttribute(NS_LITERAL_STRING("contextposition"), strPos); + riElement->SetAttribute(NS_LITERAL_STRING("contextsize"), strSize); + + // Get and set context node + nsCOMPtr riContext = do_QueryInterface(riElement); + NS_ENSURE_TRUE(riContext, NS_ERROR_FAILURE); + + nsCOMPtr contextNode; + rv = result->SnapshotItem(i - 1, getter_AddRefs(contextNode)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr contextElement = do_QueryInterface(contextNode); + NS_ENSURE_TRUE(contextElement, NS_ERROR_FAILURE); + + rv = riContext->SetContextNode(contextElement); + NS_ENSURE_SUCCESS(rv, rv); + + // Iterate over template children, clone them, and append them to + nsCOMPtr child; + rv = mElement->GetFirstChild(getter_AddRefs(child)); + NS_ENSURE_SUCCESS(rv, rv); + while (child) { + nsCOMPtr childClone; + rv = child->CloneNode(PR_TRUE, getter_AddRefs(childClone)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr newNode; + rv = riElement->AppendChild(childClone, getter_AddRefs(newNode)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = child->GetNextSibling(getter_AddRefs(child)); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Append node + nsCOMPtr domNode; + rv = mHTMLElement->AppendChild(riElement, getter_AddRefs(domNode)); + NS_ENSURE_SUCCESS(rv, rv); + + // There is an awfull lot of evaluating being done by all the + // children, as they are created and inserted into the different + // places in the DOM, the only refresh necessary is the one when they + // are appended in mHTMLElement. + } + } + } + + return NS_OK; +} + +/** + * @todo This function will be part of the general schema support, so it will + * only live here until this is implemented there. (XXX) + */ +nsresult +nsXFormsRepeatElement::GetIntAttr(const nsAString& aName, PRInt32* aVal, const PRUint16 aType) +{ + nsresult rv = NS_OK; + + NS_ENSURE_ARG_POINTER(aVal); + + nsAutoString attrVal; + mElement->GetAttribute(aName, attrVal); + + /// @todo Is this the correct error to return? We need to distinguish between + /// an empty attribute and other errors. (XXX) + if (attrVal.IsEmpty()) { + return NS_ERROR_NOT_AVAILABLE; + } + + PRInt32 errCode; + /// @todo ToInteger is extremely large, "xxx23xxx" will be parsed with no errors + /// as "23"... (XXX) + *aVal = attrVal.ToInteger(&errCode); + NS_ENSURE_TRUE(errCode == 0, NS_ERROR_FAILURE); + + /// + /// @todo Check maximum values? (XXX) + switch (aType) { + case nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER: + if (*aVal < 0) { + rv = NS_ERROR_FAILURE; + } + break; + + case nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER: + if (*aVal <= 0) { + rv = NS_ERROR_FAILURE; + } + break; + + case nsISchemaBuiltinType::BUILTIN_TYPE_ANYTYPE: + break; + + default: + rv = NS_ERROR_INVALID_ARG; // or NOT_IMPLEMENTED? + break; + } + + return rv; +} + +// Factory +NS_HIDDEN_(nsresult) +NS_NewXFormsRepeatElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsRepeatElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/mozilla/extensions/xforms/nsXFormsRepeatItemElement.cpp b/mozilla/extensions/xforms/nsXFormsRepeatItemElement.cpp new file mode 100644 index 00000000000..8075573383d --- /dev/null +++ b/mozilla/extensions/xforms/nsXFormsRepeatItemElement.cpp @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla XForms support. + * + * The Initial Developer of the Original Code is + * Novell, Inc. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Allan Beaufour + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIXTFXMLVisualWrapper.h" + +#include "nsCOMPtr.h" +#include "nsString.h" + +#include "nsIDOM3Node.h" +#include "nsIDOMDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOMSerializer.h" +#include "nsIDOMXPathResult.h" + +#include "nsIXFormsControl.h" +#include "nsIXFormsContextControl.h" +#include "nsXFormsStubElement.h" +#include "nsXFormsUtils.h" + +#ifdef DEBUG +//#define DEBUG_XF_REPEATITEM +#endif + +/** + * Implementation of \. + * + * \ is a pseudo-element that is wrapped around each row in the + * "unrolled" \. @see nsXFormsRepeatElement. + * + * @todo Should this class inherit from nsIXFormsControl? (XXX) + * + * @todo Support ::repeat-item and ::repeat-index pseudo-elements. (XXX) + * @see http://www.w3.org/TR/xforms/sliceF.html#id2645142 + * @see http://bugzilla.mozilla.org/show_bug.cgi?id=271724 + */ +class nsXFormsRepeatItemElement : public nsIXFormsControl, + public nsXFormsXMLVisualStub, + public nsIXFormsContextControl +{ +protected: + /** The DOM element for the node */ + nsCOMPtr mElement; + + /** The HTML representation for the node */ + nsCOMPtr mHTMLElement; + + /** The context node for the node */ + nsCOMPtr mContextNode; + +public: + NS_DECL_ISUPPORTS_INHERITED + + // nsIXTFXMLVisual overrides + NS_IMETHOD OnCreated(nsIXTFXMLVisualWrapper *aWrapper); + + // nsIXTFVisual overrides + NS_IMETHOD GetVisualContent(nsIDOMElement **aElement); + NS_IMETHOD GetInsertionPoint(nsIDOMElement **aElement); + + // nsIXTFElement overrides + NS_IMETHOD OnDestroyed(); + + // nsIXFormsControl + NS_DECL_NSIXFORMSCONTROL + + // nsIXFormsContextControl + NS_DECL_NSIXFORMSCONTEXTCONTROL + +}; + +NS_IMPL_ISUPPORTS_INHERITED2(nsXFormsRepeatItemElement, + nsXFormsXMLVisualStub, + nsIXFormsControl, + nsIXFormsContextControl) + + +// nsIXTFXMLVisual +NS_IMETHODIMP +nsXFormsRepeatItemElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) +{ +#ifdef DEBUG_XF_REPEATITEM + printf("nsXFormsRepeatItemElement::OnCreated(aWrapper=%p)\n", (void*) aWrapper); +#endif + + nsresult rv; + + // Get node and document + nsCOMPtr node; + rv = aWrapper->GetElementNode(getter_AddRefs(node)); + NS_ENSURE_SUCCESS(rv, rv); + mElement = node; + NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon"); + + nsCOMPtr domDoc; + rv = node->GetOwnerDocument(getter_AddRefs(domDoc)); + NS_ENSURE_SUCCESS(rv, rv); + + // Create UI element + rv = domDoc->CreateElementNS(NS_LITERAL_STRING("http://www.w3.org/1999/xhtml"), + NS_LITERAL_STRING("div"), + getter_AddRefs(mHTMLElement)); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsRepeatItemElement::GetVisualContent(nsIDOMElement **aElement) +{ +#ifdef DEBUG_XF_REPEATITEM + printf("nsXFormsRepeatItemElement::GetVisualContent()\n"); +#endif + + NS_IF_ADDREF(*aElement = mHTMLElement); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsRepeatItemElement::GetInsertionPoint(nsIDOMElement **aElement) +{ +#ifdef DEBUG_XF_REPEATITEM + printf("nsXFormsRepeatItemElement::GetInsertionPoint()\n"); +#endif + + NS_IF_ADDREF(*aElement = mHTMLElement); + + return NS_OK; +} + +// nsIXTFElement +NS_IMETHODIMP +nsXFormsRepeatItemElement::OnDestroyed() +{ + mHTMLElement = nsnull; + mElement = nsnull; + mContextNode = nsnull; + + return NS_OK; +} + +// nsIXFormsContextControl +NS_IMETHODIMP +nsXFormsRepeatItemElement::SetContextNode(nsIDOMElement *aContextNode) +{ + mContextNode = aContextNode; + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsRepeatItemElement::GetContext(nsAString& aModelID, + nsIDOMElement **aContextNode, + PRInt32 *aContextPosition, + PRInt32 *aContextSize) +{ + NS_IF_ADDREF(*aContextNode = mContextNode); + nsAutoString val; + mElement->GetAttribute(NS_LITERAL_STRING("contextsize"), val); + PRInt32 errCode; + *aContextSize = val.ToInteger(&errCode); + if (errCode) { + *aContextSize = 1; + } + mElement->GetAttribute(NS_LITERAL_STRING("contextposition"), val); + *aContextPosition = val.ToInteger(&errCode); + if (errCode) { + *aContextPosition = 1; + } + mElement->GetAttribute(NS_LITERAL_STRING("model"), aModelID); + return NS_OK; +} + +// nsXFormsControl +nsresult +nsXFormsRepeatItemElement::Refresh() +{ + return NS_OK; +} + +// Factory +NS_HIDDEN_(nsresult) +NS_NewXFormsRepeatItemElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsRepeatItemElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +}