/* -*- 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) 2004 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Brian Ryner * Allan Beaufour * Darin Fisher * Olli Pettay * * 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 "nsXFormsModelElement.h" #include "nsIXTFGenericElementWrapper.h" #include "nsMemory.h" #include "nsIDOMElement.h" #include "nsIDOM3Node.h" #include "nsIDOMNodeList.h" #include "nsString.h" #include "nsIDocument.h" #include "nsXFormsAtoms.h" #include "nsINameSpaceManager.h" #include "nsIServiceManager.h" #include "nsIDOMEvent.h" #include "nsIDOMDOMImplementation.h" #include "nsIDOMXMLDocument.h" #include "nsIDOMEventReceiver.h" #include "nsIDOMXPathResult.h" #include "nsIXFormsXPathEvaluator.h" #include "nsIDOMXPathNSResolver.h" #include "nsIDOMNSXPathExpression.h" #include "nsIContent.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsIXFormsControl.h" #include "nsXFormsTypes.h" #include "nsXFormsXPathParser.h" #include "nsXFormsXPathAnalyzer.h" #include "nsIInstanceElementPrivate.h" #include "nsXFormsUtils.h" #include "nsXFormsSchemaValidator.h" #include "nsIXFormsUIWidget.h" #include "nsIAttribute.h" #include "nsISchemaLoader.h" #include "nsISchema.h" #include "nsAutoPtr.h" #include "nsArray.h" #include "nsIDOMDocumentXBL.h" #include "nsIProgrammingLanguage.h" #include "nsDOMError.h" #define XFORMS_LAZY_INSTANCE_BINDING \ "chrome://xforms/content/xforms.xml#xforms-lazy-instance" #ifdef DEBUG //#define DEBUG_MODEL #endif //------------------------------------------------------------------------------ // Helper function for using XPath to locate an element by // matching its "id" attribute. This is necessary since is // treated as an ordinary XML data node without an "ID" attribute. static void GetSchemaElementById(nsIDOMElement *contextNode, const nsString &id, nsIDOMElement **resultNode) { // search for an element with the given "id" attribute, and then verify // that the element is in the XML Schema namespace. nsAutoString expr; expr.AssignLiteral("//*[@id=\""); expr.Append(id); expr.AppendLiteral("\"]"); nsCOMPtr xpRes = nsXFormsUtils::EvaluateXPath(expr, contextNode, contextNode, nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE); if (xpRes) { nsCOMPtr node; xpRes->GetSingleNodeValue(getter_AddRefs(node)); if (node) { nsAutoString ns; node->GetNamespaceURI(ns); if (ns.EqualsLiteral(NS_NAMESPACE_XML_SCHEMA)) CallQueryInterface(node, resultNode); } } } //------------------------------------------------------------------------------ static void DeleteVoidArray(void *aObject, nsIAtom *aPropertyName, void *aPropertyValue, void *aData) { delete NS_STATIC_CAST(nsVoidArray *, aPropertyValue); } static nsresult AddToModelList(nsIDOMDocument *domDoc, nsXFormsModelElement *model) { nsCOMPtr doc = do_QueryInterface(domDoc); nsVoidArray *models = NS_STATIC_CAST(nsVoidArray *, doc->GetProperty(nsXFormsAtoms::modelListProperty)); if (!models) { models = new nsVoidArray(16); if (!models) return NS_ERROR_OUT_OF_MEMORY; doc->SetProperty(nsXFormsAtoms::modelListProperty, models, DeleteVoidArray); } models->AppendElement(model); return NS_OK; } static void RemoveFromModelList(nsIDOMDocument *domDoc, nsXFormsModelElement *model) { nsCOMPtr doc = do_QueryInterface(domDoc); nsVoidArray *models = NS_STATIC_CAST(nsVoidArray *, doc->GetProperty(nsXFormsAtoms::modelListProperty)); if (models) models->RemoveElement(model); } static const nsVoidArray * GetModelList(nsIDOMDocument *domDoc) { nsCOMPtr doc = do_QueryInterface(domDoc); return NS_STATIC_CAST(nsVoidArray *, doc->GetProperty(nsXFormsAtoms::modelListProperty)); } static void SupportsDtorFunc(void *aObject, nsIAtom *aPropertyName, void *aPropertyValue, void *aData) { nsISupports *propertyValue = NS_STATIC_CAST(nsISupports*, aPropertyValue); NS_IF_RELEASE(propertyValue); } //------------------------------------------------------------------------------ // --- nsXFormsControlListItem --- nsXFormsControlListItem::iterator::iterator() : mCur(0) { } nsXFormsControlListItem::iterator::iterator(const nsXFormsControlListItem::iterator& aCopy) : mCur(aCopy.mCur) { mStack = aCopy.mStack; } nsXFormsControlListItem::iterator nsXFormsControlListItem::iterator::operator=(nsXFormsControlListItem* aCnt) { mCur = aCnt; return *this; } bool nsXFormsControlListItem::iterator::operator!=(const nsXFormsControlListItem* aCnt) { return mCur != aCnt; } nsXFormsControlListItem::iterator nsXFormsControlListItem::iterator::operator++() { if (!mCur) return *this; if (mCur->mFirstChild) { if (!mCur->mNextSibling) { mCur = mCur->mFirstChild; return *this; } mStack.AppendElement(mCur->mFirstChild); } if (mCur->mNextSibling) { mCur = mCur->mNextSibling; } else if (mStack.Count()) { mCur = (nsXFormsControlListItem*) mStack[mStack.Count() - 1]; mStack.RemoveElementAt(mStack.Count() - 1); } else { mCur = nsnull; } return *this; } nsXFormsControlListItem* nsXFormsControlListItem::iterator::operator*() { return mCur; } nsXFormsControlListItem::nsXFormsControlListItem(nsIXFormsControl* aControl) : mNode(aControl), mNextSibling(nsnull), mFirstChild(nsnull) { } nsXFormsControlListItem::~nsXFormsControlListItem() { Clear(); } nsXFormsControlListItem::nsXFormsControlListItem(const nsXFormsControlListItem& aCopy) : mNode(aCopy.mNode) { if (aCopy.mNextSibling) { mNextSibling = new nsXFormsControlListItem(*aCopy.mNextSibling); NS_WARN_IF_FALSE(mNextSibling, "could not new?!"); } else { mNextSibling = nsnull; } if (aCopy.mFirstChild) { mFirstChild = new nsXFormsControlListItem(*aCopy.mFirstChild); NS_WARN_IF_FALSE(mFirstChild, "could not new?!"); } else { mFirstChild = nsnull; } } void nsXFormsControlListItem::Clear() { if (mFirstChild) { mFirstChild->Clear(); NS_ASSERTION(!(mFirstChild->mFirstChild || mFirstChild->mNextSibling), "child did not clear members!!"); delete mFirstChild; mFirstChild = nsnull; } if (mNextSibling) { mNextSibling->Clear(); NS_ASSERTION(!(mNextSibling->mFirstChild || mNextSibling->mNextSibling), "sibling did not clear members!!"); delete mNextSibling; mNextSibling = nsnull; } if (mNode) mNode = nsnull; } nsresult nsXFormsControlListItem::AddControl(nsIXFormsControl *aControl, nsIXFormsControl *aParent) { // Four insertion posibilities: // 1) Delegate to first child from root node if (!mNode && mFirstChild) { return mFirstChild->AddControl(aControl, aParent); } // 2) control with no parent if (!aParent) { nsXFormsControlListItem* newNode = new nsXFormsControlListItem(aControl); NS_ENSURE_STATE(newNode); // Empty tree (we have already checked mFirstChild) if (!mNode) { mFirstChild = newNode; return NS_OK; } if (mNextSibling) { newNode->mNextSibling = mNextSibling; } mNextSibling = newNode; #ifdef DEBUG nsXFormsControlListItem* next = newNode->mNextSibling; while (next) { NS_ASSERTION(aControl != next->mNode, "Node already in tree!!"); next = next->mNextSibling; } #endif return NS_OK; } // Locate parent nsXFormsControlListItem* parentControl = FindControl(aParent); NS_ASSERTION(parentControl, "Parent not found?!"); // 3) parentControl has a first child, insert as sibling to that if (parentControl->mFirstChild) { return parentControl->mFirstChild->AddControl(aControl, nsnull); } // 4) first child for parentControl nsXFormsControlListItem* newNode = new nsXFormsControlListItem(aControl); NS_ENSURE_STATE(newNode); parentControl->mFirstChild = newNode; return NS_OK; } nsresult nsXFormsControlListItem::RemoveControl(nsIXFormsControl *aControl, PRBool &aRemoved) { nsXFormsControlListItem* deleteMe = nsnull; aRemoved = PR_FALSE; // Try children if (mFirstChild) { // The control to remove is our first child if (mFirstChild->mNode == aControl) { deleteMe = mFirstChild; // Fix siblings if (deleteMe->mNextSibling) { mFirstChild = deleteMe->mNextSibling; deleteMe->mNextSibling = nsnull; } else { mFirstChild = nsnull; } // Fix children if (deleteMe->mFirstChild) { if (!mFirstChild) { mFirstChild = deleteMe->mFirstChild; } else { nsXFormsControlListItem *insertPos = mFirstChild; while (insertPos->mNextSibling) { insertPos = insertPos->mNextSibling; } insertPos->mNextSibling = deleteMe->mFirstChild; } deleteMe->mFirstChild = nsnull; } } else { // Run through children nsresult rv = mFirstChild->RemoveControl(aControl, aRemoved); NS_ENSURE_SUCCESS(rv, rv); if (aRemoved) return rv; } } // Try siblings if (!deleteMe && mNextSibling) { if (mNextSibling->mNode == aControl) { deleteMe = mNextSibling; // Fix siblings if (deleteMe->mNextSibling) { mNextSibling = deleteMe->mNextSibling; deleteMe->mNextSibling = nsnull; } else { mNextSibling = nsnull; } // Fix children if (deleteMe->mFirstChild) { if (!mNextSibling) { mNextSibling = deleteMe->mFirstChild; } else { nsXFormsControlListItem *insertPos = mNextSibling; while (insertPos->mNextSibling) { insertPos = insertPos->mNextSibling; } insertPos->mNextSibling = deleteMe->mFirstChild; } deleteMe->mFirstChild = nsnull; } } else { // run through siblings return mNextSibling->RemoveControl(aControl, aRemoved); } } if (deleteMe) { NS_ASSERTION(!(deleteMe->mNextSibling), "Deleted control should not have siblings!"); NS_ASSERTION(!(deleteMe->mFirstChild), "Deleted control should not have children!"); delete deleteMe; aRemoved = PR_TRUE; } return NS_OK; } nsXFormsControlListItem* nsXFormsControlListItem::FindControl(nsIXFormsControl *aControl) { if (!aControl) return nsnull; // this should only be false for the root if (mNode) { // XXX: *sigh* pointer comparision of nsIXFormsControl would be nice... nsCOMPtr el1, el2; aControl->GetElement(getter_AddRefs(el1)); mNode->GetElement(getter_AddRefs(el2)); if (el1 == el2) return this; } nsXFormsControlListItem* cur = nsnull; if (mFirstChild) { cur = mFirstChild->FindControl(aControl); } if (!cur && mNextSibling) { cur = mNextSibling->FindControl(aControl); } return cur; } already_AddRefed nsXFormsControlListItem::Control() { nsIXFormsControl* res = nsnull; if (mNode) NS_ADDREF(res = mNode); NS_WARN_IF_FALSE(res, "Returning nsnull for a control. Bad sign."); return res; } nsXFormsControlListItem* nsXFormsControlListItem::begin() { // handle root if (!mNode) return mFirstChild; return this; } nsXFormsControlListItem* nsXFormsControlListItem::end() { return nsnull; } //------------------------------------------------------------------------------ static const nsIID sScriptingIIDs[] = { NS_IDOMELEMENT_IID, NS_IDOMEVENTTARGET_IID, NS_IDOM3NODE_IID, NS_IXFORMSMODELELEMENT_IID, NS_IXFORMSNSMODELELEMENT_IID }; static nsIAtom* sModelPropsList[eModel__count]; // This can be nsVoidArray because elements will remove // themselves from the list if they are deleted during refresh. static nsVoidArray* sPostRefreshList = nsnull; static PRInt32 sRefreshing = 0; nsPostRefresh::nsPostRefresh() { #ifdef DEBUG_smaug printf("nsPostRefresh\n"); #endif ++sRefreshing; } nsPostRefresh::~nsPostRefresh() { #ifdef DEBUG_smaug printf("~nsPostRefresh\n"); #endif --sRefreshing; if (sPostRefreshList && !sRefreshing) { while (sPostRefreshList->Count()) { // Iterating this way because refresh can lead to // additions/deletions in sPostRefreshList. // Iterating from last to first saves possibly few memcopies, // see nsVoidArray::RemoveElementsAt(). PRInt32 last = sPostRefreshList->Count() - 1; nsIXFormsControl* control = NS_STATIC_CAST(nsIXFormsControl*, sPostRefreshList->ElementAt(last)); sPostRefreshList->RemoveElementAt(last); if (control) control->Refresh(); } delete sPostRefreshList; sPostRefreshList = nsnull; } } const nsVoidArray* nsPostRefresh::PostRefreshList() { return sPostRefreshList; } nsresult nsXFormsModelElement::NeedsPostRefresh(nsIXFormsControl* aControl) { if (sRefreshing) { if (!sPostRefreshList) { sPostRefreshList = new nsVoidArray(); NS_ENSURE_TRUE(sPostRefreshList, NS_ERROR_OUT_OF_MEMORY); } if (sPostRefreshList->IndexOf(aControl) < 0) { sPostRefreshList->AppendElement(aControl); } } else { // We are not refreshing any models, so the control // can be refreshed immediately. aControl->Refresh(); } return NS_OK; } void nsXFormsModelElement::CancelPostRefresh(nsIXFormsControl* aControl) { if (sPostRefreshList) sPostRefreshList->RemoveElement(aControl); } nsXFormsModelElement::nsXFormsModelElement() : mElement(nsnull), mFormControls(nsnull), mSchemaCount(0), mSchemaTotal(0), mPendingInstanceCount(0), mDocumentLoaded(PR_FALSE), mNeedsRefresh(PR_FALSE), mInstancesInitialized(PR_FALSE), mReadyHandled(PR_FALSE), mInstanceDocuments(nsnull), mLazyModel(PR_FALSE), mConstructDoneHandled(PR_FALSE) { } NS_INTERFACE_MAP_BEGIN(nsXFormsModelElement) NS_INTERFACE_MAP_ENTRY(nsIXFormsModelElement) NS_INTERFACE_MAP_ENTRY(nsIXFormsNSModelElement) NS_INTERFACE_MAP_ENTRY(nsIModelElementPrivate) NS_INTERFACE_MAP_ENTRY(nsISchemaLoadListener) NS_INTERFACE_MAP_ENTRY(nsIWebServiceErrorHandler) NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) NS_INTERFACE_MAP_ENTRY(nsIXFormsContextControl) NS_INTERFACE_MAP_END_INHERITING(nsXFormsStubElement) NS_IMPL_ADDREF_INHERITED(nsXFormsModelElement, nsXFormsStubElement) NS_IMPL_RELEASE_INHERITED(nsXFormsModelElement, nsXFormsStubElement) NS_IMETHODIMP nsXFormsModelElement::OnDestroyed() { RemoveModelFromDocument(); mElement = nsnull; mSchemas = nsnull; if (mInstanceDocuments) mInstanceDocuments->DropReferences(); mFormControls.Clear(); return NS_OK; } void nsXFormsModelElement::RemoveModelFromDocument() { mDocumentLoaded = PR_FALSE; nsCOMPtr domDoc; mElement->GetOwnerDocument(getter_AddRefs(domDoc)); if (!domDoc) return; RemoveFromModelList(domDoc, this); nsCOMPtr targ = do_QueryInterface(domDoc); if (targ) { targ->RemoveEventListener(NS_LITERAL_STRING("DOMContentLoaded"), this, PR_TRUE); nsCOMPtr window; nsXFormsUtils::GetWindowFromDocument(domDoc, getter_AddRefs(window)); targ = do_QueryInterface(window); if (targ) { targ->RemoveEventListener(NS_LITERAL_STRING("unload"), this, PR_TRUE); } } } NS_IMETHODIMP nsXFormsModelElement::GetScriptingInterfaces(PRUint32 *aCount, nsIID ***aArray) { return nsXFormsUtils::CloneScriptingInterfaces(sScriptingIIDs, NS_ARRAY_LENGTH(sScriptingIIDs), aCount, aArray); } NS_IMETHODIMP nsXFormsModelElement::WillChangeDocument(nsIDOMDocument* aNewDocument) { RemoveModelFromDocument(); return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::DocumentChanged(nsIDOMDocument* aNewDocument) { if (!aNewDocument) return NS_OK; AddToModelList(aNewDocument, this); nsCOMPtr targ = do_QueryInterface(aNewDocument); if (targ) { targ->AddEventListener(NS_LITERAL_STRING("DOMContentLoaded"), this, PR_TRUE); nsCOMPtr window; nsXFormsUtils::GetWindowFromDocument(aNewDocument, getter_AddRefs(window)); targ = do_QueryInterface(window); if (targ) { targ->AddEventListener(NS_LITERAL_STRING("unload"), this, PR_TRUE); } } return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::DoneAddingChildren() { return InitializeInstances(); } nsresult nsXFormsModelElement::InitializeInstances() { if (mInstancesInitialized || !mElement) { return NS_OK; } mInstancesInitialized = PR_TRUE; nsCOMPtr children; mElement->GetChildNodes(getter_AddRefs(children)); PRUint32 childCount = 0; if (children) { children->GetLength(&childCount); } for (PRUint32 i = 0; i < childCount; ++i) { nsCOMPtr child; children->Item(i, getter_AddRefs(child)); if (nsXFormsUtils::IsXFormsElement(child, NS_LITERAL_STRING("instance"))) { nsCOMPtr instance(do_QueryInterface(child)); if (instance) { instance->Initialize(); } } } // (XForms 4.2.1) // 1. load xml schemas nsAutoString schemaList; mElement->GetAttribute(NS_LITERAL_STRING("schema"), schemaList); if (!schemaList.IsEmpty()) { NS_ENSURE_TRUE(mSchemas, NS_ERROR_FAILURE); // Parse the whitespace-separated list. nsCOMPtr content = do_QueryInterface(mElement); nsRefPtr baseURI = content->GetBaseURI(); nsRefPtr docURI = content->GetOwnerDoc() ? content->GetOwnerDoc()->GetDocumentURI() : nsnull; nsCStringArray schemas; schemas.ParseString(NS_ConvertUTF16toUTF8(schemaList).get(), " \t\r\n"); // Increase by 1 to prevent OnLoad from calling FinishConstruction mSchemaTotal = schemas.Count(); for (PRInt32 i=0; i newURI; NS_NewURI(getter_AddRefs(newURI), *schemas[i], nsnull, baseURI); nsCOMPtr newURL = do_QueryInterface(newURI); if (!newURL) { rv = NS_ERROR_UNEXPECTED; } else { // This code is copied from nsXMLEventsManager for extracting an // element ID from an xsd:anyURI link. nsCAutoString ref; newURL->GetRef(ref); newURL->SetRef(EmptyCString()); PRBool equals = PR_FALSE; newURL->Equals(docURI, &equals); if (equals) { // We will not be able to locate the element using the // getElementById function defined on our document when // is treated as an ordinary XML data node. So, we employ XPath to // locate it for us. NS_ConvertUTF8toUTF16 id(ref); nsCOMPtr el; GetSchemaElementById(mElement, id, getter_AddRefs(el)); if (!el) { // Perhaps the element appears after the // element in the document, so we'll defer loading it until the // document has finished loading. mPendingInlineSchemas.AppendString(id); } else { nsCOMPtr schema; // no need to observe errors via the callback. instead, rely on // this method returning a failure code when it encounters errors. rv = mSchemas->ProcessSchemaElement(el, nsnull, getter_AddRefs(schema)); if (NS_SUCCEEDED(rv)) mSchemaCount++; } } else { nsCAutoString uriSpec; newURI->GetSpec(uriSpec); rv = mSchemas->LoadAsync(NS_ConvertUTF8toUTF16(uriSpec), this); } } if (NS_FAILED(rv)) { // this is a fatal error nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement); nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException); rv = NS_OK; if (!nsXFormsUtils::HandleFatalError(mElement, NS_LITERAL_STRING("XFormsLinkException"))) { rv = NS_ERROR_FAILURE; } return rv; } } } // 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. // Lazy element is created in anonymous content using XBL. NS_ENSURE_STATE(mInstanceDocuments); PRUint32 instCount; mInstanceDocuments->GetLength(&instCount); if (!instCount) { #ifdef DEBUG printf("Creating lazy instance\n"); #endif nsCOMPtr domDoc; mElement->GetOwnerDocument(getter_AddRefs(domDoc)); nsCOMPtr xblDoc(do_QueryInterface(domDoc)); if (xblDoc) { nsresult rv = xblDoc->AddBinding(mElement, NS_LITERAL_STRING(XFORMS_LAZY_INSTANCE_BINDING)); NS_ENSURE_SUCCESS(rv, rv); mInstanceDocuments->GetLength(&instCount); nsCOMPtr list; xblDoc->GetAnonymousNodes(mElement, getter_AddRefs(list)); if (list) { PRUint32 childCount = 0; if (list) { list->GetLength(&childCount); } for (PRUint32 i = 0; i < childCount; ++i) { nsCOMPtr item; list->Item(i, getter_AddRefs(item)); nsCOMPtr instance = do_QueryInterface(item); if (instance) { rv = instance->Initialize(); NS_ENSURE_SUCCESS(rv, rv); mLazyModel = PR_TRUE; break; } } } } NS_WARN_IF_FALSE(mLazyModel, "Installing lazy instance didn't succeed!"); } // (XForms 4.2.1 - cont) // 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 // point. // schema and external instance data loads should delay document onload if (IsComplete()) { // No need to fire refresh event if we assume that all UI controls // appear later in the document. NS_ASSERTION(!mDocumentLoaded, "document should not be loaded yet"); return FinishConstruction(); } return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::HandleDefault(nsIDOMEvent *aEvent, PRBool *aHandled) { if (!nsXFormsUtils::EventHandlingAllowed(aEvent, mElement)) return NS_OK; *aHandled = PR_TRUE; nsAutoString type; aEvent->GetType(type); nsresult rv = NS_OK; if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Refresh].name)) { rv = Refresh(); } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Revalidate].name)) { rv = Revalidate(); } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Recalculate].name)) { rv = Recalculate(); } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Rebuild].name)) { rv = Rebuild(); } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_ModelConstructDone].name)) { rv = ConstructDone(); mConstructDoneHandled = PR_TRUE; } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Reset].name)) { Reset(); } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_BindingException].name)) { *aHandled = nsXFormsUtils::HandleFatalError(mElement, NS_LITERAL_STRING("XFormsBindingException")); } else { *aHandled = PR_FALSE; } #ifdef DEBUG if (NS_FAILED(rv)) printf("nsXFormsModelElement::HandleDefault() failed!\n"); #endif return rv; } nsresult nsXFormsModelElement::ConstructDone() { nsresult rv = InitializeControls(); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper) { aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_WILL_CHANGE_DOCUMENT | nsIXTFElement::NOTIFY_DOCUMENT_CHANGED | nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN | nsIXTFElement::NOTIFY_HANDLE_DEFAULT); nsCOMPtr node; aWrapper->GetElementNode(getter_AddRefs(node)); // It's ok to keep a weak pointer to mElement. mElement will have an // owning reference to this object, so as long as we null out mElement in // OnDestroyed, it will always be valid. mElement = node; NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon"); nsresult rv = mMDG.Init(this); NS_ENSURE_SUCCESS(rv, rv); mSchemas = do_CreateInstance(NS_SCHEMALOADER_CONTRACTID); mInstanceDocuments = new nsXFormsModelInstanceDocuments(); NS_ASSERTION(mInstanceDocuments, "could not create mInstanceDocuments?!"); // Initialize hash tables NS_ENSURE_TRUE(mNodeToType.Init(), NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(mNodeToP3PType.Init(), NS_ERROR_OUT_OF_MEMORY); return NS_OK; } // nsIXFormsModelElement NS_IMETHODIMP nsXFormsModelElement::GetInstanceDocuments(nsIDOMNodeList **aDocuments) { NS_ENSURE_STATE(mInstanceDocuments); NS_ENSURE_ARG_POINTER(aDocuments); NS_ADDREF(*aDocuments = mInstanceDocuments); return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::GetInstanceDocument(const nsAString& aInstanceID, nsIDOMDocument **aDocument) { NS_ENSURE_ARG_POINTER(aDocument); *aDocument = FindInstanceDocument(aInstanceID).get(); // transfer reference if (*aDocument) { return NS_OK; } const nsPromiseFlatString& flat = PromiseFlatString(aInstanceID); const PRUnichar *strings[] = { flat.get() }; nsXFormsUtils::ReportError(aInstanceID.IsEmpty() ? NS_LITERAL_STRING("defInstanceNotFound") : NS_LITERAL_STRING("instanceNotFound"), strings, 1, mElement, nsnull); return NS_ERROR_DOM_NOT_FOUND_ERR; } NS_IMETHODIMP nsXFormsModelElement::Rebuild() { #ifdef DEBUG printf("nsXFormsModelElement::Rebuild()\n"); #endif // 1 . Clear graph nsresult rv; rv = mMDG.Clear(); NS_ENSURE_SUCCESS(rv, rv); // Clear any type information NS_ENSURE_TRUE(mNodeToType.IsInitialized() && mNodeToP3PType.IsInitialized(), NS_ERROR_FAILURE); mNodeToType.Clear(); mNodeToP3PType.Clear(); // 2. Process bind elements rv = ProcessBindElements(); NS_ENSURE_SUCCESS(rv, rv); // 3. Re-attach all elements if (mDocumentLoaded) { // if it's not during initializing phase nsXFormsControlListItem::iterator it; for (it = mFormControls.begin(); it != mFormControls.end(); ++it) { nsCOMPtr control = (*it)->Control(); NS_ASSERTION(control, "mFormControls has null control?!"); // run bind to reset mBoundNode for all of the model's controls control->Bind(); } // Triggers a refresh of all controls mNeedsRefresh = PR_TRUE; } // 4. Rebuild graph return mMDG.Rebuild(); } NS_IMETHODIMP nsXFormsModelElement::Recalculate() { #ifdef DEBUG printf("nsXFormsModelElement::Recalculate()\n"); #endif return mMDG.Recalculate(&mChangedNodes); } void nsXFormsModelElement::SetSingleState(nsIDOMElement *aElement, PRBool aState, nsXFormsEvent aOnEvent) { nsXFormsEvent event = aState ? aOnEvent : (nsXFormsEvent) (aOnEvent + 1); // Dispatch event nsXFormsUtils::DispatchEvent(aElement, event); } nsresult nsXFormsModelElement::SetStatesInternal(nsIXFormsControl *aControl, nsIDOMNode *aNode, PRBool aDispatchEvents) { NS_ENSURE_ARG(aControl); if (!aNode) return NS_OK; nsCOMPtr element; aControl->GetElement(getter_AddRefs(element)); NS_ENSURE_STATE(element); nsCOMPtr xtfWrap(do_QueryInterface(element)); NS_ENSURE_STATE(xtfWrap); const nsXFormsNodeState *ns = mMDG.GetNodeState(aNode); NS_ENSURE_STATE(ns); nsresult rv = xtfWrap->SetIntrinsicState(ns->GetIntrinsicState()); NS_ENSURE_SUCCESS(rv, rv); if (!aDispatchEvents) return NS_OK; if (ns->ShouldDispatchValid()) { SetSingleState(element, ns->IsValid(), eEvent_Valid); } if (ns->ShouldDispatchReadonly()) { SetSingleState(element, ns->IsReadonly(), eEvent_Readonly); } if (ns->ShouldDispatchRequired()) { SetSingleState(element, ns->IsRequired(), eEvent_Required); } if (ns->ShouldDispatchRelevant()) { SetSingleState(element, ns->IsRelevant(), eEvent_Enabled); } if (ns->ShouldDispatchValueChanged()) { nsXFormsUtils::DispatchEvent(element, eEvent_ValueChanged); } return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::Revalidate() { #ifdef DEBUG printf("nsXFormsModelElement::Revalidate()\n"); #endif #ifdef DEBUG_MODEL printf("[%s] Changed nodes:\n", __TIME__); for (PRInt32 j = 0; j < mChangedNodes.Count(); ++j) { nsCOMPtr node = mChangedNodes[j]; nsAutoString name; node->GetNodeName(name); printf("\t%s [%p]\n", NS_ConvertUTF16toUTF8(name).get(), (void*) node); } #endif // Revalidate nodes mMDG.Revalidate(&mChangedNodes); return NS_OK; } nsresult nsXFormsModelElement::RefreshSubTree(nsXFormsControlListItem *aCurrent, PRBool aForceRebind) { nsresult rv; while (aCurrent) { nsCOMPtr control(aCurrent->Control()); NS_ASSERTION(control, "A tree node without a control?!"); // Get bound node nsCOMPtr boundNode; control->GetBoundNode(getter_AddRefs(boundNode)); PRBool rebind = aForceRebind; PRBool refresh = PR_FALSE; PRBool rebindChildren = PR_FALSE; #ifdef DEBUG_MODEL nsCOMPtr controlElement; control->GetElement(getter_AddRefs(controlElement)); printf("rebind: %d, mNeedsRefresh: %d, rebindChildren: %d\n", rebind, mNeedsRefresh, rebindChildren); if (controlElement) { printf("Checking control: "); //DBG_TAGINFO(controlElement); } #endif if (mNeedsRefresh || rebind) { refresh = PR_TRUE; } else { PRBool usesModelBinding = PR_FALSE; control->GetUsesModelBinding(&usesModelBinding); #ifdef DEBUG_MODEL printf("usesModelBinding: %d\n", usesModelBinding); #endif nsCOMArray *deps = nsnull; if (usesModelBinding) { if (!boundNode) { // If a control uses a model binding, but has no bound node a // rebuild is the only thing that'll (eventually) change it aCurrent = aCurrent->NextSibling(); continue; } } else { // Get dependencies control->GetDependencies(&deps); } PRUint32 depCount = deps ? deps->Count() : 0; #ifdef DEBUG_MODEL nsAutoString boundName; if (boundNode) boundNode->GetNodeName(boundName); printf("\tDependencies: %d, Bound to: '%s' [%p]\n", depCount, NS_ConvertUTF16toUTF8(boundName).get(), (void*) boundNode); nsAutoString depNodeName; for (PRUint32 t = 0; t < depCount; ++t) { nsCOMPtr tmpdep = deps->ObjectAt(t); if (tmpdep) { tmpdep->GetNodeName(depNodeName); printf("\t\t%s [%p]\n", NS_ConvertUTF16toUTF8(depNodeName).get(), (void*) tmpdep); } } #endif nsCOMPtr curChanged; // Iterator over changed nodes. Checking for rebind, too. If it ever // becomes true due to some condition below, we can stop this testing // since any control that needs to rebind will also refresh. for (PRInt32 j = 0; j < mChangedNodes.Count() && !rebind; ++j) { curChanged = do_QueryInterface(mChangedNodes[j]); // Check whether the bound node is dirty. If so, we need to refresh the // control (get updated node value from the bound node) if (!refresh && boundNode) { curChanged->IsSameNode(boundNode, &refresh); // Two ways to go here. Keep in mind that controls using model // binding expressions never needs to have dependencies checked as // they only rebind on xforms-rebuild if (refresh && usesModelBinding) { // 1) If the control needs a refresh, and uses model bindings, // we can stop checking here break; } if (refresh || usesModelBinding) { // 2) If either the control needs a refresh or it uses a model // binding we can continue to next changed node continue; } } // Check whether any dependencies are dirty. If so, we need to rebind // the control (re-evaluate it's binding expression) for (PRUint32 k = 0; k < depCount; ++k) { /// @note beaufour: I'm not too happy about this ... /// O(mChangedNodes.Count() * deps->Count()), but using the pointers /// for sorting and comparing does not work... curChanged->IsSameNode(deps->ObjectAt(k), &rebind); if (rebind) // We need to rebind the control, no need to check any more break; } } #ifdef DEBUG_MODEL printf("\trebind: %d, refresh: %d\n", rebind, refresh); #endif } // Handle rebinding if (rebind) { nsCOMPtr oldBoundNode; control->GetBoundNode(getter_AddRefs(oldBoundNode)); rv = control->Bind(); NS_ENSURE_SUCCESS(rv, rv); control->GetBoundNode(getter_AddRefs(boundNode)); rebindChildren = (oldBoundNode != boundNode); } // Handle refreshing if (rebind || refresh) { rv = SetStatesInternal(control, boundNode); NS_ENSURE_SUCCESS(rv, rv); control->Refresh(); // XXX: we should really check the return result, but f.x. select1 // returns error because of no widget...? so we should ensure that an // error is only returned when there actually is an error, and we should // report that on the console... possibly we should then continue, // instead of bailing totally. // NS_ENSURE_SUCCESS(rv, rv); } // Refresh children rv = RefreshSubTree(aCurrent->FirstChild(), rebindChildren); NS_ENSURE_SUCCESS(rv, rv); aCurrent = aCurrent->NextSibling(); } return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::Refresh() { #ifdef DEBUG printf("nsXFormsModelElement::Refresh()\n"); #endif nsPostRefresh postRefresh = nsPostRefresh(); if (!mDocumentLoaded) { return NS_OK; } // Kick off refreshing on root node nsresult rv = RefreshSubTree(mFormControls.FirstChild(), PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); // Clear refresh structures mChangedNodes.Clear(); mNeedsRefresh = PR_FALSE; mMDG.ClearDispatchFlags(); return NS_OK; } // nsISchemaLoadListener NS_IMETHODIMP nsXFormsModelElement::OnLoad(nsISchema* aSchema) { mSchemaCount++; if (IsComplete()) { nsresult rv = FinishConstruction(); NS_ENSURE_SUCCESS(rv, rv); MaybeNotifyCompletion(); } return NS_OK; } // nsIWebServiceErrorHandler NS_IMETHODIMP nsXFormsModelElement::OnError(nsresult aStatus, const nsAString &aStatusMessage) { nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement); nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException); return NS_OK; } // nsIDOMEventListener NS_IMETHODIMP nsXFormsModelElement::HandleEvent(nsIDOMEvent* aEvent) { if (!nsXFormsUtils::EventHandlingAllowed(aEvent, mElement)) return NS_OK; nsAutoString type; aEvent->GetType(type); if (type.EqualsLiteral("DOMContentLoaded")) { return HandleLoad(aEvent); }else if (type.EqualsLiteral("unload")) { return HandleUnload(aEvent); } return NS_OK; } // nsIModelElementPrivate NS_IMETHODIMP nsXFormsModelElement::AddFormControl(nsIXFormsControl *aControl, nsIXFormsControl *aParent) { NS_ENSURE_ARG(aControl); return mFormControls.AddControl(aControl, aParent); } NS_IMETHODIMP nsXFormsModelElement::RemoveFormControl(nsIXFormsControl *aControl) { NS_ENSURE_ARG(aControl); PRBool removed; nsresult rv = mFormControls.RemoveControl(aControl, removed); NS_WARN_IF_FALSE(removed, "Tried to remove control that was not in the model"); return rv; } NS_IMETHODIMP nsXFormsModelElement::GetTypeForControl(nsIXFormsControl *aControl, nsISchemaType **aType) { NS_ENSURE_ARG_POINTER(aType); *aType = nsnull; nsCOMPtr boundNode; aControl->GetBoundNode(getter_AddRefs(boundNode)); if (!boundNode) { // if the control isn't bound to instance data, it doesn't make sense to // return a type. It is perfectly valid for there to be no bound node, // so no need to use an NS_ENSURE_xxx macro, either. return NS_ERROR_FAILURE; } nsAutoString schemaTypeName, schemaTypeNamespace; nsresult rv = GetTypeAndNSFromNode(boundNode, schemaTypeName, schemaTypeNamespace); NS_ENSURE_SUCCESS(rv, rv); nsXFormsSchemaValidator validator; nsCOMPtr schemaColl = do_QueryInterface(mSchemas); if (schemaColl) { nsCOMPtr schema; schemaColl->GetSchema(schemaTypeNamespace, getter_AddRefs(schema)); // if no schema found, then we will only handle built-in types. if (schema) validator.LoadSchema(schema); } return validator.GetType(schemaTypeName, schemaTypeNamespace, aType); } /* static */ nsresult nsXFormsModelElement::GetTypeAndNSFromNode(nsIDOMNode *aInstanceData, nsAString &aType, nsAString &aNSUri) { nsresult rv = GetTypeFromNode(aInstanceData, aType, aNSUri); if (rv == NS_ERROR_NOT_AVAILABLE) { // if there is no type assigned, then assume that the type is 'string' aNSUri.Assign(NS_LITERAL_STRING(NS_NAMESPACE_XML_SCHEMA)); aType.Assign(NS_LITERAL_STRING("string")); rv = NS_OK; } return rv; } NS_IMETHODIMP nsXFormsModelElement::InstanceLoadStarted() { ++mPendingInstanceCount; return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::InstanceLoadFinished(PRBool aSuccess) { --mPendingInstanceCount; if (!aSuccess) { nsXFormsUtils::ReportError(NS_LITERAL_STRING("instanceLoadError"), mElement); nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException); } else if (IsComplete()) { nsresult rv = FinishConstruction(); if (NS_SUCCEEDED(rv)) { MaybeNotifyCompletion(); } } return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::FindInstanceElement(const nsAString &aID, nsIInstanceElementPrivate **aElement) { NS_ENSURE_STATE(mInstanceDocuments); *aElement = nsnull; PRUint32 instCount; mInstanceDocuments->GetLength(&instCount); if (instCount) { nsCOMPtr element; nsAutoString id; for (PRUint32 i = 0; i < instCount; ++i) { nsIInstanceElementPrivate* instEle = mInstanceDocuments->GetInstanceAt(i); instEle->GetElement(getter_AddRefs(element)); 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; } } } return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::SetNodeValue(nsIDOMNode *aContextNode, const nsAString &aNodeValue, PRBool *aNodeChanged) { return mMDG.SetNodeValue(aContextNode, aNodeValue, aNodeChanged); } NS_IMETHODIMP nsXFormsModelElement::GetNodeValue(nsIDOMNode *aContextNode, nsAString &aNodeValue) { return mMDG.GetNodeValue(aContextNode, aNodeValue); } NS_IMETHODIMP nsXFormsModelElement::SetNodeContent(nsIDOMNode *aContextNode, nsIDOMNode *aNodeContent, PRBool *aNodeChanged) { return mMDG.SetNodeContent(aContextNode, aNodeContent, aNodeChanged); } NS_IMETHODIMP nsXFormsModelElement::ValidateNode(nsIDOMNode *aInstanceNode, PRBool *aResult) { NS_ENSURE_ARG_POINTER(aResult); nsAutoString schemaTypeName, schemaTypeNamespace; nsresult rv = GetTypeAndNSFromNode(aInstanceNode, schemaTypeName, schemaTypeNamespace); NS_ENSURE_SUCCESS(rv, rv); nsXFormsSchemaValidator validator; nsCOMPtr schemaColl = do_QueryInterface(mSchemas); if (schemaColl) { nsCOMPtr schema; schemaColl->GetSchema(schemaTypeNamespace, getter_AddRefs(schema)); // if no schema found, then we will only handle built-in types. if (schema) validator.LoadSchema(schema); } nsAutoString value; nsXFormsUtils::GetNodeValue(aInstanceNode, value); PRBool isValid = validator.ValidateString(value, schemaTypeName, schemaTypeNamespace); *aResult = isValid; return NS_OK; } /* * SUBMIT_SERIALIZE_NODE - node is to be serialized * SUBMIT_SKIP_NODE - node is not to be serialized * SUBMIT_ABORT_SUBMISSION - abort submission (invalid node or empty required node) */ NS_IMETHODIMP nsXFormsModelElement::HandleInstanceDataNode(nsIDOMNode *aInstanceDataNode, unsigned short *aResult) { // abort by default *aResult = SUBMIT_ABORT_SUBMISSION; const nsXFormsNodeState* ns; ns = mMDG.GetNodeState(aInstanceDataNode); NS_ENSURE_STATE(ns); if (!ns->IsRelevant()) { // not relevant, thus skip *aResult = SUBMIT_SKIP_NODE; } else if (ns->IsRequired()) { // required and has a value, continue nsAutoString value; nsXFormsUtils::GetNodeValue(aInstanceDataNode, value); if (!value.IsEmpty() && ns->IsValid()) *aResult = SUBMIT_SERIALIZE_NODE; } else if (ns->IsValid()) { // valid *aResult = SUBMIT_SERIALIZE_NODE; } return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::GetLazyAuthored(PRBool *aLazyInstance) { *aLazyInstance = mLazyModel; return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::GetIsReady(PRBool *aIsReady) { *aIsReady = mReadyHandled; return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::GetTypeFromNode(nsIDOMNode *aInstanceData, nsAString &aType, nsAString &aNSUri) { // aInstanceData could be an instance data node or it could be an attribute // on an instance data node (basically the node that a control is bound to). nsString *typeVal = nsnull; // Get type stored directly on instance node nsAutoString typeAttribute; nsCOMPtr nodeElem(do_QueryInterface(aInstanceData)); if (nodeElem) { nodeElem->GetAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XML_SCHEMA_INSTANCE), NS_LITERAL_STRING("type"), typeAttribute); if (!typeAttribute.IsEmpty()) { typeVal = &typeAttribute; } } // If there was no type information on the node itself, check for a type // bound to the node via \ if (!typeVal && !mNodeToType.Get(aInstanceData, &typeVal)) { // No type information found return NS_ERROR_NOT_AVAILABLE; } // split type (ns:type) into namespace and type. nsAutoString prefix; PRInt32 separator = typeVal->FindChar(':'); if ((PRUint32) separator == (typeVal->Length() - 1)) { const PRUnichar *strings[] = { typeVal->get() }; nsXFormsUtils::ReportError(NS_LITERAL_STRING("missingTypeName"), strings, 1, mElement, nsnull); return NS_ERROR_UNEXPECTED; } if (separator == kNotFound) { // no namespace prefix, which is valid; prefix = EmptyString(); aType.Assign(*typeVal); } else { prefix.Assign(Substring(*typeVal, 0, separator)); aType.Assign(Substring(*typeVal, ++separator, typeVal->Length())); } if (prefix.IsEmpty()) { aNSUri = EmptyString(); return NS_OK; } // get the namespace url from the prefix using instance data node nsresult rv; nsCOMPtr domNode3 = do_QueryInterface(aInstanceData, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = domNode3->LookupNamespaceURI(prefix, aNSUri); if (DOMStringIsNull(aNSUri)) { // if not found using instance data node, use node nsCOMPtr instanceNode; rv = nsXFormsUtils::GetInstanceNodeForData(aInstanceData, getter_AddRefs(instanceNode)); NS_ENSURE_SUCCESS(rv, rv); domNode3 = do_QueryInterface(instanceNode, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = domNode3->LookupNamespaceURI(prefix, aNSUri); } return rv; } // nsIXFormsContextControl NS_IMETHODIMP nsXFormsModelElement::SetContext(nsIDOMNode *aContextNode, PRInt32 aContextPosition, PRInt32 aContextSize) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsXFormsModelElement::GetContext(nsAString &aModelID, nsIDOMNode **aContextNode, PRInt32 *aContextPosition, PRInt32 *aContextSize) { // Adding the nsIXFormsContextControl interface to model to allow // submission elements to call our binding evaluation methods, like // EvaluateNodeBinding. If GetContext can get called outside of the binding // codepath, then this MIGHT lead to problems. NS_ENSURE_ARG(aContextSize); NS_ENSURE_ARG(aContextPosition); *aContextNode = nsnull; // better get the stuff most likely to fail out of the way first. No sense // changing the other values that we are returning unless this is successful. nsresult rv = NS_ERROR_FAILURE; // Anybody (like a submission element) asking a model element for its context // for XPath expressions will want the root node of the default instance // document nsCOMPtr firstInstanceDoc = FindInstanceDocument(EmptyString()); NS_ENSURE_TRUE(firstInstanceDoc, rv); nsCOMPtr firstInstanceRoot; rv = firstInstanceDoc->GetDocumentElement(getter_AddRefs(firstInstanceRoot)); NS_ENSURE_TRUE(firstInstanceRoot, rv); nsCOMPtrrootNode = do_QueryInterface(firstInstanceRoot); rootNode.swap(*aContextNode); // found the context, so can finish up assinging the rest of the values that // we are returning *aContextPosition = 1; *aContextSize = 1; nsAutoString id; mElement->GetAttribute(NS_LITERAL_STRING("id"), id); aModelID.Assign(id); return NS_OK; } // internal methods already_AddRefed nsXFormsModelElement::FindInstanceDocument(const nsAString &aID) { nsCOMPtr instance; nsXFormsModelElement::FindInstanceElement(aID, getter_AddRefs(instance)); nsIDOMDocument *doc = nsnull; if (instance) { instance->GetInstanceDocument(&doc); // addrefs } return doc; } nsresult nsXFormsModelElement::ProcessBindElements() { // ProcessBindElements() will go through each xforms:bind element in // document order and apply all of the Model Item Properties to the // instance items in the nodeset. This information will also be entered // in the Master Dependency Graph. Most of this work is done in the // ProcessBind() method. nsCOMPtr firstInstanceDoc = FindInstanceDocument(EmptyString()); if (!firstInstanceDoc) return NS_OK; nsCOMPtr firstInstanceRoot; firstInstanceDoc->GetDocumentElement(getter_AddRefs(firstInstanceRoot)); nsresult rv; nsCOMPtr xpath = do_CreateInstance("@mozilla.org/dom/xforms-xpath-evaluator;1", &rv); NS_ENSURE_TRUE(xpath, rv); nsCOMPtr children; mElement->GetChildNodes(getter_AddRefs(children)); PRUint32 childCount = 0; if (children) children->GetLength(&childCount); nsAutoString namespaceURI, localName; for (PRUint32 i = 0; i < childCount; ++i) { nsCOMPtr child; children->Item(i, getter_AddRefs(child)); NS_ASSERTION(child, "there can't be null items in the NodeList!"); child->GetLocalName(localName); if (localName.EqualsLiteral("bind")) { child->GetNamespaceURI(namespaceURI); if (namespaceURI.EqualsLiteral(NS_NAMESPACE_XFORMS)) { rv = ProcessBind(xpath, firstInstanceRoot, 1, 1, nsCOMPtr(do_QueryInterface(child)), PR_TRUE); if (NS_FAILED(rv)) { return NS_OK; } } } } return NS_OK; } void nsXFormsModelElement::Reset() { BackupOrRestoreInstanceData(PR_TRUE); nsXFormsUtils::DispatchEvent(mElement, eEvent_Rebuild); nsXFormsUtils::DispatchEvent(mElement, eEvent_Recalculate); nsXFormsUtils::DispatchEvent(mElement, eEvent_Revalidate); nsXFormsUtils::DispatchEvent(mElement, eEvent_Refresh); } // This function will restore all of the model's instance data to it's original // state if the supplied boolean is PR_TRUE. If it is PR_FALSE, this function // will cause this model's instance data to be backed up. void nsXFormsModelElement::BackupOrRestoreInstanceData(PRBool restore) { if (!mInstanceDocuments) return; PRUint32 instCount; mInstanceDocuments->GetLength(&instCount); if (instCount) { for (PRUint32 i = 0; i < instCount; ++i) { nsIInstanceElementPrivate *instance = mInstanceDocuments->GetInstanceAt(i); // 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(); } } } } nsresult nsXFormsModelElement::FinishConstruction() { // process inline schemas that aren't referenced via the schema attribute nsCOMPtr children; mElement->GetChildNodes(getter_AddRefs(children)); if (children) { PRUint32 childCount = 0; children->GetLength(&childCount); nsCOMPtr node; nsCOMPtr element; nsAutoString nsURI, localName, targetNamespace; for (PRUint32 i = 0; i < childCount; ++i) { children->Item(i, getter_AddRefs(node)); element = do_QueryInterface(node); if (!element) continue; node->GetNamespaceURI(nsURI); node->GetLocalName(localName); if (nsURI.EqualsLiteral(NS_NAMESPACE_XML_SCHEMA) && localName.EqualsLiteral("schema")) { // we don't have to check if the schema was already added because // nsSchemaLoader::ProcessSchemaElement takes care of that. nsCOMPtr schema; nsresult rv = mSchemas->ProcessSchemaElement(element, nsnull, getter_AddRefs(schema)); if (!NS_SUCCEEDED(rv)) { nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaProcessError"), node); } } } } // (XForms 4.2.1 - cont) // 3. if applicable, initialize P3P // 4. construct instance data from initial instance data. apply all // elements in document order. // we get the instance data from our instance child nodes // We're done initializing this model. // 5. Perform an xforms-rebuild, xforms-recalculate, and xforms-revalidate in // sequence, for this model element. (The xforms-refresh is not performed // since the user interface has not yet been initialized). nsXFormsUtils::DispatchEvent(mElement, eEvent_Rebuild); nsXFormsUtils::DispatchEvent(mElement, eEvent_Recalculate); nsXFormsUtils::DispatchEvent(mElement, eEvent_Revalidate); return NS_OK; } nsresult nsXFormsModelElement::InitializeControls() { #ifdef DEBUG printf("nsXFormsModelElement::InitializeControls()\n"); #endif nsPostRefresh postRefresh = nsPostRefresh(); nsXFormsControlListItem::iterator it; nsresult rv; for (it = mFormControls.begin(); it != mFormControls.end(); ++it) { // Get control nsCOMPtr control = (*it)->Control(); NS_ASSERTION(control, "mFormControls has null control?!"); #ifdef DEBUG_MODEL printf("\tControl (%p): ", (void*) control); nsCOMPtr controlElement; control->GetElement(getter_AddRefs(controlElement)); // DBG_TAGINFO(controlElement); #endif // Rebind rv = control->Bind(); NS_ENSURE_SUCCESS(rv, rv); // Get bound node nsCOMPtr boundNode; rv = control->GetBoundNode(getter_AddRefs(boundNode)); NS_ENSURE_SUCCESS(rv, rv); // Set MIP states on control rv = SetStatesInternal(control, boundNode, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); // Refresh controls rv = control->Refresh(); NS_ENSURE_SUCCESS(rv, rv); } mChangedNodes.Clear(); return NS_OK; } void nsXFormsModelElement::MaybeNotifyCompletion() { nsCOMPtr domDoc; mElement->GetOwnerDocument(getter_AddRefs(domDoc)); const nsVoidArray *models = GetModelList(domDoc); if (!models) { NS_NOTREACHED("no model list property"); return; } PRInt32 i; // Nothing to be done if any model is incomplete or hasn't seen // DOMContentLoaded. for (i = 0; i < models->Count(); ++i) { nsXFormsModelElement *model = NS_STATIC_CAST(nsXFormsModelElement *, models->ElementAt(i)); if (!model->mDocumentLoaded || !model->IsComplete()) return; } // Okay, dispatch xforms-model-construct-done and xforms-ready events! for (i = 0; i < models->Count(); ++i) { nsXFormsModelElement *model = NS_STATIC_CAST(nsXFormsModelElement *, models->ElementAt(i)); nsXFormsUtils::DispatchEvent(model->mElement, eEvent_ModelConstructDone); } nsXFormsModelElement::ProcessDeferredBinds(domDoc); nsCOMPtr doc = do_QueryInterface(domDoc); if (doc) { PRUint32 loadingMessages = NS_PTR_TO_UINT32( doc->GetProperty(nsXFormsAtoms::externalMessagesProperty)); if (loadingMessages) { // if we are still waiting for external messages to load, then put off // the xforms-ready until a model in the document is notified that they // are finished loading return; } } for (i = 0; i < models->Count(); ++i) { nsXFormsModelElement *model = NS_STATIC_CAST(nsXFormsModelElement *, models->ElementAt(i)); model->BackupOrRestoreInstanceData(PR_FALSE); model->mReadyHandled = PR_TRUE; nsXFormsUtils::DispatchEvent(model->mElement, eEvent_Ready); } } nsresult nsXFormsModelElement::ProcessBind(nsIXFormsXPathEvaluator *aEvaluator, nsIDOMNode *aContextNode, PRInt32 aContextPosition, PRInt32 aContextSize, nsIDOMElement *aBindElement, PRBool aIsOuter) { // Get the model item properties specified by this \. nsCOMPtr props[eModel__count]; nsAutoString propStrings[eModel__count]; nsresult rv; nsAutoString attrStr; for (PRUint32 i = 0; i < eModel__count; ++i) { sModelPropsList[i]->ToString(attrStr); aBindElement->GetAttribute(attrStr, propStrings[i]); if (!propStrings[i].IsEmpty() && i != eModel_type && i != eModel_p3ptype) { rv = aEvaluator->CreateExpression(propStrings[i], aBindElement, getter_AddRefs(props[i])); if (NS_FAILED(rv)) { const PRUnichar *strings[] = { propStrings[i].get() }; nsXFormsUtils::ReportError(NS_LITERAL_STRING("mipParseError"), strings, 1, aBindElement, aBindElement); nsXFormsUtils::DispatchEvent(mElement, eEvent_ComputeException); return rv; } } } // Find the nodeset that this bind applies to. nsCOMPtr result; nsAutoString expr; aBindElement->GetAttribute(NS_LITERAL_STRING("nodeset"), expr); if (expr.IsEmpty()) { expr = NS_LITERAL_STRING("."); } rv = aEvaluator->Evaluate(expr, aContextNode, aContextPosition, aContextSize, aBindElement, nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, nsnull, getter_AddRefs(result)); if (NS_FAILED(rv)) { if (rv == NS_ERROR_DOM_INVALID_EXPRESSION_ERR) { // the xpath expression isn't valid xpath const nsPromiseFlatString& flat = PromiseFlatString(expr); const PRUnichar *strings[] = { flat.get() }; nsXFormsUtils::ReportError(NS_LITERAL_STRING("exprParseError"), strings, 1, aBindElement, nsnull); nsXFormsUtils::DispatchEvent(mElement, eEvent_ComputeException); } else { #ifdef DEBUG printf("xforms-binding-exception: XPath Evaluation failed\n"); #endif const PRUnichar *strings[] = { expr.get() }; nsXFormsUtils::ReportError(NS_LITERAL_STRING("nodesetEvaluateError"), strings, 1, aBindElement, aBindElement); nsXFormsUtils::DispatchEvent(mElement, eEvent_BindingException); } return rv; } NS_ENSURE_STATE(result); // If this is an outer bind, store the nodeset, as controls binding to this // bind will need this. if (aIsOuter) { nsCOMPtr content(do_QueryInterface(aBindElement)); NS_ASSERTION(content, "nsIDOMElement not implementing nsIContent?!"); rv = content->SetProperty(nsXFormsAtoms::bind, result, SupportsDtorFunc); NS_ENSURE_SUCCESS(rv, rv); // addref, circumventing nsDerivedSave NS_ADDREF(NS_STATIC_CAST(nsIDOMXPathResult*, result)); } PRUint32 snapLen; rv = result->GetSnapshotLength(&snapLen); NS_ENSURE_SUCCESS(rv, rv); // Iterate over resultset nsCOMArray deps; nsCOMPtr node; PRUint32 snapItem; for (snapItem = 0; snapItem < snapLen; ++snapItem) { rv = result->SnapshotItem(snapItem, getter_AddRefs(node)); NS_ENSURE_SUCCESS(rv, rv); if (!node) { NS_WARNING("nsXFormsModelElement::ProcessBind(): Empty node in result set."); continue; } // Apply MIPs nsXFormsXPathParser parser; nsXFormsXPathAnalyzer analyzer(aEvaluator, aBindElement); PRBool multiMIP = PR_FALSE; for (PRUint32 j = 0; j < eModel__count; ++j) { if (propStrings[j].IsEmpty()) continue; // type and p3ptype are stored as properties on the instance node if (j == eModel_type || j == eModel_p3ptype) { nsClassHashtable *table; table = j == eModel_type ? &mNodeToType : &mNodeToP3PType; NS_ENSURE_TRUE(table->IsInitialized(), NS_ERROR_FAILURE); // Check for existing value if (table->Get(node, nsnull)) { multiMIP = PR_TRUE; break; } // Insert value nsAutoPtr newString(new nsString(propStrings[j])); NS_ENSURE_TRUE(newString, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(table->Put(node, newString), NS_ERROR_OUT_OF_MEMORY); // string is succesfully stored in the table, we should not dealloc it newString.forget(); if (j == eModel_type) { // Inform MDG that it needs to check type. The only arguments // actually used are |eModel_constraint| and |node|. rv = mMDG.AddMIP(eModel_constraint, nsnull, nsnull, PR_FALSE, node, 1, 1); NS_ENSURE_SUCCESS(rv, rv); } } else { // the rest of the MIPs are given to the MDG nsCOMPtr expr = props[j]; // Get node dependencies nsAutoPtr xNode(parser.Parse(propStrings[j])); deps.Clear(); rv = analyzer.Analyze(node, xNode, expr, &propStrings[j], &deps, snapItem + 1, snapLen, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); // Insert into MDG rv = mMDG.AddMIP((ModelItemPropName) j, expr, &deps, parser.UsesDynamicFunc(), node, snapItem + 1, snapLen); // if the call results in NS_ERROR_ABORT the page has tried to set a // MIP twice, break and emit an exception. if (rv == NS_ERROR_ABORT) { multiMIP = PR_TRUE; break; } NS_ENSURE_SUCCESS(rv, rv); } } // If the attribute is already there, the page sets a MIP twice // which is illegal, and should result in an xforms-binding-exception. // @see http://www.w3.org/TR/xforms/slice4.html#evt-modelConstruct // (item 4, c) if (multiMIP) { #ifdef DEBUG printf("xforms-binding-exception: Multiple MIPs on same node!"); #endif nsXFormsUtils::ReportError(NS_LITERAL_STRING("multiMIPError"), aBindElement); nsXFormsUtils::DispatchEvent(aBindElement, eEvent_BindingException); return NS_ERROR_FAILURE; } // Now evaluate any child \ elements. nsCOMPtr children; aBindElement->GetChildNodes(getter_AddRefs(children)); if (children) { PRUint32 childCount = 0; children->GetLength(&childCount); nsCOMPtr child; nsAutoString value; for (PRUint32 k = 0; k < childCount; ++k) { children->Item(k, getter_AddRefs(child)); if (child) { child->GetLocalName(value); if (!value.EqualsLiteral("bind")) continue; child->GetNamespaceURI(value); if (!value.EqualsLiteral(NS_NAMESPACE_XFORMS)) continue; rv = ProcessBind(aEvaluator, node, snapItem + 1, snapLen, nsCOMPtr(do_QueryInterface(child))); NS_ENSURE_SUCCESS(rv, rv); } } } } return NS_OK; } NS_IMETHODIMP nsXFormsModelElement::SetStates(nsIXFormsControl *aControl, nsIDOMNode *aBoundNode) { return SetStatesInternal(aControl, aBoundNode, PR_FALSE); } nsresult nsXFormsModelElement::AddInstanceElement(nsIInstanceElementPrivate *aInstEle) { NS_ENSURE_STATE(mInstanceDocuments); mInstanceDocuments->AddInstance(aInstEle); return NS_OK; } nsresult nsXFormsModelElement::RemoveInstanceElement(nsIInstanceElementPrivate *aInstEle) { NS_ENSURE_STATE(mInstanceDocuments); mInstanceDocuments->RemoveInstance(aInstEle); return NS_OK; } nsresult nsXFormsModelElement::MessageLoadFinished() { // This is our signal that all external message links have been tested. If // we were waiting for this to send out xforms-ready, then now is the time. // if this document hasn't processed xforms-model-construct-done, yet (which // must precede xforms-ready), then we'll send out the xforms-ready later // as part of our normal handling. If we've already become ready, then this // event was probably generated by a change in the src attribute on the // message element. Ignore it in that case. if (!mConstructDoneHandled || mReadyHandled) { return NS_OK; } nsCOMPtr domDoc; mElement->GetOwnerDocument(getter_AddRefs(domDoc)); const nsVoidArray *models = GetModelList(domDoc); nsCOMPtrdoc = do_QueryInterface(domDoc); nsCOMArray *deferredBindList = NS_STATIC_CAST(nsCOMArray *, doc->GetProperty(nsXFormsAtoms::deferredBindListProperty)); // if we've already gotten the xforms-model-construct-done event and not // yet the xforms-ready, we've hit a window where we may still be // processing the deferred control binding. If so, we'll leave now and // leave it to MaybeNotifyCompletion to generate the xforms-ready event. if (deferredBindList) { return NS_OK; } // if we reached here, then we had to wait on sending out the xforms-ready // events until the external messages were tested. Now we are finally // ready to send out xforms-ready to all of the models. for (int i = 0; i < models->Count(); ++i) { nsXFormsModelElement *model = NS_STATIC_CAST(nsXFormsModelElement *, models->ElementAt(i)); model->mReadyHandled = PR_TRUE; nsXFormsUtils::DispatchEvent(model->mElement, eEvent_Ready); } return NS_OK; } /* static */ void nsXFormsModelElement::Startup() { sModelPropsList[eModel_type] = nsXFormsAtoms::type; sModelPropsList[eModel_readonly] = nsXFormsAtoms::readonly; sModelPropsList[eModel_required] = nsXFormsAtoms::required; sModelPropsList[eModel_relevant] = nsXFormsAtoms::relevant; sModelPropsList[eModel_calculate] = nsXFormsAtoms::calculate; sModelPropsList[eModel_constraint] = nsXFormsAtoms::constraint; sModelPropsList[eModel_p3ptype] = nsXFormsAtoms::p3ptype; } static void DeleteBindList(void *aObject, nsIAtom *aPropertyName, void *aPropertyValue, void *aData) { delete NS_STATIC_CAST(nsCOMArray *, aPropertyValue); } /* static */ nsresult nsXFormsModelElement::DeferElementBind(nsIDOMDocument *aDoc, nsIXFormsControlBase *aControl) { nsCOMPtr doc = do_QueryInterface(aDoc); if (!doc || !aControl) { return NS_ERROR_FAILURE; } nsCOMArray *deferredBindList = NS_STATIC_CAST(nsCOMArray *, doc->GetProperty(nsXFormsAtoms::deferredBindListProperty)); if (!deferredBindList) { deferredBindList = new nsCOMArray(16); NS_ENSURE_TRUE(deferredBindList, NS_ERROR_OUT_OF_MEMORY); doc->SetProperty(nsXFormsAtoms::deferredBindListProperty, deferredBindList, DeleteBindList); } // always append to the end of the list. We need to keep the elements in // document order when we process the binds later. Otherwise we have trouble // when an element is trying to bind and should use its parent as a context // for the xpath evaluation but the parent isn't bound yet. deferredBindList->AppendObject(aControl); return NS_OK; } /* static */ void nsXFormsModelElement::ProcessDeferredBinds(nsIDOMDocument *aDoc) { nsCOMPtr doc = do_QueryInterface(aDoc); if (!doc) { return; } nsPostRefresh postRefresh = nsPostRefresh(); doc->SetProperty(nsXFormsAtoms::readyForBindProperty, doc); nsCOMArray *deferredBindList = NS_STATIC_CAST(nsCOMArray *, doc->GetProperty(nsXFormsAtoms::deferredBindListProperty)); if (deferredBindList) { for (int i = 0; i < deferredBindList->Count(); ++i) { nsIXFormsControlBase *base = deferredBindList->ObjectAt(i); if (base) { base->Bind(); base->Refresh(); } } doc->DeleteProperty(nsXFormsAtoms::deferredBindListProperty); } } nsresult nsXFormsModelElement::HandleLoad(nsIDOMEvent* aEvent) { if (!mInstancesInitialized) { // XXX This is for Bug 308106. In Gecko 1.8 DoneAddingChildren is not // called in XUL if the element doesn't have any child nodes. InitializeInstances(); } mDocumentLoaded = PR_TRUE; // dispatch xforms-model-construct, xforms-rebuild, xforms-recalculate, // xforms-revalidate // We wait until DOMContentLoaded to dispatch xforms-model-construct, // since the model may have an action handler for this event and Mozilla // doesn't register XML Event listeners until the document is loaded. // xforms-model-construct is not cancellable, so always proceed. nsXFormsUtils::DispatchEvent(mElement, eEvent_ModelConstruct); if (mPendingInlineSchemas.Count() > 0) { nsCOMPtr el; nsresult rv; for (PRInt32 i=0; i schema; // no need to observe errors via the callback. instead, rely on // this method returning a failure code when it encounters errors. rv = mSchemas->ProcessSchemaElement(el, nsnull, getter_AddRefs(schema)); if (NS_SUCCEEDED(rv)) mSchemaCount++; } if (NS_FAILED(rv)) { // this is a fatal error (XXX) nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement); nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException); return NS_OK; } } if (IsComplete()) { rv = FinishConstruction(); NS_ENSURE_SUCCESS(rv, rv); } mPendingInlineSchemas.Clear(); } // We may still be waiting on external documents to load. MaybeNotifyCompletion(); return NS_OK; } nsresult nsXFormsModelElement::HandleUnload(nsIDOMEvent* aEvent) { // due to fastback changes, had to move this notification out from under // model's WillChangeDocument override. return nsXFormsUtils::DispatchEvent(mElement, eEvent_ModelDestruct); } nsresult NS_NewXFormsModelElement(nsIXTFElement **aResult) { *aResult = new nsXFormsModelElement(); if (!*aResult) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*aResult); return NS_OK; } // ---------------------------- // // nsXFormsModelInstanceDocuments NS_IMPL_ISUPPORTS2(nsXFormsModelInstanceDocuments, nsIDOMNodeList, nsIClassInfo) nsXFormsModelInstanceDocuments::nsXFormsModelInstanceDocuments() : mInstanceList(16) { } NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetLength(PRUint32* aLength) { *aLength = mInstanceList.Count(); return NS_OK; } NS_IMETHODIMP nsXFormsModelInstanceDocuments::Item(PRUint32 aIndex, nsIDOMNode** aReturn) { *aReturn = nsnull; nsIInstanceElementPrivate* instance = mInstanceList.SafeObjectAt(aIndex); if (instance) { nsCOMPtr doc; if (NS_SUCCEEDED(instance->GetInstanceDocument(getter_AddRefs(doc))) && doc) { NS_ADDREF(*aReturn = doc); } } return NS_OK; } nsIInstanceElementPrivate* nsXFormsModelInstanceDocuments::GetInstanceAt(PRUint32 aIndex) { return mInstanceList.ObjectAt(aIndex); } void nsXFormsModelInstanceDocuments::AddInstance(nsIInstanceElementPrivate *aInst) { // 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(aInst); } void nsXFormsModelInstanceDocuments::RemoveInstance(nsIInstanceElementPrivate *aInst) { mInstanceList.RemoveObject(aInst); } void nsXFormsModelInstanceDocuments::DropReferences() { mInstanceList.Clear(); } // nsIClassInfo implementation static const nsIID sInstScriptingIIDs[] = { NS_IDOMNODELIST_IID }; NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetInterfaces(PRUint32 *aCount, nsIID * **aArray) { return nsXFormsUtils::CloneScriptingInterfaces(sInstScriptingIIDs, NS_ARRAY_LENGTH(sInstScriptingIIDs), aCount, aArray); } NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetHelperForLanguage(PRUint32 language, nsISupports **_retval) { *_retval = nsnull; return NS_OK; } NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetContractID(char * *aContractID) { *aContractID = nsnull; return NS_OK; } NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetClassDescription(char * *aClassDescription) { *aClassDescription = nsnull; return NS_OK; } NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetClassID(nsCID * *aClassID) { *aClassID = nsnull; return NS_OK; } NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetImplementationLanguage(PRUint32 *aLang) { *aLang = nsIProgrammingLanguage::CPLUSPLUS; return NS_OK; } NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetFlags(PRUint32 *aFlags) { *aFlags = nsIClassInfo::DOM_OBJECT; return NS_OK; } NS_IMETHODIMP nsXFormsModelInstanceDocuments::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; }