From 898bfde5e815e1940718c0227f4b73ba462954ed Mon Sep 17 00:00:00 2001 From: "darin%meer.net" Date: Wed, 24 Nov 2004 20:47:23 +0000 Subject: [PATCH] fixes bug 265216 "Tracking bug for XForms Group Module" patch by allan@beaufour.dk r=bryner --- extensions/xforms/Makefile.in | 3 + extensions/xforms/nsIXFormsContextControl.idl | 62 ++++ extensions/xforms/nsXFormsElementFactory.cpp | 3 + extensions/xforms/nsXFormsGroupElement.cpp | 343 ++++++++++++++++++ extensions/xforms/nsXFormsInputElement.cpp | 12 +- extensions/xforms/nsXFormsOutputElement.cpp | 8 +- extensions/xforms/nsXFormsRebuildElement.cpp | 6 +- .../xforms/nsXFormsRecalculateElement.cpp | 6 +- extensions/xforms/nsXFormsRefreshElement.cpp | 6 +- extensions/xforms/nsXFormsResetElement.cpp | 6 +- .../xforms/nsXFormsRevalidateElement.cpp | 6 +- extensions/xforms/nsXFormsSetValueElement.cpp | 7 +- extensions/xforms/nsXFormsUtils.cpp | 301 +++++++++++---- extensions/xforms/nsXFormsUtils.h | 65 +++- 14 files changed, 707 insertions(+), 127 deletions(-) create mode 100644 extensions/xforms/nsIXFormsContextControl.idl create mode 100644 extensions/xforms/nsXFormsGroupElement.cpp diff --git a/extensions/xforms/Makefile.in b/extensions/xforms/Makefile.in index a3415c8684b3..1db30d5ec694 100644 --- a/extensions/xforms/Makefile.in +++ b/extensions/xforms/Makefile.in @@ -1,3 +1,4 @@ +# vim:set ts=8 sw=8 sts=8 noet: # # ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -70,6 +71,7 @@ XPIDLSRCS = \ nsIInstanceElementPrivate.idl \ nsIModelElementPrivate.idl \ nsIXFormsControl.idl \ + nsIXFormsContextControl.idl \ nsIXFormsActionElement.idl \ nsIXFormsActionModuleElement.idl \ $(NULL) @@ -79,6 +81,7 @@ CPPSRCS = \ nsXFormsUtils.cpp \ nsXFormsModelElement.cpp \ nsXFormsInputElement.cpp \ + nsXFormsGroupElement.cpp \ nsXFormsOutputElement.cpp \ nsXFormsTriggerElement.cpp \ nsXFormsSubmissionElement.cpp \ diff --git a/extensions/xforms/nsIXFormsContextControl.idl b/extensions/xforms/nsIXFormsContextControl.idl new file mode 100644 index 000000000000..7eba0e9aee43 --- /dev/null +++ b/extensions/xforms/nsIXFormsContextControl.idl @@ -0,0 +1,62 @@ +/* -*- Mode: IDL; 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 "nsISupports.idl" + +interface nsIDOMElement; + +/** + * Interface for elements that sets the XPath context of its children. + */ +[uuid(38a904ba-147c-4c7f-9356-9fff88107868)] +interface nsIXFormsContextControl : nsISupports +{ + /** + * Used by children to retrieve the context of their parent. + * + * @param aModelID The modelID + * @param aContextNode The context node + * @param aContextPosition The context position + * @param aContextSize The context size + * + * @note The actual model ID must be returned. An empty |aModelID| should + * only be returned if the default model has an id="". + */ + void getContext(out AString aModelID, out nsIDOMElement aContextNode, out long aContextPosition, out long aContextSize); +}; diff --git a/extensions/xforms/nsXFormsElementFactory.cpp b/extensions/xforms/nsXFormsElementFactory.cpp index 05328237162f..1b93fec950d0 100644 --- a/extensions/xforms/nsXFormsElementFactory.cpp +++ b/extensions/xforms/nsXFormsElementFactory.cpp @@ -45,6 +45,7 @@ // Form controls NS_HIDDEN_(nsresult) NS_NewXFormsInputElement(nsIXTFElement **aElement); +NS_HIDDEN_(nsresult) NS_NewXFormsGroupElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsOutputElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsTriggerElement(nsIXTFElement **aElement); NS_HIDDEN_(nsresult) NS_NewXFormsSubmitElement(nsIXTFElement **aElement); @@ -77,6 +78,8 @@ nsXFormsElementFactory::CreateElement(const nsAString& aTagName, return NS_NewXFormsStubElement(aElement); if (aTagName.EqualsLiteral("input")) return NS_NewXFormsInputElement(aElement); + if (aTagName.EqualsLiteral("group")) + return NS_NewXFormsGroupElement(aElement); if (aTagName.EqualsLiteral("output")) return NS_NewXFormsOutputElement(aElement); if (aTagName.EqualsLiteral("label")) diff --git a/extensions/xforms/nsXFormsGroupElement.cpp b/extensions/xforms/nsXFormsGroupElement.cpp new file mode 100644 index 000000000000..e31a5d299417 --- /dev/null +++ b/extensions/xforms/nsXFormsGroupElement.cpp @@ -0,0 +1,343 @@ +/* -*- 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 "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsString.h" + +#include "nsIDOM3Node.h" +#include "nsIDOMElement.h" +#include "nsIDOMDocument.h" +#include "nsIDOMEventTarget.h" +#include "nsIDOMXPathResult.h" +#include "nsIDOMHTMLDivElement.h" + +#include "nsIXTFXMLVisual.h" +#include "nsIXTFXMLVisualWrapper.h" + +#include "nsIXFormsControl.h" +#include "nsIXFormsContextControl.h" +#include "nsIModelElementPrivate.h" +#include "nsXFormsAtoms.h" +#include "nsXFormsStubElement.h" +#include "nsXFormsUtils.h" + +#ifdef DEBUG +// #define DEBUG_XF_GROUP +#endif + +/** + * Implementation of the XForms \ control. + * + * @see http://www.w3.org/TR/xforms/slice9.html#id2631290 + * + * @todo If a \ is the first element child for \ it is the + * label for the entire group + * + * @todo "Setting the input focus on a group results in the focus being set to + * the first form control in the navigation order within that group." + * (spec. 9.1.1) + * + * @bug If a group only has a model attribute, the group fails to set this for + * children, as it is impossible to distinguish between a failure and absence + * of binding attributes when calling EvaluateNodeBinding(). + */ +class nsXFormsGroupElement : public nsIXFormsControl, + public nsXFormsXMLVisualStub, + public nsIXFormsContextControl +{ +protected: + /** The DOM element for the node */ + nsCOMPtr mElement; + + /** The UI HTML element used to represent the tag */ + nsCOMPtr mHTMLElement; + + /** Have DoneAddingChildren() been called? */ + PRBool mDoneAddingChildren; + + /** The context node for the children of this element */ + nsCOMPtr mContextNode; + + /** The current ID of the model node is bound to */ + nsString mModelID; + + /** Process element */ + nsresult Process(); + +public: + NS_DECL_ISUPPORTS_INHERITED + + // Constructor + nsXFormsGroupElement(); + ~nsXFormsGroupElement(); + + // nsIXTFXMLVisual overrides + NS_IMETHOD OnCreated(nsIXTFXMLVisualWrapper *aWrapper); + + // nsIXTFVisual overrides + NS_IMETHOD GetVisualContent(nsIDOMElement **aElement); + NS_IMETHOD GetInsertionPoint(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 + + // nsIXFormsContextControl + NS_DECL_NSIXFORMSCONTEXTCONTROL +}; + +NS_IMPL_ISUPPORTS_INHERITED2(nsXFormsGroupElement, + nsXFormsXMLVisualStub, + nsIXFormsControl, + nsIXFormsContextControl) + +MOZ_DECL_CTOR_COUNTER(nsXFormsGroupElement) + +nsXFormsGroupElement::nsXFormsGroupElement() + : mElement(nsnull) +{ + MOZ_COUNT_CTOR(nsXFormsGroupElement); +} + +nsXFormsGroupElement::~nsXFormsGroupElement() +{ + MOZ_COUNT_DTOR(nsXFormsGroupElement); +} + +// nsIXTFXMLVisual +NS_IMETHODIMP +nsXFormsGroupElement::OnCreated(nsIXTFXMLVisualWrapper *aWrapper) +{ +#ifdef DEBUG_XF_GROUP + printf("nsXFormsGroupElement::OnCreated(aWrapper=%p)\n", (void*) aWrapper); +#endif + + // Initialize member(s) + nsCOMPtr node; + aWrapper->GetElementNode(getter_AddRefs(node)); + mElement = node; + NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon"); + + mDoneAddingChildren = PR_FALSE; + + // Create HTML tag + nsCOMPtr domDoc; + node->GetOwnerDocument(getter_AddRefs(domDoc)); + + nsCOMPtr domElement; + domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML), + NS_LITERAL_STRING("div"), + getter_AddRefs(domElement)); + + mHTMLElement = do_QueryInterface(domElement); + NS_ENSURE_TRUE(mHTMLElement, NS_ERROR_FAILURE); + + // Setup which notifications to receive + aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN | + nsIXTFElement::NOTIFY_ATTRIBUTE_SET | + nsIXTFElement::NOTIFY_WILL_SET_ATTRIBUTE); + + return NS_OK; +} + +// nsIXTFVisual +NS_IMETHODIMP +nsXFormsGroupElement::GetVisualContent(nsIDOMElement * *aVisualContent) +{ + NS_ADDREF(*aVisualContent = mHTMLElement); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsGroupElement::GetInsertionPoint(nsIDOMElement **aElement) +{ + NS_ADDREF(*aElement = mHTMLElement); + return NS_OK; +} + +// nsIXTFElement +NS_IMETHODIMP +nsXFormsGroupElement::OnDestroyed() +{ + mHTMLElement = nsnull; + mContextNode = nsnull; + mElement = nsnull; + + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsGroupElement::WillSetAttribute(nsIAtom *aName, const nsAString& aNewValue) +{ + 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 +nsXFormsGroupElement::AttributeSet(nsIAtom *aName, const nsAString& aNewValue) +{ + if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) { + Refresh(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsGroupElement::DoneAddingChildren() +{ +#ifdef DEBUG_XF_GROUP + printf("nsXFormsGroupElement::DoneAddingChildren()\n"); +#endif + + mDoneAddingChildren = PR_TRUE; + Refresh(); + + return NS_OK; +} + +// nsXFormsControl +nsresult +nsXFormsGroupElement::Refresh() +{ +#ifdef DEBUG_XF_GROUP + printf("nsXFormsGroupElement::Refresh(mDoneAddingChildren=%d)\n", mDoneAddingChildren); +#endif + + nsresult rv = NS_OK; + if (mDoneAddingChildren) { + rv = Process(); + } + return rv; +} + +// nsXFormsGroupElement +nsresult +nsXFormsGroupElement::Process() +{ +#ifdef DEBUG_XF_GROUP + printf("nsXFormsGroupElement::Process()\n"); +#endif + + mModelID.Truncate(); + nsCOMPtr modelNode; + nsCOMPtr bindElement; + nsCOMPtr result = + nsXFormsUtils::EvaluateNodeBinding(mElement, + nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, + NS_LITERAL_STRING("ref"), + EmptyString(), + nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE, + getter_AddRefs(modelNode), + getter_AddRefs(bindElement)); + + nsCOMPtr model = do_QueryInterface(modelNode); + + if (model) { + model->AddFormControl(this); + } + + NS_ENSURE_TRUE(result, NS_ERROR_FAILURE); + + // Get model ID + nsCOMPtr modelElement = do_QueryInterface(modelNode); + NS_ENSURE_TRUE(modelElement, NS_ERROR_FAILURE); + modelElement->GetAttribute(NS_LITERAL_STRING("id"), mModelID); + + // Get context node, if any + nsCOMPtr singleNode; + result->GetSingleNodeValue(getter_AddRefs(singleNode)); + mContextNode = do_QueryInterface(singleNode); + NS_ENSURE_TRUE(mContextNode, NS_ERROR_FAILURE); + + return NS_OK; +} + +// nsIXFormsContextControl +nsresult +nsXFormsGroupElement::GetContext(nsAString& aModelID, + nsIDOMElement **aContextNode, + PRInt32 *aContextPosition, + PRInt32 *aContextSize) +{ +#ifdef DEBUG_XF_GROUP + printf("nsXFormsGroupElement::GetContext()\n"); +#endif + NS_ENSURE_ARG(aContextSize); + NS_ENSURE_ARG(aContextPosition); + + /** @todo Not too elegant to call Process() here, but DoneAddingChildren is, + * logically, called on children before us. We need a notification + * that goes from the document node and DOWN, where the controls + * should Refresh(). + */ + *aContextPosition = 1; + *aContextSize = 1; + + nsresult rv = Process(); + NS_ENSURE_SUCCESS(rv, rv); + + NS_IF_ADDREF(*aContextNode = mContextNode); + aModelID = mModelID; + + return NS_OK; +} + +// Factory +NS_HIDDEN_(nsresult) +NS_NewXFormsGroupElement(nsIXTFElement **aResult) +{ + *aResult = new nsXFormsGroupElement(); + if (!*aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + return NS_OK; +} diff --git a/extensions/xforms/nsXFormsInputElement.cpp b/extensions/xforms/nsXFormsInputElement.cpp index 9e78ce3dabb4..e05ce8f50fa9 100644 --- a/extensions/xforms/nsXFormsInputElement.cpp +++ b/extensions/xforms/nsXFormsInputElement.cpp @@ -216,13 +216,9 @@ NS_IMETHODIMP nsXFormsInputElement::WillSetAttribute(nsIAtom *aName, const nsAString &aValue) { if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) { - nsCOMPtr bindElement; - nsCOMPtr model; + nsCOMPtr modelNode = nsXFormsUtils::GetModel(mElement); - model = do_QueryInterface( - nsXFormsUtils::GetModelAndBind(mElement, - nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, - getter_AddRefs(bindElement))); + nsCOMPtr model = do_QueryInterface(modelNode); if (model) model->RemoveFormControl(this); } @@ -315,6 +311,10 @@ nsXFormsInputElement::Refresh() nsCOMPtr model = do_QueryInterface(modelNode); + // @bug / todo: If \ has binding attributes that are invalid, we + // should clear the content. But the content should be left if the element + // is unbound. + // @see https://bugzilla.mozilla.org/show_bug.cgi?id=265216 if (model) { model->AddFormControl(this); diff --git a/extensions/xforms/nsXFormsOutputElement.cpp b/extensions/xforms/nsXFormsOutputElement.cpp index 443d5edb602e..1e538b130927 100755 --- a/extensions/xforms/nsXFormsOutputElement.cpp +++ b/extensions/xforms/nsXFormsOutputElement.cpp @@ -180,13 +180,9 @@ NS_IMETHODIMP nsXFormsOutputElement::WillSetAttribute(nsIAtom *aName, const nsAString &aValue) { if (aName == nsXFormsAtoms::bind || aName == nsXFormsAtoms::ref) { - nsCOMPtr bindElement; - nsCOMPtr model; + nsCOMPtr modelNode = nsXFormsUtils::GetModel(mElement); - model = do_QueryInterface( - nsXFormsUtils::GetModelAndBind(mElement, - nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, - getter_AddRefs(bindElement))); + nsCOMPtr model = do_QueryInterface(modelNode); if (model) model->RemoveFormControl(this); } diff --git a/extensions/xforms/nsXFormsRebuildElement.cpp b/extensions/xforms/nsXFormsRebuildElement.cpp index e7abd8e0cb1f..d5e189c3fb89 100644 --- a/extensions/xforms/nsXFormsRebuildElement.cpp +++ b/extensions/xforms/nsXFormsRebuildElement.cpp @@ -54,10 +54,8 @@ NS_IMETHODIMP nsXFormsRebuildElement::HandleAction(nsIDOMEvent* aEvent, nsIXFormsActionElement *aParentAction) { - nsCOMPtr model = - nsXFormsUtils::GetModelAndBind(mElement, - nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, - nsnull); + nsCOMPtr model = nsXFormsUtils::GetModel(mElement); + if (model) { if (aParentAction) { aParentAction->SetRebuild(model, PR_FALSE); diff --git a/extensions/xforms/nsXFormsRecalculateElement.cpp b/extensions/xforms/nsXFormsRecalculateElement.cpp index fc417199e500..f4ecb04c394b 100644 --- a/extensions/xforms/nsXFormsRecalculateElement.cpp +++ b/extensions/xforms/nsXFormsRecalculateElement.cpp @@ -54,10 +54,8 @@ NS_IMETHODIMP nsXFormsRecalculateElement::HandleAction(nsIDOMEvent* aEvent, nsIXFormsActionElement *aParentAction) { - nsCOMPtr model = - nsXFormsUtils::GetModelAndBind(mElement, - nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, - nsnull); + nsCOMPtr model = nsXFormsUtils::GetModel(mElement); + if (model) { if (aParentAction) { aParentAction->SetRecalculate(model, PR_FALSE); diff --git a/extensions/xforms/nsXFormsRefreshElement.cpp b/extensions/xforms/nsXFormsRefreshElement.cpp index 45bbe07ed771..4bddc1a1373d 100644 --- a/extensions/xforms/nsXFormsRefreshElement.cpp +++ b/extensions/xforms/nsXFormsRefreshElement.cpp @@ -54,10 +54,8 @@ NS_IMETHODIMP nsXFormsRefreshElement::HandleAction(nsIDOMEvent* aEvent, nsIXFormsActionElement *aParentAction) { - nsCOMPtr model = - nsXFormsUtils::GetModelAndBind(mElement, - nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, - nsnull); + nsCOMPtr model = nsXFormsUtils::GetModel(mElement); + if (model) { if (aParentAction) { aParentAction->SetRefresh(model, PR_FALSE); diff --git a/extensions/xforms/nsXFormsResetElement.cpp b/extensions/xforms/nsXFormsResetElement.cpp index 9c42c1b3bc97..9088878b1ecb 100644 --- a/extensions/xforms/nsXFormsResetElement.cpp +++ b/extensions/xforms/nsXFormsResetElement.cpp @@ -53,10 +53,8 @@ NS_IMETHODIMP nsXFormsResetElement::HandleAction(nsIDOMEvent* aEvent, nsIXFormsActionElement *aParentAction) { - nsCOMPtr model = - nsXFormsUtils::GetModelAndBind(mElement, - nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, - nsnull); + nsCOMPtr model = nsXFormsUtils::GetModel(mElement); + if (model) { if (aParentAction) { aParentAction->SetRebuild(model, PR_FALSE); diff --git a/extensions/xforms/nsXFormsRevalidateElement.cpp b/extensions/xforms/nsXFormsRevalidateElement.cpp index 58fd5f482d1b..bf3beaf9f718 100644 --- a/extensions/xforms/nsXFormsRevalidateElement.cpp +++ b/extensions/xforms/nsXFormsRevalidateElement.cpp @@ -54,10 +54,8 @@ NS_IMETHODIMP nsXFormsRevalidateElement::HandleAction(nsIDOMEvent* aEvent, nsIXFormsActionElement *aParentAction) { - nsCOMPtr model = - nsXFormsUtils::GetModelAndBind(mElement, - nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, - nsnull); + nsCOMPtr model = nsXFormsUtils::GetModel(mElement); + if (model) { if (aParentAction) { aParentAction->SetRevalidate(model, PR_FALSE); diff --git a/extensions/xforms/nsXFormsSetValueElement.cpp b/extensions/xforms/nsXFormsSetValueElement.cpp index c47366773faa..edae752dcfe1 100644 --- a/extensions/xforms/nsXFormsSetValueElement.cpp +++ b/extensions/xforms/nsXFormsSetValueElement.cpp @@ -65,11 +65,8 @@ nsXFormsSetValueElement::HandleAction(nsIDOMEvent* aEvent, if (!mElement) return NS_OK; - nsCOMPtr bind; - nsCOMPtr dommodel = - nsXFormsUtils::GetModelAndBind(mElement, - nsXFormsUtils::ELEMENT_WITH_MODEL_ATTR, - getter_AddRefs(bind)); + nsCOMPtr dommodel = nsXFormsUtils::GetModel(mElement); + if (!dommodel) return NS_OK; diff --git a/extensions/xforms/nsXFormsUtils.cpp b/extensions/xforms/nsXFormsUtils.cpp index b9cf3ad01c8f..85899b8d62c4 100644 --- a/extensions/xforms/nsXFormsUtils.cpp +++ b/extensions/xforms/nsXFormsUtils.cpp @@ -52,6 +52,7 @@ #include "nsIDOMText.h" #include "nsIXFormsModelElement.h" +#include "nsIXFormsContextControl.h" #include "nsIDOMDocumentEvent.h" #include "nsIDOMEvent.h" #include "nsIDOMEventTarget.h" @@ -179,8 +180,8 @@ nsXFormsUtils::Init() return NS_OK; } -/* static */ nsIDOMNode* -nsXFormsUtils::GetParentModel(nsIDOMElement *aElement) +/* static */ void +nsXFormsUtils::GetParentModel(nsIDOMElement *aElement, nsIDOMNode **aModel) { nsCOMPtr modelWrapper; @@ -201,76 +202,108 @@ nsXFormsUtils::GetParentModel(nsIDOMElement *aElement) temp.swap(modelWrapper); temp->GetParentNode(getter_AddRefs(modelWrapper)); } - - // We're releasing this reference, but the node is owned by the DOM. - return modelWrapper; + *aModel = nsnull; + modelWrapper.swap(*aModel); } -/* static */ nsIDOMNode* -nsXFormsUtils::GetModelAndBind(nsIDOMElement *aElement, - PRUint32 aElementFlags, - nsIDOMElement **aBindElement) -{ - NS_ENSURE_TRUE(aElement, nsnull); +/** + * beaufour: Section 7.4 in the specification does a really bad job of + * explaining how to find the model, so the code below is my interpretation of + * it... + * + * @see http://bugzilla.mozilla.org/show_bug.cgi?id=265216 + */ +/* static */ nsresult +nsXFormsUtils::GetNodeContext(nsIDOMElement *aElement, + PRUint32 aElementFlags, + nsIDOMNode **aModel, + nsIDOMElement **aBindElement, + nsIDOMElement **aContextNode, + PRInt32 *aContextPosition, + PRInt32 *aContextSize) +{ + *aBindElement = nsnull; + NS_ENSURE_ARG(aElement); + NS_ENSURE_ARG_POINTER(aContextNode); + + // Find correct model element nsCOMPtr domDoc; aElement->GetOwnerDocument(getter_AddRefs(domDoc)); - if (!domDoc) - return nsnull; - - if (aBindElement) { - *aBindElement = nsnull; - - nsAutoString bindId; - aElement->GetAttribute(NS_LITERAL_STRING("bind"), bindId); - - if (!bindId.IsEmpty()) { - // Get the bind element with the given id. - domDoc->GetElementById(bindId, aBindElement); - - if (*aBindElement) - return GetParentModel(*aBindElement); - } - } + NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); - if (aElementFlags & ELEMENT_WITH_MODEL_ATTR) { - // If no bind was given, we use model. + nsAutoString bindId; + aElement->GetAttribute(NS_LITERAL_STRING("bind"), bindId); + /// + /// @todo: Is there a need for ELEMENT_WITH_BIND_ATTR? + if (!bindId.IsEmpty()) { + // CASE 1: Use @bind + domDoc->GetElementById(bindId, aBindElement); + + if (*aBindElement) + GetParentModel(*aBindElement, aModel); + + // Error: There was a bind attribute, but it did not lead us to a model. + NS_ENSURE_TRUE(*aModel, NS_ERROR_FAILURE); + } else if (aElementFlags & ELEMENT_WITH_MODEL_ATTR) { + // CASE 2: Use @model + // If bind did not set model, and the element has a model attribute we use this nsAutoString modelId; aElement->GetAttribute(NS_LITERAL_STRING("model"), modelId); + + if (!modelId.IsEmpty()) { + nsCOMPtr modelElement; + domDoc->GetElementById(modelId, getter_AddRefs(modelElement)); + NS_IF_ADDREF(*aModel = modelElement); - nsCOMPtr modelWrapper; - - if (modelId.IsEmpty()) { - // No model given, so use the first one in the document. - nsCOMPtr nodes; - domDoc->GetElementsByTagNameNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS), - NS_LITERAL_STRING("model"), - getter_AddRefs(nodes)); - - if (!nodes) - return nsnull; - - nodes->Item(0, getter_AddRefs(modelWrapper)); - } else { - nsCOMPtr wrapperElement; - domDoc->GetElementById(modelId, getter_AddRefs(wrapperElement)); - modelWrapper = wrapperElement; + // Not tag found for that ID + NS_ENSURE_TRUE(*aModel, NS_ERROR_FAILURE); } - - // We're releasing this reference, but the node is owned by the DOM. - return modelWrapper; } - // If no bind was given, we assume the given element is a child - // of the model. - return GetParentModel(aElement); + nsresult rv = FindParentContext(aElement, + aModel, + aContextNode, + aContextPosition, + aContextSize); + // CASE 3/4: Use parent's model / first model in document. + // If FindParentContext() does not find a parent context but |aModel| is not + // set, it sets the model to the first model in the document. + + return rv; +} + +/* static */ already_AddRefed +nsXFormsUtils::GetModel(nsIDOMElement *aElement, + PRUint32 aElementFlags) + +{ + + nsCOMPtr model; + nsCOMPtr contextNode; + nsCOMPtr bind; + + nsresult rv = GetNodeContext(aElement, + aElementFlags, + getter_AddRefs(model), + getter_AddRefs(bind), + getter_AddRefs(contextNode)); + + NS_ENSURE_SUCCESS(rv, nsnull); + + nsIDOMNode *result = nsnull; + if (model) + CallQueryInterface(model, &result); // addrefs + return result; } /* static */ already_AddRefed nsXFormsUtils::EvaluateXPath(const nsAString &aExpression, nsIDOMNode *aContextNode, nsIDOMNode *aResolverNode, - PRUint16 aResultType) + PRUint16 aResultType, + PRInt32 aContextPosition, + PRInt32 aContextSize) { nsCOMPtr doc; aContextNode->GetOwnerDocument(getter_AddRefs(doc)); @@ -283,6 +316,8 @@ nsXFormsUtils::EvaluateXPath(const nsAString &aExpression, eval->CreateNSResolver(aResolverNode, getter_AddRefs(resolver)); NS_ENSURE_TRUE(resolver, nsnull); + /// + /// @todo Evaluate() should use aContextPosition and aContextSize nsCOMPtr supResult; eval->Evaluate(aExpression, aContextNode, resolver, aResultType, nsnull, getter_AddRefs(supResult)); @@ -389,16 +424,25 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement, nsIDOMNode **aModel, nsIDOMElement **aBind) { - // A control may be attached to a model by either using the 'bind' - // attribute to give the id of a bind element, or using the 'model' - // attribute to give the id of a model. If neither of these are given, - // the control belongs to the first model in the document. + if (!aElement || !aBind || !aModel) { + return nsnull; + } *aBind = nsnull; + *aModel = nsnull; - NS_IF_ADDREF(*aModel = GetModelAndBind(aElement, aElementFlags, aBind)); - if (!*aModel) - return nsnull; + nsCOMPtr contextNode; + PRInt32 contextPosition; + PRInt32 contextSize; + nsresult rv = GetNodeContext(aElement, + aElementFlags, + aModel, + aBind, + getter_AddRefs(contextNode), + &contextPosition, + &contextSize); + + NS_ENSURE_SUCCESS(rv, nsnull); // If there is a bind element, we just evaluate its nodeset. if (*aBind) @@ -415,27 +459,34 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement, expr.Assign(aDefaultRef); } - // Get the instance data and evaluate the xpath expression. - // XXXfixme when xpath extensions are implemented (instance()) - nsCOMPtr instanceDoc; - nsCOMPtr model = do_QueryInterface(*aModel); - if (!model) { - // The referenced model is not actually a model element, or does not exist. - return nsnull; + if (!contextNode) { + nsCOMPtr instanceDoc; + nsCOMPtr model = do_QueryInterface(*aModel); + if (!model) { + // The referenced model is not actually a model element, or does not exist. + return nsnull; + } + + model->GetInstanceDocument(NS_LITERAL_STRING(""), + getter_AddRefs(instanceDoc)); + + if (!instanceDoc) + return nsnull; + + instanceDoc->GetDocumentElement(getter_AddRefs(contextNode)); + + if (!contextNode) { + return nsnull; // this will happen if the doc is still loading + } } - model->GetInstanceDocument(NS_LITERAL_STRING(""), - getter_AddRefs(instanceDoc)); - - if (!instanceDoc) - return nsnull; - - nsCOMPtr docElement; - instanceDoc->GetDocumentElement(getter_AddRefs(docElement)); - if (!docElement) - return nsnull; // this will happen if the doc is still loading - - return EvaluateXPath(expr, docElement, aElement, aResultType); + // Evaluate |expr| + return EvaluateXPath(expr, + contextNode, + aElement, + aResultType, + contextSize, + contextPosition); } /* static */ void @@ -694,3 +745,93 @@ nsXFormsUtils::CloneScriptingInterfaces(const nsIID *aIIDList, *aOutCount = aIIDCount; return NS_OK; } + +/* static */ nsresult +nsXFormsUtils::FindParentContext(nsIDOMElement *aElement, + nsIDOMNode **aModel, + nsIDOMElement **aContextNode, + PRInt32 *aContextPosition, + PRInt32 *aContextSize) +{ + NS_ENSURE_ARG(aElement); + NS_ENSURE_ARG_POINTER(aModel); + NS_ENSURE_ARG_POINTER(aContextNode); + + nsCOMPtr elementNode = do_QueryInterface(aElement); + NS_ENSURE_TRUE(elementNode, NS_ERROR_FAILURE); + + nsCOMPtr curNode; + nsresult rv = elementNode->GetParentNode(getter_AddRefs(curNode)); + NS_ENSURE_SUCCESS(rv, NS_OK); + + // If a model is set, get its ID + nsAutoString childModelID; + if (*aModel) { + nsCOMPtr modelElement = do_QueryInterface(*aModel); + NS_ENSURE_TRUE(modelElement, NS_ERROR_FAILURE); + modelElement->GetAttribute(NS_LITERAL_STRING("id"), childModelID); + } + + // Find our context: + // Iterate over all parents and find first one that implements nsIXFormsContextControl, + // and has the same model as us. + nsCOMPtr temp; + nsAutoString contextModelID; + while (curNode) { + nsCOMPtr contextControl = do_QueryInterface(curNode); + nsCOMPtr cElement = do_QueryInterface(curNode); + if (contextControl && cElement) { + PRInt32 cSize; + PRInt32 cPosition; + nsCOMPtr tempNode; + rv = contextControl->GetContext(contextModelID, getter_AddRefs(tempNode), &cPosition, &cSize); + NS_ENSURE_SUCCESS(rv, rv); + // If the call failed, it means that we _have_ a parent which sets the + // context but it is invalid, ie. the XPath expression could have + // generated an error. + + if (childModelID.IsEmpty() + || childModelID.Equals(contextModelID)) { + NS_ADDREF(*aContextNode = tempNode); + if (aContextSize) + *aContextSize = cSize; + if (aContextPosition) + *aContextPosition = cPosition; + break; + } + } + // Next ancestor + temp.swap(curNode); + rv = temp->GetParentNode(getter_AddRefs(curNode)); + NS_ENSURE_SUCCESS(rv, NS_OK); + } + + // Child had no model set, set it + if (!*aModel) { + nsCOMPtr domDoc; + nsresult rv = aElement->GetOwnerDocument(getter_AddRefs(domDoc)); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); + + if (!*aContextNode || contextModelID.IsEmpty()) { + // We have either not found a context node, or we have found one where + // the model ID is empty. That means we use the first model in document + nsCOMPtr nodes; + domDoc->GetElementsByTagNameNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS), + NS_LITERAL_STRING("model"), + getter_AddRefs(nodes)); + // No model element in document! + NS_ENSURE_TRUE(nodes, NS_ERROR_FAILURE); + + nodes->Item(0, aModel); + } else { + // Get the model with the correct ID + nsCOMPtr modelElement; + domDoc->GetElementById(contextModelID, getter_AddRefs(modelElement)); + NS_IF_ADDREF(*aModel = modelElement); + } + } + + return NS_OK; +} diff --git a/extensions/xforms/nsXFormsUtils.h b/extensions/xforms/nsXFormsUtils.h index 5bf6447de712..034f9336af71 100644 --- a/extensions/xforms/nsXFormsUtils.h +++ b/extensions/xforms/nsXFormsUtils.h @@ -133,18 +133,43 @@ public: * Locate the model that is a parent of |aElement|. This method walks up the * content tree looking for the containing model. */ - static NS_HIDDEN_(nsIDOMNode*) - GetParentModel(nsIDOMElement *aElement); + static NS_HIDDEN_(void) + GetParentModel(nsIDOMElement *aElement, nsIDOMNode **aModel); /** - * Locate the model that |aElement| is bound to, and if applicable, the - * \ element that it uses. The model is returned and the - * bind element is returned (addrefed) in |aBindElement|. + * Find the evaluation context for an element. + * + * That is, the model it is bound to (|aModel|), and if applicable the + * \ element that it uses (|aBindElement| and the context node + * (|aContextNode|). + * + * @param aElement The element + * @param aElementFlags Flags describing characteristics of aElement + * @param aModel The \ for the element + * @param aBindElement The \ the element is bound to (if any) + * @param aContextNode The context node for the element */ - static NS_HIDDEN_(nsIDOMNode*) - GetModelAndBind(nsIDOMElement *aElement, - PRUint32 aElementFlags, - nsIDOMElement **aBindElement); + static NS_HIDDEN_(nsresult) + GetNodeContext(nsIDOMElement *aElement, + PRUint32 aElementFlags, + nsIDOMNode **aModel, + nsIDOMElement **aBindElement, + nsIDOMElement **aContextNode, + PRInt32 *aContextPosition = nsnull, + PRInt32 *aContextSize = nsnull); + + /** + * Locate the model for an element. + * + * @note Actually it is just a shortcut for GetNodeContext(). + * + * @param aElement The element + * @param aElementFlags Flags describing characteristics of aElement + * @return The model + */ + static NS_HIDDEN_(already_AddRefed) + GetModel(nsIDOMElement *aElement, + PRUint32 aElementFlags = ELEMENT_WITH_MODEL_ATTR); /** * Evaluate a 'bind' or |aBindingAttr| attribute on |aElement|. @@ -191,7 +216,9 @@ public: EvaluateXPath(const nsAString &aExpression, nsIDOMNode *aContextNode, nsIDOMNode *aResolverNode, - PRUint16 aResultType); + PRUint16 aResultType, + PRInt32 aContextPosition = 1, + PRInt32 aContextSize = 1); /** * Given a node in the instance data, get its string value according @@ -247,6 +274,24 @@ public: unsigned int aIIDCount, PRUint32 *aOutCount, nsIID ***aOutArray); + + /** + * Returns the context for the element, if set by a parent node. + * + * Controls inheriting from nsIXFormsContextControl sets the context for its children. + * + * @param aElement The document element of the caller + * @param aModel The model for |aElement| (if (!*aModel), it is set) + * @param aContextNode The resulting context node + * @param aContextPosition The resulting context position + * @param aContextSize The resulting context size + */ + static NS_HIDDEN_(nsresult) FindParentContext(nsIDOMElement *aElement, + nsIDOMNode **aModel, + nsIDOMElement **aContextNode, + PRInt32 *aContextPosition, + PRInt32 *aContextSize); + }; #endif