diff --git a/mozilla/extensions/xforms/Makefile.in b/mozilla/extensions/xforms/Makefile.in index 9dd17fd07e3..cb180e662c9 100644 --- a/mozilla/extensions/xforms/Makefile.in +++ b/mozilla/extensions/xforms/Makefile.in @@ -71,6 +71,7 @@ REQUIRES = \ schemavalidation \ intl \ pref \ + htmlparser \ $(NULL) XPIDLSRCS = \ @@ -145,6 +146,7 @@ CPPSRCS = \ nsXFormsControlStub.cpp \ nsXFormsUtilityService.cpp \ nsXFormsDelegateStub.cpp \ + nsXFormsLazyInstanceElement.cpp \ $(NULL) EXTRA_DSO_LDOPTS = $(MOZ_COMPONENT_LIBS) diff --git a/mozilla/extensions/xforms/contents.rdf b/mozilla/extensions/xforms/contents.rdf deleted file mode 100755 index e21f0bdcdb1..00000000000 --- a/mozilla/extensions/xforms/contents.rdf +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/mozilla/extensions/xforms/nsIInstanceElementPrivate.idl b/mozilla/extensions/xforms/nsIInstanceElementPrivate.idl index bb396f9e308..a114bd55447 100644 --- a/mozilla/extensions/xforms/nsIInstanceElementPrivate.idl +++ b/mozilla/extensions/xforms/nsIInstanceElementPrivate.idl @@ -39,6 +39,7 @@ #include "nsISupports.idl" interface nsIDOMDocument; +interface nsIDOMElement; /** * Private interface implemented by the instance element. @@ -50,6 +51,7 @@ interface nsIInstanceElementPrivate : nsISupports * The document which holds the live instance data. */ attribute nsIDOMDocument document; + readonly attribute nsIDOMElement element; void backupOriginalDocument(); void restoreOriginalDocument(); }; diff --git a/mozilla/extensions/xforms/nsIModelElementPrivate.idl b/mozilla/extensions/xforms/nsIModelElementPrivate.idl index 3d12f0affec..6709f36935b 100644 --- a/mozilla/extensions/xforms/nsIModelElementPrivate.idl +++ b/mozilla/extensions/xforms/nsIModelElementPrivate.idl @@ -42,6 +42,11 @@ interface nsIXFormsControl; interface nsISchemaType; interface nsIInstanceElementPrivate; interface nsIDOMNode; +%{C++ + #include "nsCOMArray.h" +%} + +[ptr] native nsCOMArrayPtr(nsCOMArray); /** * Private interface implemented by the model element for other @@ -119,4 +124,21 @@ interface nsIModelElementPrivate : nsIXFormsModelElement * @param aBoundNode The node the control is bound to */ void setStates(in nsIXFormsControl aControl, in nsIDOMNode aBoundNode); + + /** + * The list of instance elements held by this model. Includes lazy-authored + * instance elements + */ + readonly attribute nsCOMArrayPtr instanceList; + + /** + * Add an instance element to the model's instance list + * @param aInstanceElement The instance element to add to the list + */ + void addInstanceElement(in nsIInstanceElementPrivate aInstanceElement); + + /** + * This attribute is set when the model's instance was lazy authored + */ + readonly attribute boolean lazyAuthored; }; diff --git a/mozilla/extensions/xforms/nsXFormsControlStub.cpp b/mozilla/extensions/xforms/nsXFormsControlStub.cpp index 769572aa5cf..34eec696556 100644 --- a/mozilla/extensions/xforms/nsXFormsControlStub.cpp +++ b/mozilla/extensions/xforms/nsXFormsControlStub.cpp @@ -159,6 +159,19 @@ nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute, if (mBoundNode && mModel) { mModel->SetStates(this, mBoundNode); + } else if (mModel) { + // we should have been successful. Must be pointing to a node that + // doesn't exist in the instance document. Disable the control + // per 4.2.2 in the spec + + // Set pseudo class + /// + /// @bug Set via attributes right now. Bug 271720. (XXX) + mElement->SetAttribute(NS_LITERAL_STRING("disabled"), + NS_LITERAL_STRING("1")); + mElement->RemoveAttribute(NS_LITERAL_STRING("enabled")); + // Dispatch event + nsXFormsUtils::DispatchEvent(mElement, eEvent_Disabled); } if (aResult) { diff --git a/mozilla/extensions/xforms/nsXFormsInstanceElement.cpp b/mozilla/extensions/xforms/nsXFormsInstanceElement.cpp index 6a79007a3bf..d099026e09a 100644 --- a/mozilla/extensions/xforms/nsXFormsInstanceElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsInstanceElement.cpp @@ -140,9 +140,9 @@ nsXFormsInstanceElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper) { aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_ATTRIBUTE_SET | nsIXTFElement::NOTIFY_ATTRIBUTE_REMOVED | + nsIXTFElement::NOTIFY_PARENT_CHANGED | nsIXTFElement::NOTIFY_BEGIN_ADDING_CHILDREN | nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN); - // XXX observe nsIXTFElement::NOTIFY_PARENT_CHANGED ?? nsCOMPtr node; aWrapper->GetElementNode(getter_AddRefs(node)); @@ -157,6 +157,23 @@ nsXFormsInstanceElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper) return NS_OK; } +NS_IMETHODIMP +nsXFormsInstanceElement::ParentChanged(nsIDOMElement *aNewParent) +{ + if (!aNewParent) + return NS_OK; + + // Once we are set up in the DOM, can find the model and make sure that this + // instance is on the list of instance elements that model keeps + nsCOMPtr model = GetModel(); + NS_ENSURE_TRUE(model, NS_ERROR_FAILURE); + nsCOMPtr instance = + NS_STATIC_CAST(nsIInstanceElementPrivate*, this); + model->AddInstanceElement(this); + + return NS_OK; +} + // nsIInterfaceRequestor NS_IMETHODIMP @@ -298,10 +315,11 @@ nsXFormsInstanceElement::RestoreOriginalDocument() { nsresult rv = NS_OK; - // This is called when xforms-ready is received by the model. By now we know - // that the instance document, whether external or inline, is loaded into - // mDocument. Get the root node, clone it, and insert it into our copy of - // the document. This is the magic behind getting xforms-reset to work. + // This is called when xforms-reset is received by the model. We assume + // that the backup of the instance document has been populated and is + // loaded into mOriginalDocument. Get the backup's root node, clone it, and + // insert it into the live copy of the instance document. This is the magic + // behind getting xforms-reset to work. if(mDocument && mOriginalDocument) { nsCOMPtr newNode, instanceRootNode, nodeReturn; nsCOMPtr instanceRoot; @@ -332,6 +350,12 @@ nsXFormsInstanceElement::RestoreOriginalDocument() return rv; } +NS_IMETHODIMP +nsXFormsInstanceElement::GetElement(nsIDOMElement **aElement) +{ + NS_IF_ADDREF(*aElement = mElement); + return NS_OK; +} // private methods @@ -446,7 +470,7 @@ nsXFormsInstanceElement::CreateInstanceDocument() NS_ENSURE_SUCCESS(rv, rv); rv = domImpl->CreateDocument(EmptyString(), EmptyString(), nsnull, - getter_AddRefs(mDocument)); + getter_AddRefs(mDocument)); NS_ENSURE_SUCCESS(rv, rv); // I don't know if not being able to create a backup document is worth diff --git a/mozilla/extensions/xforms/nsXFormsInstanceElement.h b/mozilla/extensions/xforms/nsXFormsInstanceElement.h index 27853e67899..bb3a369bc62 100644 --- a/mozilla/extensions/xforms/nsXFormsInstanceElement.h +++ b/mozilla/extensions/xforms/nsXFormsInstanceElement.h @@ -78,6 +78,7 @@ public: NS_IMETHOD BeginAddingChildren(); NS_IMETHOD DoneAddingChildren(); NS_IMETHOD OnCreated(nsIXTFGenericElementWrapper *aWrapper); + NS_IMETHOD ParentChanged(nsIDOMElement *aNewParent); nsXFormsInstanceElement() NS_HIDDEN; diff --git a/mozilla/extensions/xforms/nsXFormsLazyInstanceElement.cpp b/mozilla/extensions/xforms/nsXFormsLazyInstanceElement.cpp new file mode 100644 index 00000000000..6a85a8d0731 --- /dev/null +++ b/mozilla/extensions/xforms/nsXFormsLazyInstanceElement.cpp @@ -0,0 +1,191 @@ +/* -*- 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 + * IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Aaron Reed + * + * 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 "nsXFormsLazyInstanceElement.h" +#include "nsIDOMElement.h" +#include "nsIDocument.h" +#include "nsIDOMDOMImplementation.h" + +NS_IMPL_ISUPPORTS_INHERITED2(nsXFormsLazyInstanceElement, + nsXFormsStubElement, + nsIInstanceElementPrivate, + nsIInterfaceRequestor) + +nsXFormsLazyInstanceElement::nsXFormsLazyInstanceElement() +{ +} + +// nsIInstanceElementPrivate + +NS_IMETHODIMP +nsXFormsLazyInstanceElement::GetDocument(nsIDOMDocument **aDocument) +{ + NS_IF_ADDREF(*aDocument = mDocument); + return NS_OK; +} + +NS_IMETHODIMP +nsXFormsLazyInstanceElement::SetDocument(nsIDOMDocument *aDocument) +{ + mDocument = aDocument; + return NS_OK; +} + + +NS_IMETHODIMP +nsXFormsLazyInstanceElement::BackupOriginalDocument() +{ + nsresult rv = NS_OK; + + // This is called when xforms-ready is received by the model. By now we know + // that the lazy instance document has been populated and is loaded into + // mDocument. Get the root node, clone it, and insert it into our copy of + // the document. This is the magic behind getting xforms-reset to work. + if(mDocument && mOriginalDocument) { + nsCOMPtr newNode; + nsCOMPtr instanceRoot; + rv = mDocument->GetDocumentElement(getter_AddRefs(instanceRoot)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(instanceRoot, NS_ERROR_FAILURE); + + nsCOMPtr nodeReturn; + rv = instanceRoot->CloneNode(PR_TRUE, getter_AddRefs(newNode)); + if(NS_SUCCEEDED(rv)) { + rv = mOriginalDocument->AppendChild(newNode, getter_AddRefs(nodeReturn)); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), + "failed to set up original instance document"); + } + } + return rv; +} + +NS_IMETHODIMP +nsXFormsLazyInstanceElement::RestoreOriginalDocument() +{ + nsresult rv = NS_OK; + + // This is called when xforms-reset is received by the model. We assume + // that the backup of the lazy instance document has been populated and is + // loaded into mOriginalDocument. Get the backup's root node, clone it, and + // insert it into the live copy of the instance document. This is the magic + // behind getting xforms-reset to work. + if(mDocument && mOriginalDocument) { + nsCOMPtr newNode, instanceRootNode, nodeReturn; + nsCOMPtr instanceRoot; + + // first remove all the old stuff + rv = mDocument->GetDocumentElement(getter_AddRefs(instanceRoot)); + if(NS_SUCCEEDED(rv)) { + if(instanceRoot) { + rv = mDocument->RemoveChild(instanceRoot, getter_AddRefs(nodeReturn)); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + // now all of the garbage is out o' there! Put the original data back + // into mDocument + rv = mOriginalDocument->GetDocumentElement(getter_AddRefs(instanceRoot)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(instanceRoot, NS_ERROR_FAILURE); + instanceRootNode = do_QueryInterface(instanceRoot); + + rv = instanceRootNode->CloneNode(PR_TRUE, getter_AddRefs(newNode)); + if(NS_SUCCEEDED(rv)) { + rv = mDocument->AppendChild(newNode, getter_AddRefs(nodeReturn)); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), + "failed to restore original instance document"); + } + } + return rv; +} + +NS_IMETHODIMP +nsXFormsLazyInstanceElement::GetElement(nsIDOMElement **aElement) +{ + *aElement = nsnull; + return NS_OK; +} + +// nsIInterfaceRequestor + +NS_IMETHODIMP +nsXFormsLazyInstanceElement::GetInterface(const nsIID & aIID, void **aResult) +{ + *aResult = nsnull; + return QueryInterface(aIID, aResult); +} + +nsresult +nsXFormsLazyInstanceElement::CreateLazyInstanceDocument(nsIDOMDocument *aXFormsDocument) +{ + NS_ENSURE_ARG(aXFormsDocument); + + // Do not try to load an instance document if the current document is not + // associated with a DOM window. This could happen, for example, if some + // XForms document loaded itself as instance data (which is what the Forms + // 1.0 testsuite does). + nsCOMPtr d = do_QueryInterface(aXFormsDocument); + if (d && !d->GetScriptGlobalObject()) + return NS_ERROR_FAILURE; + + nsCOMPtr domImpl; + nsresult rv = aXFormsDocument->GetImplementation(getter_AddRefs(domImpl)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = domImpl->CreateDocument(EmptyString(), EmptyString(), nsnull, + getter_AddRefs(mDocument)); + NS_ENSURE_SUCCESS(rv, rv); + + // Lazy authored instance documents have a root named "instanceData" + nsCOMPtr instanceDataElement; + nsCOMPtr childReturn; + rv = mDocument->CreateElementNS(EmptyString(), + NS_LITERAL_STRING("instanceData"), + getter_AddRefs(instanceDataElement)); + NS_ENSURE_SUCCESS(rv, rv); + rv = mDocument->AppendChild(instanceDataElement, getter_AddRefs(childReturn)); + NS_ENSURE_SUCCESS(rv, rv); + + // I don't know if not being able to create a backup document is worth + // failing this function. Since it probably won't be used often, we'll + // let it slide. But it probably does mean that things are going south + // with the browser. + domImpl->CreateDocument(EmptyString(), EmptyString(), nsnull, + getter_AddRefs(mOriginalDocument)); + return rv; +} diff --git a/mozilla/extensions/xforms/nsXFormsLazyInstanceElement.h b/mozilla/extensions/xforms/nsXFormsLazyInstanceElement.h new file mode 100644 index 00000000000..e4a4cf4b759 --- /dev/null +++ b/mozilla/extensions/xforms/nsXFormsLazyInstanceElement.h @@ -0,0 +1,76 @@ +/* -*- 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 + * IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Aaron Reed + * + * 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 ***** */ + +#ifndef nsXFormsLazyInstanceElement_h_ +#define nsXFormsLazyInstanceElement_h_ + +#include "nsXFormsStubElement.h" +#include "nsIDOMDocument.h" +#include "nsCOMPtr.h" +#include "nsIModelElementPrivate.h" +#include "nsIInstanceElementPrivate.h" +#include "nsIInterfaceRequestor.h" + +class nsIDOMElement; + +/** + * Implementation of the XForms \ element created through lazy + * authoring. It creates an instance document by cloning the contained + * instance data. + */ + +class nsXFormsLazyInstanceElement : public nsXFormsStubElement, + public nsIInstanceElementPrivate, + public nsIInterfaceRequestor +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIINSTANCEELEMENTPRIVATE + NS_DECL_NSIINTERFACEREQUESTOR + + NS_HIDDEN_(nsresult) CreateLazyInstanceDocument(nsIDOMDocument *aXFormsDocument); + + nsXFormsLazyInstanceElement() NS_HIDDEN; + +private: + + nsCOMPtr mDocument; + nsCOMPtr mOriginalDocument; +}; + +#endif diff --git a/mozilla/extensions/xforms/nsXFormsModelElement.cpp b/mozilla/extensions/xforms/nsXFormsModelElement.cpp index a77f2ad710e..8198c1acb39 100644 --- a/mozilla/extensions/xforms/nsXFormsModelElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsModelElement.cpp @@ -76,6 +76,7 @@ #include "nsISchema.h" #include "nsAutoPtr.h" #include "nsArray.h" +#include "nsXFormsLazyInstanceElement.h" #ifdef DEBUG //#define DEBUG_MODEL @@ -248,7 +249,9 @@ nsXFormsModelElement::nsXFormsModelElement() mSchemaTotal(0), mPendingInstanceCount(0), mDocumentLoaded(PR_FALSE), - mNeedsRefresh(PR_FALSE) + mNeedsRefresh(PR_FALSE), + mInstanceList(16), + mLazyModel(PR_FALSE) { } @@ -268,6 +271,8 @@ nsXFormsModelElement::OnDestroyed() mElement = nsnull; mSchemas = nsnull; + + mInstanceList.Clear(); return NS_OK; } @@ -398,6 +403,22 @@ nsXFormsModelElement::DoneAddingChildren() } } + // If all of the children are added and there aren't any instance elements, + // yet, then we need to make sure that one is ready in case the form author + // is using lazy authoring. + PRUint32 instCount = mInstanceList.Count(); + if (!instCount) { + nsCOMPtr domDoc; + mElement->GetOwnerDocument(getter_AddRefs(domDoc)); + if (domDoc) { + nsXFormsLazyInstanceElement *lazyInstance = + new nsXFormsLazyInstanceElement(); + lazyInstance->CreateLazyInstanceDocument(domDoc); + AddInstanceElement(lazyInstance); + mLazyModel = PR_TRUE; + } + } + // 2. construct an XPath data model from inline or external initial instance // data. This is done by our child instance elements as they are inserted // into the document, and all of the instances will be processed by this @@ -990,32 +1011,34 @@ nsXFormsModelElement::FindInstanceElement(const nsAString &aID, { *aElement = nsnull; - nsCOMPtr children; - mElement->GetChildNodes(getter_AddRefs(children)); + PRUint32 instCount = mInstanceList.Count(); + if (instCount) { + nsCOMPtr element; + nsAutoString id; + for (int i = 0; i < instCount; ++i) { + nsIInstanceElementPrivate* instEle = mInstanceList.ObjectAt(i); + instEle->GetElement(getter_AddRefs(element)); - if (!children) - return NS_OK; - - PRUint32 childCount = 0; - children->GetLength(&childCount); - - nsCOMPtr node; - nsCOMPtr element; - nsAutoString id; - - for (PRUint32 i = 0; i < childCount; ++i) { - children->Item(i, getter_AddRefs(node)); - NS_ASSERTION(node, "incorrect NodeList length?"); - - element = do_QueryInterface(node); - if (!element) - continue; - - element->GetAttribute(NS_LITERAL_STRING("id"), id); - if (aID.IsEmpty() || aID.Equals(id)) { - CallQueryInterface(element, aElement); - if (*aElement) + if (aID.IsEmpty()) { + NS_ADDREF(instEle); + *aElement = instEle; break; + } else if (!element) { + // this should only happen if the instance on the list is lazy authored + // and as far as I can tell, a lazy authored instance should be the + // first (and only) instance in the model and unable to have an ID. + // But that isn't clear to me reading the spec, so for now + // we'll play it safe in case the WG more clearly defines lazy authoring + // in the future. + continue; + } + + element->GetAttribute(NS_LITERAL_STRING("id"), id); + if (aID.Equals(id)) { + NS_ADDREF(instEle); + *aElement = instEle; + break; + } } } @@ -1155,6 +1178,12 @@ nsXFormsModelElement::GetContext(nsAString &aModelID, return NS_OK; } +NS_IMETHODIMP +nsXFormsModelElement::GetLazyAuthored(PRBool *aLazyInstance) +{ + *aLazyInstance = mLazyModel; + return NS_OK; +} // internal methods @@ -1244,40 +1273,23 @@ nsXFormsModelElement::Ready() void nsXFormsModelElement::BackupOrRestoreInstanceData(PRBool restore) { - // I imagine that this can be done more elegantly. But in the end, the - // bulk of the work is walking the model's children looking for instance - // elements. After one is found, just need to ask the instance element to - // backup or restore. - nsCOMPtr children; - mElement->GetChildNodes(getter_AddRefs(children)); + PRUint32 instCount = mInstanceList.Count(); + if (instCount) { + for (int i = 0; i < instCount; ++i) { + nsIInstanceElementPrivate *instance = mInstanceList.ObjectAt(i); - if (!children) - return; - - PRUint32 childCount = 0; - children->GetLength(&childCount); - - nsCOMPtr node; - nsCOMPtr instance; - - for (PRUint32 i = 0; i < childCount; ++i) { - children->Item(i, getter_AddRefs(node)); - NS_ASSERTION(node, "incorrect NodeList length?"); - - instance = do_QueryInterface(node); - if (!instance) - continue; - - // Don't know what to do with error if we get one. - // Restore/BackupOriginalDocument will already output warnings. - if(restore) { - instance->RestoreOriginalDocument(); - } - else { - instance->BackupOriginalDocument(); + // Don't know what to do with error if we get one. + // Restore/BackupOriginalDocument will already output warnings. + if(restore) { + instance->RestoreOriginalDocument(); + } + else { + instance->BackupOriginalDocument(); + } } } + } @@ -1614,6 +1626,26 @@ nsXFormsModelElement::SetStates(nsIXFormsControl *aControl, nsIDOMNode *aBoundNo return SetStatesInternal(aControl, aBoundNode, PR_TRUE); } +NS_IMETHODIMP +nsXFormsModelElement::GetInstanceList(nsCOMArray **aInstanceList) +{ + if (aInstanceList) { + *aInstanceList = &mInstanceList; + } + return NS_OK; +} + +nsresult +nsXFormsModelElement::AddInstanceElement(nsIInstanceElementPrivate *aInstEle) +{ + // always append to the end of the list. We need to keep the elements in + // document order since the first instance element is the default instance + // document for the model. + mInstanceList.AppendObject(aInstEle); + + return NS_OK; +} + /* static */ void nsXFormsModelElement::Startup() { diff --git a/mozilla/extensions/xforms/nsXFormsModelElement.h b/mozilla/extensions/xforms/nsXFormsModelElement.h index 0aacac6989c..e0f56a02051 100644 --- a/mozilla/extensions/xforms/nsXFormsModelElement.h +++ b/mozilla/extensions/xforms/nsXFormsModelElement.h @@ -209,6 +209,15 @@ private: // This flag indicates whether a xforms-rebuild has been called, but no // xforms-revalidate yet PRBool mNeedsRefresh; + + /** + * List of instance elements contained by this model, including lazy-authored + * instance elements. + */ + nsCOMArray mInstanceList; + + /** Indicates whether the model's instance was built by lazy authoring */ + PRBool mLazyModel; }; /** diff --git a/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp b/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp index 449ee89a2ca..17c5f02a805 100644 --- a/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp +++ b/mozilla/extensions/xforms/nsXFormsSubmissionElement.cpp @@ -775,11 +775,20 @@ nsXFormsSubmissionElement::SerializeDataXML(nsIDOMNode *data, // add namespaces from the main document to the submission document, but only // if the instance data is local, not remote. - PRBool hasSrc = PR_FALSE; + PRBool serialize = PR_FALSE; nsCOMPtr instanceElement(do_QueryInterface(instanceNode)); - instanceElement->HasAttribute(NS_LITERAL_STRING("src"), &hasSrc); - if (!hasSrc) { + // make sure that this is a DOMElement. It won't be if it was lazy + // authored. Lazy authored instance documents don't inherit namespaces + // from parent nodes or the original document (in formsPlayer and Novell, + // at least). + if (instanceElement) { + PRBool hasSrc = PR_FALSE; + instanceElement->HasAttribute(NS_LITERAL_STRING("src"), &hasSrc); + serialize = !hasSrc; + } + + if (serialize) { rv = AddNameSpaces(newDocElm, node); NS_ENSURE_SUCCESS(rv, rv); diff --git a/mozilla/extensions/xforms/nsXFormsUtilityService.cpp b/mozilla/extensions/xforms/nsXFormsUtilityService.cpp index 2c082170fe4..847b0ae9abc 100644 --- a/mozilla/extensions/xforms/nsXFormsUtilityService.cpp +++ b/mozilla/extensions/xforms/nsXFormsUtilityService.cpp @@ -116,12 +116,10 @@ nsXFormsUtilityService::IsNodeAssocWithModel( nsIDOMNode *aNode, if (modelNode && (modelNode == aModel)) { *aModelAssocWithNode = PR_TRUE; - } - else { + } else { *aModelAssocWithNode = PR_FALSE; } - } - else { + } else { // We are assuming that if the node coming in isn't a proper XForms element, // then it is an instance element in an instance doc. Now we just have // to determine if the given model contains this instance document. @@ -137,26 +135,16 @@ nsXFormsUtilityService::IsNodeAssocWithModel( nsIDOMNode *aNode, // document. If it is equal to the document that contains aNode, // then aNode is associated with this aModel element and we can return // true. - nsCOMPtr children; - aModel->GetChildNodes(getter_AddRefs(children)); - - if (!children) - return NS_OK; - - PRUint32 childCount = 0; - children->GetLength(&childCount); - - nsCOMPtr node; + nsCOMPtrmodel = do_QueryInterface(modelEle); nsCOMPtr instElement; nsCOMPtr instDocument; - - for (PRUint32 i = 0; i < childCount; ++i) { - children->Item(i, getter_AddRefs(node)); - NS_ASSERTION(node, "incorrect NodeList length?"); - - instElement = do_QueryInterface(node); - if (!instElement) - continue; + + nsCOMArray *instList = nsnull; + model->GetInstanceList(&instList); + NS_ENSURE_TRUE(instList, NS_ERROR_FAILURE); + + for (int i = 0; i < instList->Count(); ++i) { + instElement = instList->ObjectAt(i); instElement->GetDocument(getter_AddRefs(instDocument)); if (instDocument) { diff --git a/mozilla/extensions/xforms/nsXFormsUtils.cpp b/mozilla/extensions/xforms/nsXFormsUtils.cpp index 0368cbe0688..1d9b8358267 100644 --- a/mozilla/extensions/xforms/nsXFormsUtils.cpp +++ b/mozilla/extensions/xforms/nsXFormsUtils.cpp @@ -87,6 +87,7 @@ #include "nsIURI.h" #include "nsIPrivateDOMEvent.h" #include "nsIDOMNamedNodeMap.h" +#include "nsIParserService.h" #define CANCELABLE 0x01 #define BUBBLES 0x02 @@ -536,6 +537,73 @@ nsXFormsUtils::EvaluateNodeBinding(nsIDOMElement *aElement, aDeps, aIndexesUsed); + // If the evaluation failed because the node wasn't there and we should be + // lazy authoring, then create it (if the situation qualifies, of course). + // Novell only allows lazy authoring of single bound nodes. + if (aResultType == nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE) { + if (res) { + nsCOMPtr node; + rv = res->GetSingleNodeValue(getter_AddRefs(node)); + if (NS_SUCCEEDED(rv) && !node) { + PRBool lazy = PR_FALSE; + if (*aModel) + (*aModel)->GetLazyAuthored(&lazy); + if (lazy) { + // according to sec 4.2.2 in the spec, "An instance data element node + // will be created using the binding expression from the user + // interface control as the name. If the name is not a valid QName, + // processing halts with an exception (xforms-binding-exception)" + nsCOMPtr parserService = + do_GetService(NS_PARSERSERVICE_CONTRACTID); + if (NS_SUCCEEDED(rv) && parserService) { + const PRUnichar* colon; + rv = parserService->CheckQName(expr, PR_TRUE, &colon); + if (NS_SUCCEEDED(rv)) { + nsAutoString namespaceURI(EmptyString()); + + // if we detect a namespace, we'll add it to the node, otherwise + // we'll use the empty namespace. If we should have gotten a + // namespace and didn't, then we might as well give up. + if (colon) { + nsCOMPtr dom3node = do_QueryInterface(aElement); + rv = dom3node->LookupNamespaceURI(Substring(expr.get(), colon), + namespaceURI); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (NS_SUCCEEDED(rv)) { + nsCOMArray *instList = nsnull; + (*aModel)->GetInstanceList(&instList); + nsCOMPtr instance = + instList->ObjectAt(0); + nsCOMPtr domdoc; + instance->GetDocument(getter_AddRefs(domdoc)); + nsCOMPtr instanceDataEle; + nsCOMPtr childReturn; + rv = domdoc->CreateElementNS(namespaceURI, expr, + getter_AddRefs(instanceDataEle)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr instanceRoot; + rv = domdoc->GetDocumentElement(getter_AddRefs(instanceRoot)); + rv = instanceRoot->AppendChild(instanceDataEle, + getter_AddRefs(childReturn)); + NS_ENSURE_SUCCESS(rv, rv); + } + + // now that we inserted the lazy authored node, try to bind + // again + res = EvaluateXPath(expr, contextNode, aElement, aResultType, + contextPosition, contextSize, aDeps, + aIndexesUsed); + } else { + nsXFormsUtils::DispatchEvent(aElement, eEvent_BindingException); + } + } + } + } + } + } + res.swap(*aResult); // exchanges ref return NS_OK; @@ -1116,22 +1184,14 @@ nsXFormsUtils::GetInstanceNodeForData(nsIDOMNode *aInstanceDataNode, instanceDoc = do_QueryInterface(aInstanceDataNode); NS_ENSURE_TRUE(instanceDoc, NS_ERROR_UNEXPECTED); - nsCOMPtr modelElem = do_QueryInterface(aModel); - NS_ENSURE_STATE(modelElem); - nsCOMPtr nodeList; - nsresult rv = modelElem->GetElementsByTagNameNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS), - NS_LITERAL_STRING("instance"), - getter_AddRefs(nodeList)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMArray *instList = nsnull; + aModel->GetInstanceList(&instList); + NS_ENSURE_TRUE(instList, NS_ERROR_FAILURE); - PRUint32 childCount = 0; - nodeList->GetLength(&childCount); PRUint32 i; + PRUint32 childCount = instList->Count(); for (i = 0; i < childCount; ++i) { - nodeList->Item(i, aInstanceNode); - nsCOMPtr instPriv = do_QueryInterface(*aInstanceNode); - if (!instPriv) - continue; + nsCOMPtr instPriv = instList->ObjectAt(i); nsCOMPtr tmpDoc; instPriv->GetDocument(getter_AddRefs(tmpDoc));