Import from 1.9a8 tarball
[mozilla-extra.git] / extensions / xforms / nsXFormsModelElement.cpp
blobf5f61b078bd120af59290214597ae1ffd19bdb4e
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla XForms support.
17 * The Initial Developer of the Original Code is
18 * IBM Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2004
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Brian Ryner <bryner@brianryner.com>
24 * Allan Beaufour <abeaufour@novell.com>
25 * Darin Fisher <darin@meer.net>
26 * Olli Pettay <Olli.Pettay@helsinki.fi>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsXFormsModelElement.h"
43 #include "nsIXTFElementWrapper.h"
44 #include "nsMemory.h"
45 #include "nsIDOMElement.h"
46 #include "nsIDOM3Node.h"
47 #include "nsIDOMNodeList.h"
48 #include "nsString.h"
49 #include "nsIDocument.h"
50 #include "nsXFormsAtoms.h"
51 #include "nsINameSpaceManager.h"
52 #include "nsIServiceManager.h"
53 #include "nsIDOMEvent.h"
54 #include "nsIDOMDOMImplementation.h"
55 #include "nsIDOMXMLDocument.h"
56 #include "nsIDOMEventTarget.h"
57 #include "nsIDOMXPathResult.h"
58 #include "nsIDOMXPathEvaluator.h"
59 #include "nsIXPathEvaluatorInternal.h"
60 #include "nsIDOMXPathExpression.h"
61 #include "nsIDOMXPathNSResolver.h"
62 #include "nsIDOMNSXPathExpression.h"
63 #include "nsIContent.h"
64 #include "nsIURL.h"
65 #include "nsNetUtil.h"
66 #include "nsIXFormsControl.h"
67 #include "nsXFormsTypes.h"
68 #include "nsXFormsXPathParser.h"
69 #include "nsXFormsXPathAnalyzer.h"
70 #include "nsIInstanceElementPrivate.h"
71 #include "nsXFormsUtils.h"
72 #include "nsXFormsSchemaValidator.h"
73 #include "nsIXFormsUIWidget.h"
74 #include "nsIAttribute.h"
75 #include "nsISchemaLoader.h"
76 #include "nsISchema.h"
77 #include "nsAutoPtr.h"
78 #include "nsIDOMDocumentXBL.h"
79 #include "nsIProgrammingLanguage.h"
80 #include "nsDOMError.h"
81 #include "nsXFormsControlStub.h"
82 #include "nsIPrefService.h"
83 #include "nsIPrefBranch.h"
84 #include "nsIEventStateManager.h"
85 #include "nsStringEnumerator.h"
87 #define XFORMS_LAZY_INSTANCE_BINDING \
88 "chrome://xforms/content/xforms.xml#xforms-lazy-instance"
90 #ifdef DEBUG
91 //#define DEBUG_MODEL
92 #endif
94 //------------------------------------------------------------------------------
96 // Helper function for using XPath to locate an <xsd:schema> element by
97 // matching its "id" attribute. This is necessary since <xsd:schema> is
98 // treated as an ordinary XML data node without an "ID" attribute.
99 static void
100 GetSchemaElementById(nsIDOMElement *contextNode,
101 const nsString &id,
102 nsIDOMElement **resultNode)
104 // search for an element with the given "id" attribute, and then verify
105 // that the element is in the XML Schema namespace.
107 nsAutoString expr;
108 expr.AssignLiteral("//*[@id=\"");
109 expr.Append(id);
110 expr.AppendLiteral("\"]");
112 nsCOMPtr<nsIDOMXPathResult> xpRes;
113 nsresult rv =
114 nsXFormsUtils::EvaluateXPath(expr,
115 contextNode,
116 contextNode,
117 nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
118 getter_AddRefs(xpRes));
119 if (NS_SUCCEEDED(rv) && xpRes) {
120 nsCOMPtr<nsIDOMNode> node;
121 xpRes->GetSingleNodeValue(getter_AddRefs(node));
122 if (node) {
123 nsAutoString ns;
124 node->GetNamespaceURI(ns);
125 if (ns.EqualsLiteral(NS_NAMESPACE_XML_SCHEMA))
126 CallQueryInterface(node, resultNode);
131 //------------------------------------------------------------------------------
133 static void
134 DeleteVoidArray(void *aObject,
135 nsIAtom *aPropertyName,
136 void *aPropertyValue,
137 void *aData)
139 delete static_cast<nsVoidArray *>(aPropertyValue);
142 static nsresult
143 AddToModelList(nsIDOMDocument *domDoc, nsXFormsModelElement *model)
145 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
147 nsVoidArray *models =
148 static_cast<nsVoidArray *>
149 (doc->GetProperty(nsXFormsAtoms::modelListProperty));
150 if (!models) {
151 models = new nsVoidArray(16);
152 if (!models)
153 return NS_ERROR_OUT_OF_MEMORY;
154 doc->SetProperty(nsXFormsAtoms::modelListProperty, models, DeleteVoidArray);
156 models->AppendElement(model);
157 return NS_OK;
160 static void
161 RemoveFromModelList(nsIDOMDocument *domDoc, nsXFormsModelElement *model)
163 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
165 nsVoidArray *models =
166 static_cast<nsVoidArray *>
167 (doc->GetProperty(nsXFormsAtoms::modelListProperty));
168 if (models)
169 models->RemoveElement(model);
172 static const nsVoidArray *
173 GetModelList(nsIDOMDocument *domDoc)
175 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
177 return static_cast<nsVoidArray *>
178 (doc->GetProperty(nsXFormsAtoms::modelListProperty));
181 static void
182 SupportsDtorFunc(void *aObject, nsIAtom *aPropertyName,
183 void *aPropertyValue, void *aData)
185 nsISupports *propertyValue = static_cast<nsISupports*>(aPropertyValue);
186 NS_IF_RELEASE(propertyValue);
190 //------------------------------------------------------------------------------
191 // --- nsXFormsControlListItem ---
194 nsXFormsControlListItem::iterator::iterator()
195 : mCur(0)
199 nsXFormsControlListItem::iterator::iterator(const nsXFormsControlListItem::iterator& aCopy)
200 : mCur(aCopy.mCur)
202 mStack = aCopy.mStack;
205 nsXFormsControlListItem::iterator
206 nsXFormsControlListItem::iterator::operator=(nsXFormsControlListItem* aCnt)
208 mCur = aCnt;
209 return *this;
212 bool
213 nsXFormsControlListItem::iterator::operator!=(const nsXFormsControlListItem* aCnt)
215 return mCur != aCnt;
218 nsXFormsControlListItem::iterator
219 nsXFormsControlListItem::iterator::operator++()
221 if (!mCur)
222 return *this;
224 if (mCur->mFirstChild) {
225 if (!mCur->mNextSibling) {
226 mCur = mCur->mFirstChild;
227 return *this;
229 mStack.AppendElement(mCur->mFirstChild);
232 if (mCur->mNextSibling) {
233 mCur = mCur->mNextSibling;
234 } else if (mStack.Count()) {
235 mCur = (nsXFormsControlListItem*) mStack[mStack.Count() - 1];
236 mStack.RemoveElementAt(mStack.Count() - 1);
237 } else {
238 mCur = nsnull;
241 return *this;
244 nsXFormsControlListItem*
245 nsXFormsControlListItem::iterator::operator*()
247 return mCur;
250 nsXFormsControlListItem::nsXFormsControlListItem(
251 nsIXFormsControl* aControl,
252 nsRefPtrHashtable<nsISupportsHashKey,nsXFormsControlListItem>* aHashtable)
253 : mNode(aControl),
254 mNextSibling(nsnull),
255 mFirstChild(nsnull),
256 mControlListHash(aHashtable)
261 nsXFormsControlListItem::~nsXFormsControlListItem()
263 Clear();
266 nsXFormsControlListItem::nsXFormsControlListItem(const nsXFormsControlListItem& aCopy)
267 : mNode(aCopy.mNode)
269 if (aCopy.mNextSibling) {
270 mNextSibling = new nsXFormsControlListItem(*aCopy.mNextSibling);
271 NS_WARN_IF_FALSE(mNextSibling, "could not new?!");
272 } else {
273 mNextSibling = nsnull;
276 if (aCopy.mFirstChild) {
277 mFirstChild = new nsXFormsControlListItem(*aCopy.mFirstChild);
278 NS_WARN_IF_FALSE(mFirstChild, "could not new?!");
279 } else {
280 mFirstChild = nsnull;
284 void
285 nsXFormsControlListItem::Clear()
287 if (mFirstChild) {
288 mFirstChild->Clear();
289 NS_ASSERTION(!(mFirstChild->mFirstChild || mFirstChild->mNextSibling),
290 "child did not clear members!!");
291 mFirstChild = nsnull;
293 if (mNextSibling) {
294 mNextSibling->Clear();
295 NS_ASSERTION(!(mNextSibling->mFirstChild || mNextSibling->mNextSibling),
296 "sibling did not clear members!!");
297 mNextSibling = nsnull;
299 if (mNode) {
300 /* we won't bother removing each item one by one from the hashtable. This
301 * approach assumes that we are clearing the whole model's list of controls
302 * due to the model going away. After the model clears this list, it will
303 * clear the hashtable all at once.
305 mControlListHash = nsnull;
306 mNode = nsnull;
310 nsresult
311 nsXFormsControlListItem::AddControl(nsIXFormsControl *aControl,
312 nsIXFormsControl *aParent)
314 // Four insertion posibilities:
316 // 1) Delegate to first child from root node
317 if (!mNode && mFirstChild) {
318 return mFirstChild->AddControl(aControl, aParent);
321 // 2) control with no parent
322 if (!aParent) {
323 nsRefPtr<nsXFormsControlListItem> newNode =
324 new nsXFormsControlListItem(aControl, mControlListHash);
325 NS_ENSURE_TRUE(newNode, NS_ERROR_OUT_OF_MEMORY);
327 // Empty tree (we have already checked mFirstChild)
328 if (!mNode) {
329 mFirstChild = newNode;
330 nsCOMPtr<nsIDOMElement> ele;
331 aControl->GetElement(getter_AddRefs(ele));
332 mControlListHash->Put(ele, newNode);
333 return NS_OK;
336 if (mNextSibling) {
337 newNode->mNextSibling = mNextSibling;
339 mNextSibling = newNode;
340 nsCOMPtr<nsIDOMElement> ele;
341 aControl->GetElement(getter_AddRefs(ele));
342 mControlListHash->Put(ele, newNode);
343 #ifdef DEBUG
344 nsXFormsControlListItem* next = newNode->mNextSibling;
345 while (next) {
346 NS_ASSERTION(aControl != next->mNode,
347 "Node already in tree!!");
348 next = next->mNextSibling;
350 #endif
352 return NS_OK;
355 // Locate parent
356 nsXFormsControlListItem* parentControl = FindControl(aParent);
357 NS_ASSERTION(parentControl, "Parent not found?!");
359 // 3) parentControl has a first child, insert as sibling to that
360 if (parentControl->mFirstChild) {
361 return parentControl->mFirstChild->AddControl(aControl, nsnull);
364 // 4) first child for parentControl
365 nsRefPtr<nsXFormsControlListItem> newNode =
366 new nsXFormsControlListItem(aControl, mControlListHash);
367 NS_ENSURE_TRUE(newNode, NS_ERROR_OUT_OF_MEMORY);
369 parentControl->mFirstChild = newNode;
370 nsCOMPtr<nsIDOMElement> ele;
371 aControl->GetElement(getter_AddRefs(ele));
372 mControlListHash->Put(ele, newNode);
374 return NS_OK;
377 nsresult
378 nsXFormsControlListItem::RemoveControl(nsIXFormsControl *aControl,
379 PRBool &aRemoved)
381 nsXFormsControlListItem* deleteMe = nsnull;
382 aRemoved = PR_FALSE;
384 // Try children
385 if (mFirstChild) {
386 // The control to remove is our first child
387 if (mFirstChild->mNode == aControl) {
388 deleteMe = mFirstChild;
390 // Fix siblings
391 if (deleteMe->mNextSibling) {
392 mFirstChild = deleteMe->mNextSibling;
393 deleteMe->mNextSibling = nsnull;
394 } else {
395 mFirstChild = nsnull;
398 // Fix children
399 if (deleteMe->mFirstChild) {
400 if (!mFirstChild) {
401 mFirstChild = deleteMe->mFirstChild;
402 } else {
403 nsXFormsControlListItem *insertPos = mFirstChild;
404 while (insertPos->mNextSibling) {
405 insertPos = insertPos->mNextSibling;
407 insertPos->mNextSibling = deleteMe->mFirstChild;
409 deleteMe->mFirstChild = nsnull;
411 } else {
412 // Run through children
413 nsresult rv = mFirstChild->RemoveControl(aControl, aRemoved);
414 NS_ENSURE_SUCCESS(rv, rv);
415 if (aRemoved)
416 return rv;
420 // Try siblings
421 if (!deleteMe && mNextSibling) {
422 if (mNextSibling->mNode == aControl) {
423 deleteMe = mNextSibling;
424 // Fix siblings
425 if (deleteMe->mNextSibling) {
426 mNextSibling = deleteMe->mNextSibling;
427 deleteMe->mNextSibling = nsnull;
428 } else {
429 mNextSibling = nsnull;
431 // Fix children
432 if (deleteMe->mFirstChild) {
433 if (!mNextSibling) {
434 mNextSibling = deleteMe->mFirstChild;
435 } else {
436 nsXFormsControlListItem *insertPos = mNextSibling;
437 while (insertPos->mNextSibling) {
438 insertPos = insertPos->mNextSibling;
440 insertPos->mNextSibling = deleteMe->mFirstChild;
442 deleteMe->mFirstChild = nsnull;
444 } else {
445 // run through siblings
446 return mNextSibling->RemoveControl(aControl, aRemoved);
450 if (deleteMe) {
451 NS_ASSERTION(!(deleteMe->mNextSibling),
452 "Deleted control should not have siblings!");
453 NS_ASSERTION(!(deleteMe->mFirstChild),
454 "Deleted control should not have children!");
455 nsCOMPtr<nsIDOMElement> element;
456 deleteMe->mNode->GetElement(getter_AddRefs(element));
457 mControlListHash->Remove(element);
458 aRemoved = PR_TRUE;
461 return NS_OK;
464 nsXFormsControlListItem*
465 nsXFormsControlListItem::FindControl(nsIXFormsControl *aControl)
467 if (!aControl)
468 return nsnull;
470 nsRefPtr<nsXFormsControlListItem> listItem;
471 nsCOMPtr<nsIDOMElement> element;
472 aControl->GetElement(getter_AddRefs(element));
473 mControlListHash->Get(element, getter_AddRefs(listItem));
474 return listItem;
477 already_AddRefed<nsIXFormsControl>
478 nsXFormsControlListItem::Control()
480 nsIXFormsControl* res = nsnull;
481 if (mNode)
482 NS_ADDREF(res = mNode);
483 NS_WARN_IF_FALSE(res, "Returning nsnull for a control. Bad sign.");
484 return res;
487 nsXFormsControlListItem*
488 nsXFormsControlListItem::begin()
490 // handle root
491 if (!mNode)
492 return mFirstChild;
494 return this;
497 nsXFormsControlListItem*
498 nsXFormsControlListItem::end()
500 return nsnull;
504 //------------------------------------------------------------------------------
506 static const nsIID sScriptingIIDs[] = {
507 NS_IXFORMSMODELELEMENT_IID,
508 NS_IXFORMSNSMODELELEMENT_IID
511 static nsIAtom* sModelPropsList[eModel__count];
513 // This can be nsVoidArray because elements will remove
514 // themselves from the list if they are deleted during refresh.
515 static nsVoidArray* sPostRefreshList = nsnull;
516 static nsVoidArray* sContainerPostRefreshList = nsnull;
518 static PRInt32 sRefreshing = 0;
520 nsPostRefresh::nsPostRefresh()
522 #ifdef DEBUG_smaug
523 printf("nsPostRefresh\n");
524 #endif
525 ++sRefreshing;
528 nsPostRefresh::~nsPostRefresh()
530 #ifdef DEBUG_smaug
531 printf("~nsPostRefresh\n");
532 #endif
534 if (sRefreshing != 1) {
535 --sRefreshing;
536 return;
539 if (sPostRefreshList) {
540 while (sPostRefreshList->Count()) {
541 // Iterating this way because refresh can lead to
542 // additions/deletions in sPostRefreshList.
543 // Iterating from last to first saves possibly few memcopies,
544 // see nsVoidArray::RemoveElementsAt().
545 PRInt32 last = sPostRefreshList->Count() - 1;
546 nsIXFormsControl* control =
547 static_cast<nsIXFormsControl*>(sPostRefreshList->ElementAt(last));
548 sPostRefreshList->RemoveElementAt(last);
549 if (control)
550 control->Refresh();
552 if (sRefreshing == 1) {
553 delete sPostRefreshList;
554 sPostRefreshList = nsnull;
558 --sRefreshing;
560 // process sContainerPostRefreshList after we've decremented sRefreshing.
561 // container->refresh below could ask for ContainerNeedsPostRefresh which
562 // will add an item to the sContainerPostRefreshList if sRefreshing > 0.
563 // So keeping this under sRefreshing-- will avoid an infinite loop.
564 while (sContainerPostRefreshList && sContainerPostRefreshList->Count()) {
565 PRInt32 last = sContainerPostRefreshList->Count() - 1;
566 nsIXFormsControl* container =
567 static_cast<nsIXFormsControl*>(sContainerPostRefreshList->ElementAt(last));
568 sContainerPostRefreshList->RemoveElementAt(last);
569 if (container) {
570 container->Refresh();
573 delete sContainerPostRefreshList;
574 sContainerPostRefreshList = nsnull;
577 const nsVoidArray*
578 nsPostRefresh::PostRefreshList()
580 return sPostRefreshList;
583 nsresult
584 nsXFormsModelElement::NeedsPostRefresh(nsIXFormsControl* aControl)
586 if (sRefreshing) {
587 if (!sPostRefreshList) {
588 sPostRefreshList = new nsVoidArray();
589 NS_ENSURE_TRUE(sPostRefreshList, NS_ERROR_OUT_OF_MEMORY);
592 if (sPostRefreshList->IndexOf(aControl) < 0) {
593 sPostRefreshList->AppendElement(aControl);
595 } else {
596 // We are not refreshing any models, so the control
597 // can be refreshed immediately.
598 aControl->Refresh();
600 return NS_OK;
603 PRBool
604 nsXFormsModelElement::ContainerNeedsPostRefresh(nsIXFormsControl* aControl)
607 if (sRefreshing) {
608 if (!sContainerPostRefreshList) {
609 sContainerPostRefreshList = new nsVoidArray();
610 if (!sContainerPostRefreshList) {
611 return PR_FALSE;
615 if (sContainerPostRefreshList->IndexOf(aControl) < 0) {
616 sContainerPostRefreshList->AppendElement(aControl);
619 // return PR_TRUE to show that the control's refresh will be delayed,
620 // whether as a result of this call or a previous call to this function.
621 return PR_TRUE;
624 // Delaying the refresh doesn't make any sense. But since this
625 // function may be called from inside the control node's refresh already,
626 // we shouldn't just assume that we can call the refresh here. So
627 // we'll just return PR_FALSE to signal that we couldn't delay the refresh.
629 return PR_FALSE;
632 void
633 nsXFormsModelElement::CancelPostRefresh(nsIXFormsControl* aControl)
635 if (sPostRefreshList)
636 sPostRefreshList->RemoveElement(aControl);
638 if (sContainerPostRefreshList)
639 sContainerPostRefreshList->RemoveElement(aControl);
642 nsXFormsModelElement::nsXFormsModelElement()
643 : mElement(nsnull),
644 mFormControls(nsnull, &mControlListHash),
645 mSchemaCount(0),
646 mSchemaTotal(0),
647 mPendingInstanceCount(0),
648 mDocumentLoaded(PR_FALSE),
649 mRebindAllControls(PR_FALSE),
650 mInstancesInitialized(PR_FALSE),
651 mReadyHandled(PR_FALSE),
652 mLazyModel(PR_FALSE),
653 mConstructDoneHandled(PR_FALSE),
654 mProcessingUpdateEvent(PR_FALSE),
655 mLoopMax(600),
656 mInstanceDocuments(nsnull)
658 mControlListHash.Init();
661 NS_INTERFACE_MAP_BEGIN(nsXFormsModelElement)
662 NS_INTERFACE_MAP_ENTRY(nsIXFormsModelElement)
663 NS_INTERFACE_MAP_ENTRY(nsIXFormsNSModelElement)
664 NS_INTERFACE_MAP_ENTRY(nsIModelElementPrivate)
665 NS_INTERFACE_MAP_ENTRY(nsISchemaLoadListener)
666 NS_INTERFACE_MAP_ENTRY(nsIWebServiceErrorHandler)
667 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
668 NS_INTERFACE_MAP_ENTRY(nsIXFormsContextControl)
669 NS_INTERFACE_MAP_END_INHERITING(nsXFormsStubElement)
671 NS_IMPL_ADDREF_INHERITED(nsXFormsModelElement, nsXFormsStubElement)
672 NS_IMPL_RELEASE_INHERITED(nsXFormsModelElement, nsXFormsStubElement)
674 NS_IMETHODIMP
675 nsXFormsModelElement::OnDestroyed()
677 mElement = nsnull;
679 // Releasing references if the model element is removed from the
680 // document before unload, see bug 375320.
681 mSchemas = nsnull;
683 if (mInstanceDocuments)
684 mInstanceDocuments->DropReferences();
686 mFormControls.Clear();
687 mControlListHash.Clear();
689 return NS_OK;
692 void
693 nsXFormsModelElement::RemoveModelFromDocument()
695 mDocumentLoaded = PR_FALSE;
697 nsCOMPtr<nsIDOMDocument> domDoc;
698 mElement->GetOwnerDocument(getter_AddRefs(domDoc));
699 if (!domDoc)
700 return;
702 RemoveFromModelList(domDoc, this);
704 nsCOMPtr<nsIDOMEventTarget> targ = do_QueryInterface(domDoc);
705 if (targ) {
706 targ->RemoveEventListener(NS_LITERAL_STRING("DOMContentLoaded"), this, PR_TRUE);
708 nsCOMPtr<nsIDOMWindowInternal> window;
709 nsXFormsUtils::GetWindowFromDocument(domDoc, getter_AddRefs(window));
710 targ = do_QueryInterface(window);
711 if (targ) {
712 targ->RemoveEventListener(NS_LITERAL_STRING("unload"), this, PR_TRUE);
717 NS_IMETHODIMP
718 nsXFormsModelElement::GetScriptingInterfaces(PRUint32 *aCount, nsIID ***aArray)
720 return nsXFormsUtils::CloneScriptingInterfaces(sScriptingIIDs,
721 NS_ARRAY_LENGTH(sScriptingIIDs),
722 aCount, aArray);
725 NS_IMETHODIMP
726 nsXFormsModelElement::WillChangeDocument(nsIDOMDocument* aNewDocument)
728 RemoveModelFromDocument();
729 return NS_OK;
732 NS_IMETHODIMP
733 nsXFormsModelElement::DocumentChanged(nsIDOMDocument* aNewDocument)
735 if (!aNewDocument)
736 return NS_OK;
738 AddToModelList(aNewDocument, this);
740 nsCOMPtr<nsIDOMEventTarget> targ = do_QueryInterface(aNewDocument);
741 if (targ) {
742 targ->AddEventListener(NS_LITERAL_STRING("DOMContentLoaded"), this, PR_TRUE);
744 nsCOMPtr<nsIDOMWindowInternal> window;
745 nsXFormsUtils::GetWindowFromDocument(aNewDocument, getter_AddRefs(window));
746 targ = do_QueryInterface(window);
747 if (targ) {
748 targ->AddEventListener(NS_LITERAL_STRING("unload"), this, PR_TRUE);
752 return NS_OK;
755 NS_IMETHODIMP
756 nsXFormsModelElement::DoneAddingChildren()
758 return InitializeInstances();
761 nsresult
762 nsXFormsModelElement::InitializeInstances()
764 if (mInstancesInitialized || !mElement) {
765 return NS_OK;
768 mInstancesInitialized = PR_TRUE;
770 nsCOMPtr<nsIDOMNodeList> children;
771 mElement->GetChildNodes(getter_AddRefs(children));
773 PRUint32 childCount = 0;
774 if (children) {
775 children->GetLength(&childCount);
778 nsresult rv;
779 for (PRUint32 i = 0; i < childCount; ++i) {
780 nsCOMPtr<nsIDOMNode> child;
781 children->Item(i, getter_AddRefs(child));
782 if (nsXFormsUtils::IsXFormsElement(child, NS_LITERAL_STRING("instance"))) {
783 nsCOMPtr<nsIInstanceElementPrivate> instance(do_QueryInterface(child));
784 NS_ENSURE_STATE(instance);
785 rv = instance->Initialize();
786 NS_ENSURE_SUCCESS(rv, rv);
790 // (XForms 4.2.1)
791 // 1. load xml schemas
793 nsAutoString schemaList;
794 mElement->GetAttribute(NS_LITERAL_STRING("schema"), schemaList);
796 if (!schemaList.IsEmpty()) {
797 NS_ENSURE_TRUE(mSchemas, NS_ERROR_FAILURE);
798 // Parse the whitespace-separated list.
799 nsCOMPtr<nsIContent> content = do_QueryInterface(mElement);
800 nsRefPtr<nsIURI> baseURI = content->GetBaseURI();
801 nsRefPtr<nsIURI> docURI = content->GetOwnerDoc() ?
802 content->GetOwnerDoc()->GetDocumentURI() : nsnull;
804 nsCStringArray schemas;
805 schemas.ParseString(NS_ConvertUTF16toUTF8(schemaList).get(), " \t\r\n");
807 // Increase by 1 to prevent OnLoad from calling FinishConstruction
808 mSchemaTotal = schemas.Count();
810 for (PRInt32 i=0; i<mSchemaTotal; ++i) {
811 rv = NS_OK;
812 nsCOMPtr<nsIURI> newURI;
813 NS_NewURI(getter_AddRefs(newURI), *schemas[i], nsnull, baseURI);
814 nsCOMPtr<nsIURL> newURL = do_QueryInterface(newURI);
815 if (!newURL) {
816 rv = NS_ERROR_UNEXPECTED;
817 } else {
818 // This code is copied from nsXMLEventsManager for extracting an
819 // element ID from an xsd:anyURI link.
820 nsCAutoString ref;
821 newURL->GetRef(ref);
822 newURL->SetRef(EmptyCString());
823 PRBool equals = PR_FALSE;
824 newURL->Equals(docURI, &equals);
825 if (equals) {
826 // We will not be able to locate the <xsd:schema> element using the
827 // getElementById function defined on our document when <xsd:schema>
828 // is treated as an ordinary XML data node. So, we employ XPath to
829 // locate it for us.
831 NS_ConvertUTF8toUTF16 id(ref);
833 nsCOMPtr<nsIDOMElement> el;
834 GetSchemaElementById(mElement, id, getter_AddRefs(el));
835 if (!el) {
836 // Perhaps the <xsd:schema> element appears after the <xforms:model>
837 // element in the document, so we'll defer loading it until the
838 // document has finished loading.
839 mPendingInlineSchemas.AppendString(id);
840 } else {
841 // We have an inline schema in the model element that was
842 // referenced by the schema attribute. It will be processed
843 // in FinishConstruction so we skip it now to avoid processing
844 // it twice and giving invalid 'duplicate schema' errors.
845 mSchemaTotal--;
846 i--;
848 } else {
849 nsCAutoString uriSpec;
850 newURI->GetSpec(uriSpec);
851 rv = mSchemas->LoadAsync(NS_ConvertUTF8toUTF16(uriSpec), this);
854 if (NS_FAILED(rv)) {
855 // this is a fatal error
856 nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement);
857 nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException);
858 return NS_OK;
863 // If all of the children are added and there aren't any instance elements,
864 // yet, then we need to make sure that one is ready in case the form author
865 // is using lazy authoring.
866 // Lazy <xforms:intance> element is created in anonymous content using XBL.
867 NS_ENSURE_STATE(mInstanceDocuments);
868 PRUint32 instCount;
869 mInstanceDocuments->GetLength(&instCount);
870 if (!instCount) {
871 #ifdef DEBUG
872 printf("Creating lazy instance\n");
873 #endif
874 nsCOMPtr<nsIDOMDocument> domDoc;
875 mElement->GetOwnerDocument(getter_AddRefs(domDoc));
876 nsCOMPtr<nsIDOMDocumentXBL> xblDoc(do_QueryInterface(domDoc));
877 if (xblDoc) {
878 nsresult rv =
879 xblDoc->AddBinding(mElement,
880 NS_LITERAL_STRING(XFORMS_LAZY_INSTANCE_BINDING));
881 NS_ENSURE_SUCCESS(rv, rv);
883 mInstanceDocuments->GetLength(&instCount);
885 nsCOMPtr<nsIDOMNodeList> list;
886 xblDoc->GetAnonymousNodes(mElement, getter_AddRefs(list));
887 if (list) {
888 PRUint32 childCount = 0;
889 if (list) {
890 list->GetLength(&childCount);
893 for (PRUint32 i = 0; i < childCount; ++i) {
894 nsCOMPtr<nsIDOMNode> item;
895 list->Item(i, getter_AddRefs(item));
896 nsCOMPtr<nsIInstanceElementPrivate> instance =
897 do_QueryInterface(item);
898 if (instance) {
899 rv = instance->Initialize();
900 NS_ENSURE_SUCCESS(rv, rv);
902 mLazyModel = PR_TRUE;
903 break;
908 NS_WARN_IF_FALSE(mLazyModel, "Installing lazy instance didn't succeed!");
911 // (XForms 4.2.1 - cont)
912 // 2. construct an XPath data model from inline or external initial instance
913 // data. This is done by our child instance elements as they are inserted
914 // into the document, and all of the instances will be processed by this
915 // point.
917 // schema and external instance data loads should delay document onload
919 if (IsComplete()) {
920 // No need to fire refresh event if we assume that all UI controls
921 // appear later in the document.
922 NS_ASSERTION(!mDocumentLoaded, "document should not be loaded yet");
923 return FinishConstruction();
926 return NS_OK;
929 NS_IMETHODIMP
930 nsXFormsModelElement::HandleDefault(nsIDOMEvent *aEvent, PRBool *aHandled)
932 if (!nsXFormsUtils::EventHandlingAllowed(aEvent, mElement))
933 return NS_OK;
935 *aHandled = PR_TRUE;
937 nsAutoString type;
938 aEvent->GetType(type);
939 nsresult rv = NS_OK;
941 if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Refresh].name)) {
942 rv = Refresh();
943 } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Revalidate].name)) {
944 rv = Revalidate();
945 } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Recalculate].name)) {
946 rv = Recalculate();
947 } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Rebuild].name)) {
948 rv = Rebuild();
949 } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_ModelConstructDone].name)) {
950 rv = ConstructDone();
951 mConstructDoneHandled = PR_TRUE;
952 } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Reset].name)) {
953 Reset();
954 } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_BindingException].name)) {
955 // we threw up a popup during the nsXFormsUtils::DispatchEvent that sent
956 // this error to the model
957 *aHandled = PR_TRUE;
958 } else {
959 *aHandled = PR_FALSE;
962 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
963 "nsXFormsModelElement::HandleDefault() failed!\n");
965 return rv;
968 nsresult
969 nsXFormsModelElement::ConstructDone()
971 nsresult rv = InitializeControls();
972 NS_ENSURE_SUCCESS(rv, rv);
974 return NS_OK;
977 NS_IMETHODIMP
978 nsXFormsModelElement::OnCreated(nsIXTFElementWrapper *aWrapper)
980 aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_WILL_CHANGE_DOCUMENT |
981 nsIXTFElement::NOTIFY_DOCUMENT_CHANGED |
982 nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN |
983 nsIXTFElement::NOTIFY_HANDLE_DEFAULT);
985 nsCOMPtr<nsIDOMElement> node;
986 aWrapper->GetElementNode(getter_AddRefs(node));
988 // It's ok to keep a weak pointer to mElement. mElement will have an
989 // owning reference to this object, so as long as we null out mElement in
990 // OnDestroyed, it will always be valid.
992 mElement = node;
993 NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon");
995 nsresult rv = mMDG.Init(this);
996 NS_ENSURE_SUCCESS(rv, rv);
998 mSchemas = do_CreateInstance(NS_SCHEMALOADER_CONTRACTID);
1000 mInstanceDocuments = new nsXFormsModelInstanceDocuments();
1001 NS_ASSERTION(mInstanceDocuments, "could not create mInstanceDocuments?!");
1003 // Initialize hash tables
1004 NS_ENSURE_TRUE(mNodeToType.Init(), NS_ERROR_OUT_OF_MEMORY);
1005 NS_ENSURE_TRUE(mNodeToP3PType.Init(), NS_ERROR_OUT_OF_MEMORY);
1008 // Get eventual user-set loop maximum. Used by RequestUpdateEvent().
1009 nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
1010 if (NS_SUCCEEDED(rv) && pref) {
1011 PRInt32 val;
1012 if (NS_SUCCEEDED(pref->GetIntPref("xforms.modelLoopMax", &val)))
1013 mLoopMax = val;
1016 return NS_OK;
1019 // nsIXFormsModelElement
1021 NS_IMETHODIMP
1022 nsXFormsModelElement::GetInstanceDocuments(nsIDOMNodeList **aDocuments)
1024 NS_ENSURE_STATE(mInstanceDocuments);
1025 NS_ENSURE_ARG_POINTER(aDocuments);
1026 NS_ADDREF(*aDocuments = mInstanceDocuments);
1027 return NS_OK;
1030 NS_IMETHODIMP
1031 nsXFormsModelElement::GetInstanceDocument(const nsAString& aInstanceID,
1032 nsIDOMDocument **aDocument)
1034 NS_ENSURE_ARG_POINTER(aDocument);
1036 *aDocument = FindInstanceDocument(aInstanceID).get(); // transfer reference
1038 if (*aDocument) {
1039 return NS_OK;
1042 const nsPromiseFlatString& flat = PromiseFlatString(aInstanceID);
1043 const PRUnichar *strings[] = { flat.get() };
1044 nsXFormsUtils::ReportError(aInstanceID.IsEmpty() ?
1045 NS_LITERAL_STRING("defInstanceNotFound") :
1046 NS_LITERAL_STRING("instanceNotFound"),
1047 strings, 1, mElement, nsnull);
1048 return NS_ERROR_DOM_NOT_FOUND_ERR;
1051 NS_IMETHODIMP
1052 nsXFormsModelElement::Rebuild()
1054 #ifdef DEBUG
1055 printf("nsXFormsModelElement::Rebuild()\n");
1056 #endif
1058 // 1 . Clear graph
1059 nsresult rv;
1060 rv = mMDG.Clear();
1061 NS_ENSURE_SUCCESS(rv, rv);
1063 // Clear any type information
1064 NS_ENSURE_TRUE(mNodeToType.IsInitialized() && mNodeToP3PType.IsInitialized(),
1065 NS_ERROR_FAILURE);
1066 mNodeToType.Clear();
1067 mNodeToP3PType.Clear();
1069 // 2. Process bind elements
1070 rv = ProcessBindElements();
1071 NS_ENSURE_SUCCESS(rv, rv);
1073 // 3. If this is not form load, re-attach all elements and validate
1074 // instance documents
1075 if (mReadyHandled) {
1076 mRebindAllControls = PR_TRUE;
1077 ValidateInstanceDocuments();
1080 // 4. Rebuild graph
1081 return mMDG.Rebuild();
1084 NS_IMETHODIMP
1085 nsXFormsModelElement::Recalculate()
1087 #ifdef DEBUG
1088 printf("nsXFormsModelElement::Recalculate()\n");
1089 #endif
1091 return mMDG.Recalculate(&mChangedNodes);
1094 void
1095 nsXFormsModelElement::SetSingleState(nsIDOMElement *aElement,
1096 PRBool aState,
1097 nsXFormsEvent aOnEvent)
1099 nsXFormsEvent event = aState ? aOnEvent : (nsXFormsEvent) (aOnEvent + 1);
1101 // Dispatch event
1102 nsXFormsUtils::DispatchEvent(aElement, event);
1105 NS_IMETHODIMP
1106 nsXFormsModelElement::SetStates(nsIXFormsControl *aControl,
1107 nsIDOMNode *aNode)
1109 NS_ENSURE_ARG(aControl);
1111 nsCOMPtr<nsIDOMElement> element;
1112 aControl->GetElement(getter_AddRefs(element));
1113 NS_ENSURE_STATE(element);
1115 nsCOMPtr<nsIXTFElementWrapper> xtfWrap(do_QueryInterface(element));
1116 NS_ENSURE_STATE(xtfWrap);
1118 PRInt32 iState;
1119 const nsXFormsNodeState* ns = nsnull;
1120 if (aNode) {
1121 ns = mMDG.GetNodeState(aNode);
1122 NS_ENSURE_STATE(ns);
1123 iState = ns->GetIntrinsicState();
1124 nsCOMPtr<nsIContent> content(do_QueryInterface(element));
1125 NS_ENSURE_STATE(content);
1126 PRInt32 rangeState = content->IntrinsicState() &
1127 (NS_EVENT_STATE_INRANGE | NS_EVENT_STATE_OUTOFRANGE);
1128 iState = ns->GetIntrinsicState() | rangeState;
1129 } else {
1130 aControl->GetDefaultIntrinsicState(&iState);
1133 nsresult rv = xtfWrap->SetIntrinsicState(iState);
1134 NS_ENSURE_SUCCESS(rv, rv);
1136 // Event dispatching is defined by the bound node, so if there's no bound
1137 // node, there are no events to send. xforms-ready also needs to be handled,
1138 // because these events are not sent before that.
1139 if (!ns || !mReadyHandled)
1140 return NS_OK;
1142 if (ns->ShouldDispatchValid()) {
1143 SetSingleState(element, ns->IsValid(), eEvent_Valid);
1145 if (ns->ShouldDispatchReadonly()) {
1146 SetSingleState(element, ns->IsReadonly(), eEvent_Readonly);
1148 if (ns->ShouldDispatchRequired()) {
1149 SetSingleState(element, ns->IsRequired(), eEvent_Required);
1151 if (ns->ShouldDispatchRelevant()) {
1152 SetSingleState(element, ns->IsRelevant(), eEvent_Enabled);
1155 if (ns->ShouldDispatchValueChanged()) {
1156 nsXFormsUtils::DispatchEvent(element, eEvent_ValueChanged);
1159 return NS_OK;
1162 NS_IMETHODIMP
1163 nsXFormsModelElement::Revalidate()
1165 #ifdef DEBUG
1166 printf("nsXFormsModelElement::Revalidate()\n");
1167 #endif
1169 #ifdef DEBUG_MODEL
1170 printf("[%s] Changed nodes:\n", __TIME__);
1171 for (PRInt32 j = 0; j < mChangedNodes.Count(); ++j) {
1172 nsCOMPtr<nsIDOMNode> node = mChangedNodes[j];
1173 nsAutoString name;
1174 node->GetNodeName(name);
1175 printf("\t%s [%p]\n",
1176 NS_ConvertUTF16toUTF8(name).get(),
1177 (void*) node);
1179 #endif
1181 // Revalidate nodes
1182 mMDG.Revalidate(&mChangedNodes);
1184 return NS_OK;
1187 nsresult
1188 nsXFormsModelElement::RefreshSubTree(nsXFormsControlListItem *aCurrent,
1189 PRBool aForceRebind)
1191 nsresult rv;
1192 nsRefPtr<nsXFormsControlListItem> current = aCurrent;
1193 while (current) {
1194 nsCOMPtr<nsIXFormsControl> control(current->Control());
1195 NS_ASSERTION(control, "A tree node without a control?!");
1197 // Get bound node
1198 nsCOMPtr<nsIDOMNode> boundNode;
1199 control->GetBoundNode(getter_AddRefs(boundNode));
1201 PRBool rebind = aForceRebind;
1202 PRBool refresh = PR_FALSE;
1203 PRBool rebindChildren = PR_FALSE;
1205 #ifdef DEBUG_MODEL
1206 nsCOMPtr<nsIDOMElement> controlElement;
1207 control->GetElement(getter_AddRefs(controlElement));
1208 printf("rebind: %d, mRebindAllControls: %d, aForceRebind: %d\n",
1209 rebind, mRebindAllControls, aForceRebind);
1210 if (controlElement) {
1211 printf("Checking control: ");
1212 //DBG_TAGINFO(controlElement);
1214 #endif
1216 if (mRebindAllControls || rebind) {
1217 refresh = rebind = PR_TRUE;
1218 } else {
1219 PRBool usesModelBinding = PR_FALSE;
1220 control->GetUsesModelBinding(&usesModelBinding);
1222 #ifdef DEBUG_MODEL
1223 printf("usesModelBinding: %d\n", usesModelBinding);
1224 #endif
1226 nsCOMArray<nsIDOMNode> *deps = nsnull;
1227 if (usesModelBinding) {
1228 if (!boundNode) {
1229 PRBool usesSNB = PR_TRUE;
1230 control->GetUsesSingleNodeBinding(&usesSNB);
1232 // If the control doesn't use single node binding (and can thus be
1233 // bound to many nodes), the above test for boundNode means nothing.
1234 // We'll need to continue on with the work this function does so that
1235 // any controls that this control contains can be tested for whether
1236 // they may need to refresh.
1237 if (usesSNB) {
1238 // If a control uses a model binding, but has no bound node a
1239 // rebuild is the only thing that'll (eventually) change it. We
1240 // don't need to worry about contained controls (like a label)
1241 // since the fact that there is no bound node means that this
1242 // control (and contained controls) need to behave as if
1243 // irrelevant per spec.
1244 current = current->NextSibling();
1245 continue;
1248 } else {
1249 // Get dependencies
1250 control->GetDependencies(&deps);
1252 PRUint32 depCount = deps ? deps->Count() : 0;
1254 #ifdef DEBUG_MODEL
1255 nsAutoString boundName;
1256 if (boundNode)
1257 boundNode->GetNodeName(boundName);
1258 printf("\tDependencies: %d, Bound to: '%s' [%p]\n",
1259 depCount,
1260 NS_ConvertUTF16toUTF8(boundName).get(),
1261 (void*) boundNode);
1263 nsAutoString depNodeName;
1264 for (PRUint32 t = 0; t < depCount; ++t) {
1265 nsCOMPtr<nsIDOMNode> tmpdep = deps->ObjectAt(t);
1266 if (tmpdep) {
1267 tmpdep->GetNodeName(depNodeName);
1268 printf("\t\t%s [%p]\n",
1269 NS_ConvertUTF16toUTF8(depNodeName).get(),
1270 (void*) tmpdep);
1273 #endif
1275 nsCOMPtr<nsIDOM3Node> curChanged;
1277 // Iterator over changed nodes. Checking for rebind, too. If it ever
1278 // becomes true due to some condition below, we can stop this testing
1279 // since any control that needs to rebind will also refresh.
1280 for (PRInt32 j = 0; j < mChangedNodes.Count() && !rebind; ++j) {
1281 curChanged = do_QueryInterface(mChangedNodes[j]);
1283 // Check whether the bound node is dirty. If so, we need to refresh the
1284 // control (get updated node value from the bound node)
1285 if (!refresh && boundNode) {
1286 curChanged->IsSameNode(boundNode, &refresh);
1288 // Two ways to go here. Keep in mind that controls using model
1289 // binding expressions never needs to have dependencies checked as
1290 // they only rebind on xforms-rebuild
1291 if (refresh && usesModelBinding) {
1292 // 1) If the control needs a refresh, and uses model bindings,
1293 // we can stop checking here
1294 break;
1296 if (refresh || usesModelBinding) {
1297 // 2) If either the control needs a refresh or it uses a model
1298 // binding we can continue to next changed node
1299 continue;
1303 // Check whether any dependencies are dirty. If so, we need to rebind
1304 // the control (re-evaluate it's binding expression)
1305 for (PRUint32 k = 0; k < depCount; ++k) {
1306 /// @note beaufour: I'm not too happy about this ...
1307 /// O(mChangedNodes.Count() * deps->Count()), but using the pointers
1308 /// for sorting and comparing does not work...
1309 curChanged->IsSameNode(deps->ObjectAt(k), &rebind);
1310 if (rebind)
1311 // We need to rebind the control, no need to check any more
1312 break;
1315 #ifdef DEBUG_MODEL
1316 printf("\trebind: %d, refresh: %d\n", rebind, refresh);
1317 #endif
1320 // Handle rebinding
1321 if (rebind) {
1322 rv = control->Bind(&rebindChildren);
1323 NS_ENSURE_SUCCESS(rv, rv);
1326 // Handle refreshing
1327 if (rebind || refresh) {
1328 control->Refresh();
1329 // XXX: bug 336608: we should really check the return result, but
1330 // f.x. select1 returns error because of no widget...? so we should
1331 // ensure that an error is only returned when there actually is an
1332 // error, and we should report that on the console... possibly we should
1333 // then continue, instead of bailing totally.
1334 // NS_ENSURE_SUCCESS(rv, rv);
1337 // Refresh children
1338 rv = RefreshSubTree(current->FirstChild(), rebindChildren);
1339 NS_ENSURE_SUCCESS(rv, rv);
1341 current = current->NextSibling();
1344 return NS_OK;
1348 NS_IMETHODIMP
1349 nsXFormsModelElement::Refresh()
1351 #ifdef DEBUG
1352 printf("nsXFormsModelElement::Refresh()\n");
1353 #endif
1355 // XXXbeaufour: Can we somehow suspend redraw / "screen update" while doing
1356 // the refresh? That should save a lot of time, and avoid flickering of
1357 // controls.
1359 // Using brackets here to provide a scope for the
1360 // nsPostRefresh. We want to make sure that nsPostRefresh's destructor
1361 // runs (and thus processes the postrefresh and containerpostrefresh lists)
1362 // before we clear the dispatch flags
1364 nsPostRefresh postRefresh = nsPostRefresh();
1366 if (!mDocumentLoaded) {
1367 return NS_OK;
1370 // Kick off refreshing on root node
1371 nsresult rv = RefreshSubTree(mFormControls.FirstChild(), PR_FALSE);
1372 NS_ENSURE_SUCCESS(rv, rv);
1375 // Clear refresh structures
1376 mChangedNodes.Clear();
1377 mRebindAllControls = PR_FALSE;
1378 mMDG.ClearDispatchFlags();
1380 return NS_OK;
1383 // nsISchemaLoadListener
1385 NS_IMETHODIMP
1386 nsXFormsModelElement::OnLoad(nsISchema* aSchema)
1388 mSchemaCount++;
1390 // If there is no model element, then schema loading finished after
1391 // main page failed to load.
1392 if (IsComplete() && mElement) {
1393 nsresult rv = FinishConstruction();
1394 NS_ENSURE_SUCCESS(rv, rv);
1396 MaybeNotifyCompletion();
1399 return NS_OK;
1402 // nsIWebServiceErrorHandler
1404 NS_IMETHODIMP
1405 nsXFormsModelElement::OnError(nsresult aStatus,
1406 const nsAString &aStatusMessage)
1408 nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement);
1409 nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException);
1410 return NS_OK;
1413 // nsIDOMEventListener
1415 NS_IMETHODIMP
1416 nsXFormsModelElement::HandleEvent(nsIDOMEvent* aEvent)
1418 if (!nsXFormsUtils::EventHandlingAllowed(aEvent, mElement))
1419 return NS_OK;
1421 nsAutoString type;
1422 aEvent->GetType(type);
1424 if (type.EqualsLiteral("DOMContentLoaded")) {
1425 return HandleLoad(aEvent);
1426 }else if (type.EqualsLiteral("unload")) {
1427 return HandleUnload(aEvent);
1430 return NS_OK;
1433 // nsIModelElementPrivate
1435 NS_IMETHODIMP
1436 nsXFormsModelElement::AddFormControl(nsIXFormsControl *aControl,
1437 nsIXFormsControl *aParent)
1439 #ifdef DEBUG_MODEL
1440 printf("nsXFormsModelElement::AddFormControl(con: %p, parent: %p)\n",
1441 (void*) aControl, (void*) aParent);
1442 #endif
1444 NS_ENSURE_ARG(aControl);
1445 return mFormControls.AddControl(aControl, aParent);
1448 NS_IMETHODIMP
1449 nsXFormsModelElement::RemoveFormControl(nsIXFormsControl *aControl)
1451 #ifdef DEBUG_MODEL
1452 printf("nsXFormsModelElement::RemoveFormControl(con: %p)\n",
1453 (void*) aControl);
1454 #endif
1456 NS_ENSURE_ARG(aControl);
1457 PRBool removed;
1458 nsresult rv = mFormControls.RemoveControl(aControl, removed);
1459 NS_WARN_IF_FALSE(removed,
1460 "Tried to remove control that was not in the model");
1461 return rv;
1464 NS_IMETHODIMP
1465 nsXFormsModelElement::GetTypeForControl(nsIXFormsControl *aControl,
1466 nsISchemaType **aType)
1468 NS_ENSURE_ARG_POINTER(aType);
1469 *aType = nsnull;
1471 nsCOMPtr<nsIDOMNode> boundNode;
1472 aControl->GetBoundNode(getter_AddRefs(boundNode));
1473 if (!boundNode) {
1474 // if the control isn't bound to instance data, it doesn't make sense to
1475 // return a type. It is perfectly valid for there to be no bound node,
1476 // so no need to use an NS_ENSURE_xxx macro, either.
1477 return NS_ERROR_FAILURE;
1480 nsAutoString schemaTypeName, schemaTypeNamespace;
1481 nsresult rv = GetTypeAndNSFromNode(boundNode, schemaTypeName,
1482 schemaTypeNamespace);
1483 NS_ENSURE_SUCCESS(rv, rv);
1485 nsXFormsSchemaValidator validator;
1487 nsCOMPtr<nsISchemaCollection> schemaColl = do_QueryInterface(mSchemas);
1488 if (schemaColl) {
1489 nsCOMPtr<nsISchema> schema;
1490 schemaColl->GetSchema(schemaTypeNamespace, getter_AddRefs(schema));
1491 // if no schema found, then we will only handle built-in types.
1492 if (schema)
1493 validator.LoadSchema(schema);
1496 PRBool foundType = validator.GetType(schemaTypeName, schemaTypeNamespace,
1497 aType);
1498 return foundType ? NS_OK : NS_ERROR_FAILURE;
1501 /* static */ nsresult
1502 nsXFormsModelElement::GetTypeAndNSFromNode(nsIDOMNode *aInstanceData,
1503 nsAString &aType, nsAString &aNSUri)
1505 nsresult rv = GetTypeFromNode(aInstanceData, aType, aNSUri);
1507 if (rv == NS_ERROR_NOT_AVAILABLE) {
1508 // if there is no type assigned, then assume that the type is 'string'
1509 aNSUri.Assign(NS_LITERAL_STRING(NS_NAMESPACE_XML_SCHEMA));
1510 aType.Assign(NS_LITERAL_STRING("string"));
1511 rv = NS_OK;
1514 return rv;
1517 NS_IMETHODIMP
1518 nsXFormsModelElement::InstanceLoadStarted()
1520 ++mPendingInstanceCount;
1521 return NS_OK;
1524 NS_IMETHODIMP
1525 nsXFormsModelElement::InstanceLoadFinished(PRBool aSuccess)
1527 if (!aSuccess) {
1528 // This will leave mPendingInstanceCount in an invalid state, which is
1529 // exactly what we want, because this is a fatal error, and processing
1530 // should stop. If we decrease mPendingInstanceCount, the model would
1531 // finish construction, which is wrong.
1532 nsXFormsUtils::ReportError(NS_LITERAL_STRING("instanceLoadError"), mElement);
1533 nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException);
1534 return NS_OK;
1537 --mPendingInstanceCount;
1538 if (IsComplete()) {
1539 nsresult rv = FinishConstruction();
1540 if (NS_SUCCEEDED(rv)) {
1541 MaybeNotifyCompletion();
1545 return NS_OK;
1548 NS_IMETHODIMP
1549 nsXFormsModelElement::FindInstanceElement(const nsAString &aID,
1550 nsIInstanceElementPrivate **aElement)
1552 NS_ENSURE_STATE(mInstanceDocuments);
1553 *aElement = nsnull;
1555 PRUint32 instCount;
1556 mInstanceDocuments->GetLength(&instCount);
1557 if (instCount) {
1558 nsCOMPtr<nsIDOMElement> element;
1559 nsAutoString id;
1560 for (PRUint32 i = 0; i < instCount; ++i) {
1561 nsIInstanceElementPrivate* instEle = mInstanceDocuments->GetInstanceAt(i);
1562 instEle->GetElement(getter_AddRefs(element));
1564 if (aID.IsEmpty()) {
1565 NS_ADDREF(instEle);
1566 *aElement = instEle;
1567 break;
1568 } else if (!element) {
1569 // this should only happen if the instance on the list is lazy authored
1570 // and as far as I can tell, a lazy authored instance should be the
1571 // first (and only) instance in the model and unable to have an ID.
1572 // But that isn't clear to me reading the spec, so for now
1573 // we'll play it safe in case the WG more clearly defines lazy authoring
1574 // in the future.
1575 continue;
1578 element->GetAttribute(NS_LITERAL_STRING("id"), id);
1579 if (aID.Equals(id)) {
1580 NS_ADDREF(instEle);
1581 *aElement = instEle;
1582 break;
1587 return NS_OK;
1590 NS_IMETHODIMP
1591 nsXFormsModelElement::SetNodeValue(nsIDOMNode *aNode,
1592 const nsAString &aNodeValue,
1593 PRBool aDoRefresh,
1594 PRBool *aNodeChanged)
1596 NS_ENSURE_ARG_POINTER(aNodeChanged);
1597 nsresult rv = mMDG.SetNodeValue(aNode, aNodeValue, aNodeChanged);
1598 NS_ENSURE_SUCCESS(rv, rv);
1599 if (*aNodeChanged && aDoRefresh) {
1600 rv = RequestRecalculate();
1601 NS_ENSURE_SUCCESS(rv, rv);
1602 rv = RequestRevalidate();
1603 NS_ENSURE_SUCCESS(rv, rv);
1604 rv = RequestRefresh();
1605 NS_ENSURE_SUCCESS(rv, rv);
1608 return NS_OK;
1611 NS_IMETHODIMP
1612 nsXFormsModelElement::SetNodeContent(nsIDOMNode *aNode,
1613 nsIDOMNode *aNodeContent,
1614 PRBool aDoRebuild)
1616 nsresult rv = mMDG.SetNodeContent(aNode, aNodeContent);
1617 NS_ENSURE_SUCCESS(rv, rv);
1619 if (aDoRebuild) {
1620 rv = RequestRebuild();
1621 NS_ENSURE_SUCCESS(rv, rv);
1622 rv = RequestRecalculate();
1623 NS_ENSURE_SUCCESS(rv, rv);
1624 rv = RequestRevalidate();
1625 NS_ENSURE_SUCCESS(rv, rv);
1626 rv = RequestRefresh();
1627 NS_ENSURE_SUCCESS(rv, rv);
1630 return NS_OK;
1633 NS_IMETHODIMP
1634 nsXFormsModelElement::ValidateNode(nsIDOMNode *aInstanceNode, PRBool *aResult)
1636 NS_ENSURE_ARG_POINTER(aResult);
1638 nsAutoString schemaTypeName, schemaTypeNamespace;
1639 nsresult rv = GetTypeAndNSFromNode(aInstanceNode, schemaTypeName,
1640 schemaTypeNamespace);
1641 NS_ENSURE_SUCCESS(rv, rv);
1643 nsXFormsSchemaValidator validator;
1644 nsCOMPtr<nsISchemaCollection> schemaColl = do_QueryInterface(mSchemas);
1645 if (schemaColl) {
1646 nsCOMPtr<nsISchema> schema;
1647 schemaColl->GetSchema(schemaTypeNamespace, getter_AddRefs(schema));
1648 // if no schema found, then we will only handle built-in types.
1649 if (schema)
1650 validator.LoadSchema(schema);
1653 nsCOMPtr<nsISchemaType> type;
1654 rv = validator.GetType(schemaTypeName, schemaTypeNamespace,
1655 getter_AddRefs(type));
1656 NS_ENSURE_SUCCESS(rv, rv);
1658 PRUint16 typevalue = nsISchemaType::SCHEMA_TYPE_SIMPLE;
1659 if (type) {
1660 rv = type->GetSchemaType(&typevalue);
1661 NS_ENSURE_SUCCESS(rv, rv);
1664 PRBool isValid = PR_FALSE;
1665 if (typevalue == nsISchemaType::SCHEMA_TYPE_SIMPLE) {
1666 nsAutoString value;
1667 nsXFormsUtils::GetNodeValue(aInstanceNode, value);
1668 isValid = validator.ValidateString(value, schemaTypeName,
1669 schemaTypeNamespace);
1670 } else {
1671 isValid = validator.Validate(aInstanceNode);
1673 *aResult = isValid;
1674 return NS_OK;
1677 nsresult
1678 nsXFormsModelElement::ValidateDocument(nsIDOMDocument *aInstanceDocument,
1679 PRBool *aResult)
1681 NS_ENSURE_ARG_POINTER(aResult);
1682 NS_ENSURE_ARG(aInstanceDocument);
1685 This will process the instance document and check for schema validity. It
1686 will mark nodes in the document with their schema types using nsIProperty
1687 until it hits a structural schema validation error. So if the instance
1688 document's XML structure is invalid, don't expect type properties to be
1689 set.
1691 Note that if the structure is fine but some simple types nodes (nodes
1692 that contain text only) are invalid (say one has a empty nodeValue but
1693 should be a date), the schema validator will continue processing and add
1694 the type properties. Schema validation will return false at the end.
1697 nsCOMPtr<nsIDOMElement> element;
1698 nsresult rv = aInstanceDocument->GetDocumentElement(getter_AddRefs(element));
1699 NS_ENSURE_SUCCESS(rv, rv);
1700 NS_ENSURE_STATE(element);
1702 // get namespace from node
1703 nsAutoString nsuri;
1704 element->GetNamespaceURI(nsuri);
1706 nsCOMPtr<nsISchemaCollection> schemaColl = do_QueryInterface(mSchemas);
1707 NS_ENSURE_STATE(schemaColl);
1709 nsCOMPtr<nsISchema> schema;
1710 schemaColl->GetSchema(nsuri, getter_AddRefs(schema));
1711 if (!schema) {
1712 // No schema found, so nothing to validate
1713 *aResult = PR_TRUE;
1714 return NS_OK;
1717 nsXFormsSchemaValidator validator;
1718 validator.LoadSchema(schema);
1719 // Validate will validate the node and its subtree, as per the schema
1720 // specification.
1721 *aResult = validator.Validate(element);
1723 return NS_OK;
1727 * SUBMIT_SERIALIZE_NODE - node is to be serialized
1728 * SUBMIT_SKIP_NODE - node is not to be serialized
1729 * SUBMIT_ABORT_SUBMISSION - abort submission (invalid node or empty required node)
1731 NS_IMETHODIMP
1732 nsXFormsModelElement::HandleInstanceDataNode(nsIDOMNode *aInstanceDataNode,
1733 unsigned short *aResult)
1735 // abort by default
1736 *aResult = SUBMIT_ABORT_SUBMISSION;
1738 const nsXFormsNodeState* ns;
1739 ns = mMDG.GetNodeState(aInstanceDataNode);
1740 NS_ENSURE_STATE(ns);
1742 if (!ns->IsRelevant()) {
1743 // not relevant, thus skip
1744 *aResult = SUBMIT_SKIP_NODE;
1745 } else if (ns->IsRequired()) {
1746 // required and has a value, continue
1747 nsAutoString value;
1748 nsXFormsUtils::GetNodeValue(aInstanceDataNode, value);
1749 if (!value.IsEmpty() && ns->IsValid())
1750 *aResult = SUBMIT_SERIALIZE_NODE;
1751 } else if (ns->IsValid()) {
1752 // valid
1753 *aResult = SUBMIT_SERIALIZE_NODE;
1756 return NS_OK;
1759 NS_IMETHODIMP
1760 nsXFormsModelElement::GetLazyAuthored(PRBool *aLazyInstance)
1762 *aLazyInstance = mLazyModel;
1763 return NS_OK;
1766 NS_IMETHODIMP
1767 nsXFormsModelElement::GetIsReady(PRBool *aIsReady)
1769 *aIsReady = mReadyHandled;
1770 return NS_OK;
1773 NS_IMETHODIMP
1774 nsXFormsModelElement::GetTypeFromNode(nsIDOMNode *aInstanceData,
1775 nsAString &aType,
1776 nsAString &aNSUri)
1778 // aInstanceData could be an instance data node or it could be an attribute
1779 // on an instance data node (basically the node that a control is bound to).
1781 nsString *typeVal = nsnull;
1783 // Get type stored directly on instance node
1784 nsAutoString typeAttribute;
1785 nsCOMPtr<nsIDOMElement> nodeElem(do_QueryInterface(aInstanceData));
1786 if (nodeElem) {
1787 nodeElem->GetAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XML_SCHEMA_INSTANCE),
1788 NS_LITERAL_STRING("type"), typeAttribute);
1789 if (!typeAttribute.IsEmpty()) {
1790 typeVal = &typeAttribute;
1794 // If there was no type information on the node itself, check for a type
1795 // bound to the node via \<xforms:bind\>
1796 if (!typeVal && !mNodeToType.Get(aInstanceData, &typeVal)) {
1797 // check if schema validation left us a nsISchemaType*
1798 nsCOMPtr<nsIContent> content = do_QueryInterface(aInstanceData);
1800 if (content) {
1801 nsISchemaType *type;
1802 nsCOMPtr<nsIAtom> myAtom = do_GetAtom("xsdtype");
1804 type = static_cast<nsISchemaType *>(content->GetProperty(myAtom));
1805 if (type) {
1806 type->GetName(aType);
1807 type->GetTargetNamespace(aNSUri);
1808 return NS_OK;
1812 // No type information found
1813 return NS_ERROR_NOT_AVAILABLE;
1816 // split type (ns:type) into namespace and type.
1817 nsAutoString prefix;
1818 PRInt32 separator = typeVal->FindChar(':');
1819 if ((PRUint32) separator == (typeVal->Length() - 1)) {
1820 const PRUnichar *strings[] = { typeVal->get() };
1821 nsXFormsUtils::ReportError(NS_LITERAL_STRING("missingTypeName"), strings, 1,
1822 mElement, nsnull);
1823 return NS_ERROR_UNEXPECTED;
1826 if (separator == kNotFound) {
1827 // no namespace prefix, which is valid. In this case we should follow
1828 // http://www.w3.org/TR/2004/REC-xmlschema-1-20041028/#src-qname and pick
1829 // up the default namespace. Which will happen by passing an empty string
1830 // as first parameter to LookupNamespaceURI.
1831 prefix = EmptyString();
1832 aType.Assign(*typeVal);
1833 } else {
1834 prefix.Assign(Substring(*typeVal, 0, separator));
1835 aType.Assign(Substring(*typeVal, ++separator, typeVal->Length()));
1837 if (prefix.IsEmpty()) {
1838 aNSUri = EmptyString();
1839 return NS_OK;
1843 // get the namespace url from the prefix using instance data node
1844 nsresult rv;
1845 nsCOMPtr<nsIDOM3Node> domNode3 = do_QueryInterface(aInstanceData, &rv);
1846 NS_ENSURE_SUCCESS(rv, rv);
1847 rv = domNode3->LookupNamespaceURI(prefix, aNSUri);
1849 if (DOMStringIsNull(aNSUri)) {
1850 // if not found using instance data node, use <xf:instance> node
1851 nsCOMPtr<nsIDOMNode> instanceNode;
1852 rv = nsXFormsUtils::GetInstanceNodeForData(aInstanceData,
1853 getter_AddRefs(instanceNode));
1854 NS_ENSURE_SUCCESS(rv, rv);
1856 domNode3 = do_QueryInterface(instanceNode, &rv);
1857 NS_ENSURE_SUCCESS(rv, rv);
1858 rv = domNode3->LookupNamespaceURI(prefix, aNSUri);
1861 return rv;
1865 * Poor man's try-catch to make sure that we set mProcessingUpdateEvent to
1866 * when leaving scope. If we actually bail with an error at some time,
1867 * something is pretty rotten, but at least we will not prevent any further
1868 * updates.
1870 class Updating {
1871 private:
1872 nsXFormsModelElement* mModel;
1874 public:
1875 Updating(nsXFormsModelElement* aModel)
1876 : mModel(aModel) { mModel->mProcessingUpdateEvent = PR_TRUE; };
1877 ~Updating() { mModel->mProcessingUpdateEvent = PR_FALSE; };
1880 nsresult
1881 nsXFormsModelElement::RequestUpdateEvent(nsXFormsEvent aEvent)
1883 if (mProcessingUpdateEvent) {
1884 mUpdateEventQueue.AppendElement(NS_INT32_TO_PTR(aEvent));
1885 return NS_OK;
1888 Updating upd(this);
1890 // Send the requested event
1891 nsresult rv = nsXFormsUtils::DispatchEvent(mElement, aEvent);
1892 NS_ENSURE_SUCCESS(rv, rv);
1894 // Process queued events
1895 PRInt32 loopCount = 0;
1896 while (mUpdateEventQueue.Count()) {
1897 nsXFormsEvent event =
1898 static_cast<nsXFormsEvent>(NS_PTR_TO_UINT32(mUpdateEventQueue[0]));
1899 NS_ENSURE_TRUE(mUpdateEventQueue.RemoveElementAt(0), NS_ERROR_FAILURE);
1901 rv = nsXFormsUtils::DispatchEvent(mElement, event);
1902 NS_ENSURE_SUCCESS(rv, rv);
1903 ++loopCount;
1904 if (mLoopMax && loopCount > mLoopMax) {
1905 // Note: we could also popup a dialog asking the user whether or not to
1906 // continue.
1907 nsXFormsUtils::ReportError(NS_LITERAL_STRING("modelLoopError"), mElement);
1908 nsXFormsUtils::HandleFatalError(mElement, NS_LITERAL_STRING("LoopError"));
1909 return NS_ERROR_FAILURE;
1913 return NS_OK;
1917 NS_IMETHODIMP
1918 nsXFormsModelElement::RequestRebuild()
1920 return RequestUpdateEvent(eEvent_Rebuild);
1923 NS_IMETHODIMP
1924 nsXFormsModelElement::RequestRecalculate()
1926 return RequestUpdateEvent(eEvent_Recalculate);
1929 NS_IMETHODIMP
1930 nsXFormsModelElement::RequestRevalidate()
1932 return RequestUpdateEvent(eEvent_Revalidate);
1935 NS_IMETHODIMP
1936 nsXFormsModelElement::RequestRefresh()
1938 return RequestUpdateEvent(eEvent_Refresh);
1942 // nsIXFormsContextControl
1944 NS_IMETHODIMP
1945 nsXFormsModelElement::SetContext(nsIDOMNode *aContextNode,
1946 PRInt32 aContextPosition,
1947 PRInt32 aContextSize)
1949 return NS_ERROR_NOT_IMPLEMENTED;
1952 NS_IMETHODIMP
1953 nsXFormsModelElement::GetContext(nsAString &aModelID,
1954 nsIDOMNode **aContextNode,
1955 PRInt32 *aContextPosition,
1956 PRInt32 *aContextSize)
1958 // Adding the nsIXFormsContextControl interface to model to allow
1959 // submission elements to call our binding evaluation methods, like
1960 // EvaluateNodeBinding. If GetContext can get called outside of the binding
1961 // codepath, then this MIGHT lead to problems.
1963 NS_ENSURE_ARG(aContextSize);
1964 NS_ENSURE_ARG(aContextPosition);
1965 *aContextNode = nsnull;
1967 // better get the stuff most likely to fail out of the way first. No sense
1968 // changing the other values that we are returning unless this is successful.
1969 nsresult rv = NS_ERROR_FAILURE;
1971 // Anybody (like a submission element) asking a model element for its context
1972 // for XPath expressions will want the root node of the default instance
1973 // document
1974 nsCOMPtr<nsIDOMDocument> firstInstanceDoc =
1975 FindInstanceDocument(EmptyString());
1976 NS_ENSURE_TRUE(firstInstanceDoc, rv);
1978 nsCOMPtr<nsIDOMElement> firstInstanceRoot;
1979 rv = firstInstanceDoc->GetDocumentElement(getter_AddRefs(firstInstanceRoot));
1980 NS_ENSURE_TRUE(firstInstanceRoot, rv);
1982 nsCOMPtr<nsIDOMNode>rootNode = do_QueryInterface(firstInstanceRoot);
1983 rootNode.swap(*aContextNode);
1985 // found the context, so can finish up assinging the rest of the values that
1986 // we are returning
1987 *aContextPosition = 1;
1988 *aContextSize = 1;
1990 nsAutoString id;
1991 mElement->GetAttribute(NS_LITERAL_STRING("id"), id);
1992 aModelID.Assign(id);
1994 return NS_OK;
1997 NS_IMETHODIMP
1998 nsXFormsModelElement::AddRemoveAbortedControl(nsIXFormsControl *aControl,
1999 PRBool aAdd)
2001 return NS_ERROR_NOT_IMPLEMENTED;
2004 // internal methods
2006 already_AddRefed<nsIDOMDocument>
2007 nsXFormsModelElement::FindInstanceDocument(const nsAString &aID)
2009 nsCOMPtr<nsIInstanceElementPrivate> instance;
2010 nsXFormsModelElement::FindInstanceElement(aID, getter_AddRefs(instance));
2012 nsIDOMDocument *doc = nsnull;
2013 if (instance) {
2014 instance->GetInstanceDocument(&doc); // addrefs
2017 return doc;
2020 nsresult
2021 nsXFormsModelElement::ProcessBindElements()
2023 // ProcessBindElements() will go through each xforms:bind element in
2024 // document order and apply all of the Model Item Properties to the
2025 // instance items in the nodeset. This information will also be entered
2026 // in the Master Dependency Graph. Most of this work is done in the
2027 // ProcessBind() method.
2029 nsCOMPtr<nsIDOMDocument> firstInstanceDoc =
2030 FindInstanceDocument(EmptyString());
2031 if (!firstInstanceDoc)
2032 return NS_OK;
2034 nsCOMPtr<nsIDOMElement> firstInstanceRoot;
2035 firstInstanceDoc->GetDocumentElement(getter_AddRefs(firstInstanceRoot));
2037 nsresult rv;
2038 nsCOMPtr<nsIDOMXPathEvaluator> xpath = do_QueryInterface(firstInstanceDoc,
2039 &rv);
2040 NS_ENSURE_TRUE(xpath, rv);
2042 nsCOMPtr<nsIDOMNodeList> children;
2043 mElement->GetChildNodes(getter_AddRefs(children));
2045 PRUint32 childCount = 0;
2046 if (children)
2047 children->GetLength(&childCount);
2049 nsAutoString namespaceURI, localName;
2050 for (PRUint32 i = 0; i < childCount; ++i) {
2051 nsCOMPtr<nsIDOMNode> child;
2052 children->Item(i, getter_AddRefs(child));
2053 NS_ASSERTION(child, "there can't be null items in the NodeList!");
2055 child->GetLocalName(localName);
2056 if (localName.EqualsLiteral("bind")) {
2057 child->GetNamespaceURI(namespaceURI);
2058 if (namespaceURI.EqualsLiteral(NS_NAMESPACE_XFORMS)) {
2059 rv = ProcessBind(xpath, firstInstanceRoot, 1, 1,
2060 nsCOMPtr<nsIDOMElement>(do_QueryInterface(child)),
2061 PR_TRUE);
2062 if (NS_FAILED(rv)) {
2063 return NS_OK;
2069 return NS_OK;
2072 void
2073 nsXFormsModelElement::Reset()
2075 BackupOrRestoreInstanceData(PR_TRUE);
2076 nsXFormsUtils::DispatchEvent(mElement, eEvent_Rebuild);
2077 nsXFormsUtils::DispatchEvent(mElement, eEvent_Recalculate);
2078 nsXFormsUtils::DispatchEvent(mElement, eEvent_Revalidate);
2079 nsXFormsUtils::DispatchEvent(mElement, eEvent_Refresh);
2082 // This function will restore all of the model's instance data to it's original
2083 // state if the supplied boolean is PR_TRUE. If it is PR_FALSE, this function
2084 // will cause this model's instance data to be backed up.
2085 void
2086 nsXFormsModelElement::BackupOrRestoreInstanceData(PRBool restore)
2088 if (!mInstanceDocuments)
2089 return;
2091 PRUint32 instCount;
2092 mInstanceDocuments->GetLength(&instCount);
2093 if (instCount) {
2094 for (PRUint32 i = 0; i < instCount; ++i) {
2095 nsIInstanceElementPrivate *instance =
2096 mInstanceDocuments->GetInstanceAt(i);
2098 // Don't know what to do with error if we get one.
2099 // Restore/BackupOriginalDocument will already output warnings.
2100 if(restore) {
2101 instance->RestoreOriginalDocument();
2103 else {
2104 instance->BackupOriginalDocument();
2112 nsresult
2113 nsXFormsModelElement::FinishConstruction()
2115 // Ensure that FinishConstruction isn't called due to some callback
2116 // or event handler after the model has started going through its
2117 // destruction phase
2118 NS_ENSURE_STATE(mElement);
2120 // process inline schemas that aren't referenced via the schema attribute
2121 nsCOMPtr<nsIDOMNodeList> children;
2122 mElement->GetChildNodes(getter_AddRefs(children));
2124 if (children) {
2125 PRUint32 childCount = 0;
2126 children->GetLength(&childCount);
2128 nsCOMPtr<nsIDOMNode> node;
2129 nsCOMPtr<nsIDOMElement> element;
2130 nsAutoString nsURI, localName, targetNamespace;
2132 for (PRUint32 i = 0; i < childCount; ++i) {
2133 children->Item(i, getter_AddRefs(node));
2135 element = do_QueryInterface(node);
2136 if (!element)
2137 continue;
2139 node->GetNamespaceURI(nsURI);
2140 node->GetLocalName(localName);
2141 if (nsURI.EqualsLiteral(NS_NAMESPACE_XML_SCHEMA) &&
2142 localName.EqualsLiteral("schema")) {
2143 if (!IsDuplicateSchema(element)) {
2144 nsCOMPtr<nsISchema> schema;
2145 nsresult rv = mSchemas->ProcessSchemaElement(element, nsnull,
2146 getter_AddRefs(schema));
2147 if (!NS_SUCCEEDED(rv)) {
2148 nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaProcessError"),
2149 node);
2156 // (XForms 4.2.1 - cont)
2157 // 3. if applicable, initialize P3P
2159 // 4. construct instance data from initial instance data. apply all
2160 // <bind> elements in document order.
2162 // we get the instance data from our instance child nodes
2164 // We're done initializing this model.
2165 // 5. Perform an xforms-rebuild, xforms-recalculate, and xforms-revalidate in
2166 // sequence, for this model element. (The xforms-refresh is not performed
2167 // since the user interface has not yet been initialized).
2168 nsXFormsUtils::DispatchEvent(mElement, eEvent_Rebuild);
2169 nsXFormsUtils::DispatchEvent(mElement, eEvent_Recalculate);
2170 nsXFormsUtils::DispatchEvent(mElement, eEvent_Revalidate);
2172 return NS_OK;
2175 nsresult
2176 nsXFormsModelElement::InitializeControls()
2178 #ifdef DEBUG
2179 printf("nsXFormsModelElement::InitializeControls()\n");
2180 #endif
2181 nsPostRefresh postRefresh = nsPostRefresh();
2183 nsXFormsControlListItem::iterator it;
2184 nsresult rv;
2185 PRBool dummy;
2186 for (it = mFormControls.begin(); it != mFormControls.end(); ++it) {
2187 // Get control
2188 nsCOMPtr<nsIXFormsControl> control = (*it)->Control();
2189 NS_ASSERTION(control, "mFormControls has null control?!");
2191 #ifdef DEBUG_MODEL
2192 printf("\tControl (%p): ", (void*) control);
2193 nsCOMPtr<nsIDOMElement> controlElement;
2194 control->GetElement(getter_AddRefs(controlElement));
2195 // DBG_TAGINFO(controlElement);
2196 #endif
2197 // Rebind
2198 rv = control->Bind(&dummy);
2199 NS_ENSURE_SUCCESS(rv, rv);
2201 // Refresh controls
2202 rv = control->Refresh();
2203 // XXX: Bug 336608, refresh still fails for some controls, for some
2204 // reason.
2205 // NS_ENSURE_SUCCESS(rv, rv);
2208 mChangedNodes.Clear();
2210 return NS_OK;
2213 void
2214 nsXFormsModelElement::ValidateInstanceDocuments()
2216 if (mInstanceDocuments) {
2217 PRUint32 instCount;
2218 mInstanceDocuments->GetLength(&instCount);
2219 if (instCount) {
2220 nsCOMPtr<nsIDOMDocument> document;
2222 for (PRUint32 i = 0; i < instCount; ++i) {
2223 nsIInstanceElementPrivate* instEle =
2224 mInstanceDocuments->GetInstanceAt(i);
2225 nsCOMPtr<nsIXFormsNSInstanceElement> NSInstEle(instEle);
2226 NSInstEle->GetInstanceDocument(getter_AddRefs(document));
2227 NS_ASSERTION(document,
2228 "nsIXFormsNSInstanceElement::GetInstanceDocument returned null?!");
2230 if (document) {
2231 PRBool isValid = PR_FALSE;
2232 ValidateDocument(document, &isValid);
2234 if (!isValid) {
2235 nsCOMPtr<nsIDOMElement> instanceElement;
2236 instEle->GetElement(getter_AddRefs(instanceElement));
2238 nsXFormsUtils::ReportError(NS_LITERAL_STRING("instDocumentInvalid"),
2239 instanceElement);
2247 // NOTE: This function only runs to completion for _one_ of the models in the
2248 // document.
2249 void
2250 nsXFormsModelElement::MaybeNotifyCompletion()
2252 nsCOMPtr<nsIDOMDocument> domDoc;
2253 mElement->GetOwnerDocument(getter_AddRefs(domDoc));
2255 const nsVoidArray *models = GetModelList(domDoc);
2256 if (!models) {
2257 NS_NOTREACHED("no model list property");
2258 return;
2261 PRInt32 i;
2263 // Nothing to be done if any model is incomplete or hasn't seen
2264 // DOMContentLoaded.
2265 for (i = 0; i < models->Count(); ++i) {
2266 nsXFormsModelElement *model =
2267 static_cast<nsXFormsModelElement *>(models->ElementAt(i));
2268 if (!model->mDocumentLoaded || !model->IsComplete())
2269 return;
2271 // Check validity of |functions=| attribute, if it exists. Since we
2272 // don't support ANY extension functions currently, the existance of
2273 // |functions=| with a non-empty value is an error.
2274 nsCOMPtr<nsIDOMElement> tElement = model->mElement;
2275 nsAutoString extFunctionAtt;
2276 tElement->GetAttribute(NS_LITERAL_STRING("functions"), extFunctionAtt);
2277 if (!extFunctionAtt.IsEmpty()) {
2278 nsXFormsUtils::ReportError(NS_LITERAL_STRING("invalidExtFunction"),
2279 tElement);
2280 nsXFormsUtils::DispatchEvent(tElement, eEvent_ComputeException);
2281 return;
2285 // validate the instance documents because we want schemaValidation to add
2286 // schema type properties from the schema file unto our instance document
2287 // elements.
2288 // XXX: wrong location of this call, @see bug 339674
2289 ValidateInstanceDocuments();
2291 // Register deferred binds with the model. It does not bind the controls,
2292 // only bind them to the model they belong to.
2293 nsXFormsModelElement::ProcessDeferredBinds(domDoc);
2295 // Okay, dispatch xforms-model-construct-done
2296 for (i = 0; i < models->Count(); ++i) {
2297 nsXFormsModelElement *model =
2298 static_cast<nsXFormsModelElement *>(models->ElementAt(i));
2299 nsXFormsUtils::DispatchEvent(model->mElement, eEvent_ModelConstructDone);
2302 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
2303 if (doc) {
2304 PRUint32 loadingMessages = NS_PTR_TO_UINT32(
2305 doc->GetProperty(nsXFormsAtoms::externalMessagesProperty));
2306 if (loadingMessages) {
2307 // if we are still waiting for external messages to load, then put off
2308 // the xforms-ready until a model in the document is notified that they
2309 // are finished loading
2311 return;
2315 // Backup instances and fire xforms-ready
2316 for (i = 0; i < models->Count(); ++i) {
2317 nsXFormsModelElement *model =
2318 static_cast<nsXFormsModelElement *>(models->ElementAt(i));
2319 model->BackupOrRestoreInstanceData(PR_FALSE);
2320 model->mReadyHandled = PR_TRUE;
2321 nsXFormsUtils::DispatchEvent(model->mElement, eEvent_Ready);
2325 nsresult
2326 nsXFormsModelElement::ProcessBind(nsIDOMXPathEvaluator *aEvaluator,
2327 nsIDOMNode *aContextNode,
2328 PRInt32 aContextPosition,
2329 PRInt32 aContextSize,
2330 nsIDOMElement *aBindElement,
2331 PRBool aIsOuter)
2333 // Get the model item properties specified by this \<bind\>.
2334 nsCOMPtr<nsIDOMXPathExpression> props[eModel__count];
2335 nsAutoString propStrings[eModel__count];
2336 nsAutoString attrStr;
2338 nsCOMPtr<nsIDOMXPathNSResolver> resolver;
2339 nsresult rv = aEvaluator->CreateNSResolver(aBindElement,
2340 getter_AddRefs(resolver));
2341 NS_ENSURE_SUCCESS(rv, rv);
2343 nsCOMPtr<nsIXPathEvaluatorInternal> eval = do_QueryInterface(aEvaluator, &rv);
2344 NS_ENSURE_SUCCESS(rv, rv);
2346 for (PRUint32 i = 0; i < eModel__count; ++i) {
2347 sModelPropsList[i]->ToString(attrStr);
2349 aBindElement->GetAttribute(attrStr, propStrings[i]);
2352 // Find the nodeset that this bind applies to.
2353 nsCOMPtr<nsIDOMXPathResult> result;
2355 nsAutoString exprString;
2356 aBindElement->GetAttribute(NS_LITERAL_STRING("nodeset"), exprString);
2357 if (exprString.IsEmpty()) {
2358 exprString = NS_LITERAL_STRING(".");
2361 nsCOMPtr<nsIXFormsXPathState> state = new nsXFormsXPathState(aBindElement,
2362 aContextNode);
2363 NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY);
2364 rv = nsXFormsUtils::EvaluateXPath(eval, exprString, aContextNode, resolver,
2365 state,
2366 nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
2367 aContextPosition, aContextSize,
2368 nsnull, getter_AddRefs(result));
2369 if (NS_FAILED(rv)) {
2370 if (rv == NS_ERROR_DOM_INVALID_EXPRESSION_ERR) {
2371 // the xpath expression isn't valid xpath
2372 const PRUnichar *strings[] = { exprString.get() };
2373 nsXFormsUtils::ReportError(NS_LITERAL_STRING("exprParseError"),
2374 strings, 1, aBindElement, nsnull);
2375 nsXFormsUtils::DispatchEvent(mElement, eEvent_ComputeException);
2376 } else {
2377 #ifdef DEBUG
2378 printf("xforms-binding-exception: XPath Evaluation failed\n");
2379 #endif
2380 const PRUnichar *strings[] = { exprString.get() };
2381 nsXFormsUtils::ReportError(NS_LITERAL_STRING("nodesetEvaluateError"),
2382 strings, 1, aBindElement, aBindElement);
2383 nsXFormsUtils::DispatchEvent(mElement, eEvent_BindingException);
2385 return rv;
2388 NS_ENSURE_STATE(result);
2390 // If this is an outer bind, store the nodeset, as controls binding to this
2391 // bind will need this.
2392 if (aIsOuter) {
2393 nsCOMPtr<nsIContent> content(do_QueryInterface(aBindElement));
2394 NS_ASSERTION(content, "nsIDOMElement not implementing nsIContent?!");
2395 rv = content->SetProperty(nsXFormsAtoms::bind, result,
2396 SupportsDtorFunc);
2397 NS_ENSURE_SUCCESS(rv, rv);
2399 // addref, circumventing nsDerivedSave
2400 NS_ADDREF(static_cast<nsIDOMXPathResult*>(result));
2403 PRUint32 snapLen;
2404 rv = result->GetSnapshotLength(&snapLen);
2405 NS_ENSURE_SUCCESS(rv, rv);
2408 // Iterate over resultset
2409 nsCOMArray<nsIDOMNode> deps;
2410 nsCOMPtr<nsIDOMNode> node;
2412 if (!snapLen) {
2413 return NS_OK;
2416 // We rightly assume that all the nodes in the nodeset came from the same
2417 // document. So now we'll get the xpath evaluator from that document. We
2418 // need to ensure that the context node for the evaluation of each MIP
2419 // expression and the evaluator for those expressions came from the same
2420 // document. It is a rule for xpath.
2421 PRUint32 snapItem = 0;
2423 for (; snapItem < snapLen; ++snapItem) {
2424 rv = result->SnapshotItem(snapItem, getter_AddRefs(node));
2425 NS_ENSURE_SUCCESS(rv, rv);
2427 if (node){
2428 break;
2429 } else {
2430 NS_WARNING("nsXFormsModelElement::ProcessBind(): Empty node in result set.");
2434 if (!node) {
2435 return NS_OK;
2438 nsCOMPtr<nsIDOMDocument> nodesetDoc;
2439 node->GetOwnerDocument(getter_AddRefs(nodesetDoc));
2441 nsCOMPtr<nsIDOMXPathEvaluator> nodesetEval = do_QueryInterface(nodesetDoc);
2442 nsCOMPtr<nsIXPathEvaluatorInternal> nodesetEvalInternal =
2443 do_QueryInterface(nodesetEval);
2444 NS_ENSURE_STATE(nodesetEval && nodesetEvalInternal);
2446 // Since we've already gotten the first node in the nodeset and verified it is
2447 // good to go, we'll contine on. For this node and each subsequent node in
2448 // the nodeset, we'll evaluate the MIP expressions attached to the bind
2449 // element and add them to the MDG. And also process any binds that this
2450 // bind contains (aka nested binds).
2451 while (node && snapItem < snapLen) {
2453 // set the context node for the expression that is being analyzed.
2454 nsCOMPtr<nsIXFormsXPathState> stateForMIP =
2455 new nsXFormsXPathState(aBindElement, node);
2456 NS_ENSURE_TRUE(stateForMIP, NS_ERROR_OUT_OF_MEMORY);
2458 // Apply MIPs
2459 nsXFormsXPathParser parser;
2460 nsXFormsXPathAnalyzer analyzer(eval, resolver, stateForMIP);
2461 PRBool multiMIP = PR_FALSE;
2462 for (PRUint32 j = 0; j < eModel__count; ++j) {
2463 if (propStrings[j].IsEmpty())
2464 continue;
2466 // type and p3ptype are stored as properties on the instance node
2467 if (j == eModel_type || j == eModel_p3ptype) {
2468 nsClassHashtable<nsISupportsHashKey, nsString> *table;
2469 table = j == eModel_type ? &mNodeToType : &mNodeToP3PType;
2470 NS_ENSURE_TRUE(table->IsInitialized(), NS_ERROR_FAILURE);
2472 // Check for existing value
2473 if (table->Get(node, nsnull)) {
2474 multiMIP = PR_TRUE;
2475 break;
2478 // Insert value
2479 nsAutoPtr<nsString> newString(new nsString(propStrings[j]));
2480 NS_ENSURE_TRUE(newString, NS_ERROR_OUT_OF_MEMORY);
2481 NS_ENSURE_TRUE(table->Put(node, newString), NS_ERROR_OUT_OF_MEMORY);
2483 // string is successfully stored in the table, we should not dealloc it
2484 newString.forget();
2486 if (j == eModel_type) {
2487 // Inform MDG that it needs to check type. The only arguments
2488 // actually used are |eModel_constraint| and |node|.
2489 rv = mMDG.AddMIP(eModel_constraint, nsnull, nsnull, PR_FALSE, node,
2490 1, 1);
2491 NS_ENSURE_SUCCESS(rv, rv);
2493 } else {
2495 rv = nsXFormsUtils::CreateExpression(nodesetEvalInternal,
2496 propStrings[j], resolver,
2497 stateForMIP,
2498 getter_AddRefs(props[j]));
2499 if (NS_FAILED(rv)) {
2500 const PRUnichar *strings[] = { propStrings[j].get() };
2501 nsXFormsUtils::ReportError(NS_LITERAL_STRING("mipParseError"),
2502 strings, 1, aBindElement, aBindElement);
2503 nsXFormsUtils::DispatchEvent(mElement, eEvent_ComputeException);
2504 return rv;
2507 // the rest of the MIPs are given to the MDG
2508 nsCOMPtr<nsIDOMNSXPathExpression> expr = do_QueryInterface(props[j]);
2510 // Get node dependencies
2511 nsAutoPtr<nsXFormsXPathNode> xNode(parser.Parse(propStrings[j]));
2512 deps.Clear();
2513 rv = analyzer.Analyze(node, xNode, expr, &propStrings[j], &deps,
2514 snapItem + 1, snapLen, PR_FALSE);
2515 NS_ENSURE_SUCCESS(rv, rv);
2517 // Insert into MDG
2518 rv = mMDG.AddMIP((ModelItemPropName) j,
2519 expr,
2520 &deps,
2521 parser.UsesDynamicFunc(),
2522 node,
2523 snapItem + 1,
2524 snapLen);
2526 // if the call results in NS_ERROR_ABORT the page has tried to set a
2527 // MIP twice, break and emit an exception.
2528 if (rv == NS_ERROR_ABORT) {
2529 multiMIP = PR_TRUE;
2530 break;
2532 NS_ENSURE_SUCCESS(rv, rv);
2536 // If the attribute is already there, the page sets a MIP twice
2537 // which is illegal, and should result in an xforms-binding-exception.
2538 // @see http://www.w3.org/TR/xforms/slice4.html#evt-modelConstruct
2539 // (item 4, c)
2540 if (multiMIP) {
2541 #ifdef DEBUG
2542 printf("xforms-binding-exception: Multiple MIPs on same node!");
2543 #endif
2544 nsXFormsUtils::ReportError(NS_LITERAL_STRING("multiMIPError"),
2545 aBindElement);
2546 nsXFormsUtils::DispatchEvent(aBindElement,
2547 eEvent_BindingException);
2548 return NS_ERROR_FAILURE;
2551 // Now evaluate any child \<bind\> elements.
2552 nsCOMPtr<nsIDOMNodeList> children;
2553 aBindElement->GetChildNodes(getter_AddRefs(children));
2554 if (children) {
2555 PRUint32 childCount = 0;
2556 children->GetLength(&childCount);
2558 nsCOMPtr<nsIDOMNode> child;
2559 nsAutoString value;
2561 for (PRUint32 k = 0; k < childCount; ++k) {
2562 children->Item(k, getter_AddRefs(child));
2563 if (child) {
2564 child->GetLocalName(value);
2565 if (!value.EqualsLiteral("bind"))
2566 continue;
2568 child->GetNamespaceURI(value);
2569 if (!value.EqualsLiteral(NS_NAMESPACE_XFORMS))
2570 continue;
2572 rv = ProcessBind(aEvaluator, node,
2573 snapItem + 1, snapLen,
2574 nsCOMPtr<nsIDOMElement>(do_QueryInterface(child)));
2575 NS_ENSURE_SUCCESS(rv, rv);
2580 ++snapItem;
2581 while (snapItem < snapLen) {
2582 rv = result->SnapshotItem(snapItem, getter_AddRefs(node));
2583 NS_ENSURE_SUCCESS(rv, rv);
2585 if (node) {
2586 break;
2589 NS_WARNING("nsXFormsModelElement::ProcessBind(): Empty node in result set.");
2590 snapItem++;
2594 return NS_OK;
2597 NS_IMETHODIMP
2598 nsXFormsModelElement::AddInstanceElement(nsIInstanceElementPrivate *aInstEle)
2600 NS_ENSURE_STATE(mInstanceDocuments);
2601 mInstanceDocuments->AddInstance(aInstEle);
2603 return NS_OK;
2606 NS_IMETHODIMP
2607 nsXFormsModelElement::RemoveInstanceElement(nsIInstanceElementPrivate *aInstEle)
2609 NS_ENSURE_STATE(mInstanceDocuments);
2610 mInstanceDocuments->RemoveInstance(aInstEle);
2612 return NS_OK;
2615 NS_IMETHODIMP
2616 nsXFormsModelElement::MessageLoadFinished()
2618 // This is our signal that all external message links have been tested. If
2619 // we were waiting for this to send out xforms-ready, then now is the time.
2621 // if this document hasn't processed xforms-model-construct-done, yet (which
2622 // must precede xforms-ready), then we'll send out the xforms-ready later
2623 // as part of our normal handling. If we've already become ready, then this
2624 // event was probably generated by a change in the src attribute on the
2625 // message element. Ignore it in that case.
2626 if (!mConstructDoneHandled || mReadyHandled) {
2627 return NS_OK;
2630 nsCOMPtr<nsIDOMDocument> domDoc;
2631 mElement->GetOwnerDocument(getter_AddRefs(domDoc));
2632 const nsVoidArray *models = GetModelList(domDoc);
2633 nsCOMPtr<nsIDocument>doc = do_QueryInterface(domDoc);
2634 nsCOMArray<nsIXFormsControl> *deferredBindList =
2635 static_cast<nsCOMArray<nsIXFormsControl> *>
2636 (doc->GetProperty(nsXFormsAtoms::deferredBindListProperty));
2638 // if we've already gotten the xforms-model-construct-done event and not
2639 // yet the xforms-ready, we've hit a window where we may still be
2640 // processing the deferred control binding. If so, we'll leave now and
2641 // leave it to MaybeNotifyCompletion to generate the xforms-ready event.
2642 if (deferredBindList) {
2643 return NS_OK;
2647 // if we reached here, then we had to wait on sending out the xforms-ready
2648 // events until the external messages were tested. Now we are finally
2649 // ready to send out xforms-ready to all of the models.
2650 for (int i = 0; i < models->Count(); ++i) {
2651 nsXFormsModelElement *model =
2652 static_cast<nsXFormsModelElement *>(models->ElementAt(i));
2653 model->mReadyHandled = PR_TRUE;
2654 nsXFormsUtils::DispatchEvent(model->mElement, eEvent_Ready);
2657 return NS_OK;
2660 NS_IMETHODIMP
2661 nsXFormsModelElement::GetHasDOMContentFired(PRBool *aLoaded)
2663 NS_ENSURE_ARG_POINTER(aLoaded);
2665 *aLoaded = mDocumentLoaded;
2666 return NS_OK;
2669 NS_IMETHODIMP
2670 nsXFormsModelElement::ForceRebind(nsIXFormsControl* aControl)
2672 if (!aControl) {
2673 return NS_OK;
2676 nsXFormsControlListItem* controlItem = mFormControls.FindControl(aControl);
2677 NS_ENSURE_STATE(controlItem);
2679 PRBool rebindChildren;
2680 nsresult rv = aControl->Bind(&rebindChildren);
2681 NS_ENSURE_SUCCESS(rv, rv);
2683 rv = aControl->Refresh();
2684 // XXX: no rv-check, see bug 336608
2686 // Refresh children
2687 return RefreshSubTree(controlItem->FirstChild(), rebindChildren);
2690 nsresult
2691 nsXFormsModelElement::GetBuiltinTypeName(PRUint16 aType,
2692 nsAString& aName)
2694 switch (aType) {
2695 case nsISchemaBuiltinType::BUILTIN_TYPE_STRING:
2696 aName.AssignLiteral("string");
2697 break;
2698 case nsISchemaBuiltinType::BUILTIN_TYPE_BOOLEAN:
2699 aName.AssignLiteral("boolean");
2700 break;
2701 case nsISchemaBuiltinType::BUILTIN_TYPE_DECIMAL:
2702 aName.AssignLiteral("decimal");
2703 break;
2704 case nsISchemaBuiltinType::BUILTIN_TYPE_FLOAT:
2705 aName.AssignLiteral("float");
2706 break;
2707 case nsISchemaBuiltinType::BUILTIN_TYPE_DOUBLE:
2708 aName.AssignLiteral("double");
2709 break;
2710 case nsISchemaBuiltinType::BUILTIN_TYPE_DURATION:
2711 aName.AssignLiteral("duration");
2712 break;
2713 case nsISchemaBuiltinType::BUILTIN_TYPE_DATETIME:
2714 aName.AssignLiteral("dateTime");
2715 break;
2716 case nsISchemaBuiltinType::BUILTIN_TYPE_TIME:
2717 aName.AssignLiteral("time");
2718 break;
2719 case nsISchemaBuiltinType::BUILTIN_TYPE_DATE:
2720 aName.AssignLiteral("date");
2721 break;
2722 case nsISchemaBuiltinType::BUILTIN_TYPE_GYEARMONTH:
2723 aName.AssignLiteral("gYearMonth");
2724 break;
2725 case nsISchemaBuiltinType::BUILTIN_TYPE_GYEAR:
2726 aName.AssignLiteral("gYear");
2727 break;
2728 case nsISchemaBuiltinType::BUILTIN_TYPE_GMONTHDAY:
2729 aName.AssignLiteral("gMonthDay");
2730 break;
2731 case nsISchemaBuiltinType::BUILTIN_TYPE_GDAY:
2732 aName.AssignLiteral("gDay");
2733 break;
2734 case nsISchemaBuiltinType::BUILTIN_TYPE_GMONTH:
2735 aName.AssignLiteral("gMonth");
2736 break;
2737 case nsISchemaBuiltinType::BUILTIN_TYPE_HEXBINARY:
2738 aName.AssignLiteral("hexBinary");
2739 break;
2740 case nsISchemaBuiltinType::BUILTIN_TYPE_BASE64BINARY:
2741 aName.AssignLiteral("base64Binary");
2742 break;
2743 case nsISchemaBuiltinType::BUILTIN_TYPE_ANYURI:
2744 aName.AssignLiteral("anyURI");
2745 break;
2746 case nsISchemaBuiltinType::BUILTIN_TYPE_QNAME:
2747 aName.AssignLiteral("QName");
2748 break;
2749 case nsISchemaBuiltinType::BUILTIN_TYPE_NOTATION:
2750 aName.AssignLiteral("NOTATION");
2751 break;
2752 case nsISchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING:
2753 aName.AssignLiteral("normalizedString");
2754 break;
2755 case nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN:
2756 aName.AssignLiteral("token");
2757 break;
2758 case nsISchemaBuiltinType::BUILTIN_TYPE_BYTE:
2759 aName.AssignLiteral("byte");
2760 break;
2761 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE:
2762 aName.AssignLiteral("unsignedByte");
2763 break;
2764 case nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER:
2765 aName.AssignLiteral("integer");
2766 break;
2767 case nsISchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER:
2768 aName.AssignLiteral("negativeInteger");
2769 break;
2770 case nsISchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER:
2771 aName.AssignLiteral("nonPositiveInteger");
2772 break;
2773 case nsISchemaBuiltinType::BUILTIN_TYPE_LONG:
2774 aName.AssignLiteral("long");
2775 break;
2776 case nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER:
2777 aName.AssignLiteral("nonNegativeInteger");
2778 break;
2779 case nsISchemaBuiltinType::BUILTIN_TYPE_INT:
2780 aName.AssignLiteral("int");
2781 break;
2782 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT:
2783 aName.AssignLiteral("unsignedInt");
2784 break;
2785 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG:
2786 aName.AssignLiteral("unsignedLong");
2787 break;
2788 case nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER:
2789 aName.AssignLiteral("positiveInteger");
2790 break;
2791 case nsISchemaBuiltinType::BUILTIN_TYPE_SHORT:
2792 aName.AssignLiteral("short");
2793 break;
2794 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT:
2795 aName.AssignLiteral("unsignedShort");
2796 break;
2797 case nsISchemaBuiltinType::BUILTIN_TYPE_LANGUAGE:
2798 aName.AssignLiteral("language");
2799 break;
2800 case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKEN:
2801 aName.AssignLiteral("NMTOKEN");
2802 break;
2803 case nsISchemaBuiltinType::BUILTIN_TYPE_NAME:
2804 aName.AssignLiteral("Name");
2805 break;
2806 case nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME:
2807 aName.AssignLiteral("NCName");
2808 break;
2809 case nsISchemaBuiltinType::BUILTIN_TYPE_ID:
2810 aName.AssignLiteral("ID");
2811 break;
2812 case nsISchemaBuiltinType::BUILTIN_TYPE_IDREF:
2813 aName.AssignLiteral("IDREF");
2814 break;
2815 case nsISchemaBuiltinType::BUILTIN_TYPE_ENTITY:
2816 aName.AssignLiteral("ENTITY");
2817 break;
2818 case nsISchemaBuiltinType::BUILTIN_TYPE_IDREFS:
2819 aName.AssignLiteral("IDREFS");
2820 break;
2821 case nsISchemaBuiltinType::BUILTIN_TYPE_ENTITIES:
2822 aName.AssignLiteral("ENTITIES");
2823 break;
2824 case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKENS:
2825 aName.AssignLiteral("NMTOKENS");
2826 break;
2827 default:
2828 // should never hit here
2829 NS_WARNING("nsXFormsModelElement::GetBuiltinTypeName: Unknown builtin type encountered.");
2830 return NS_ERROR_FAILURE;
2833 return NS_OK;
2836 nsresult
2837 nsXFormsModelElement::GetBuiltinTypesNames(PRUint16 aType,
2838 nsStringArray *aNameArray)
2840 // This function recursively appends aType (and its base types) to
2841 // aNameArray. So it assumes aType isn't in the array already.
2842 nsAutoString typeString, builtString;
2843 PRUint16 parentType = 0;
2845 // We won't append xsd:anyType as the base of every type since that is kinda
2846 // redundant.
2848 nsresult rv = GetBuiltinTypeName(aType, typeString);
2849 NS_ENSURE_SUCCESS(rv, rv);
2851 switch (aType) {
2852 case nsISchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING:
2853 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_STRING;
2854 break;
2855 case nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN:
2856 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING;
2857 break;
2858 case nsISchemaBuiltinType::BUILTIN_TYPE_BYTE:
2859 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_SHORT;
2860 break;
2861 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE:
2862 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT;
2863 break;
2864 case nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER:
2865 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_DECIMAL;
2866 break;
2867 case nsISchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER:
2868 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER;
2869 break;
2870 case nsISchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER:
2871 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER;
2872 break;
2873 case nsISchemaBuiltinType::BUILTIN_TYPE_LONG:
2874 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER;
2875 break;
2876 case nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER:
2877 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER;
2878 break;
2879 case nsISchemaBuiltinType::BUILTIN_TYPE_INT:
2880 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_LONG;
2881 break;
2882 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT:
2883 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG;
2884 break;
2885 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG:
2886 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER;
2887 break;
2888 case nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER:
2889 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER;
2890 break;
2891 case nsISchemaBuiltinType::BUILTIN_TYPE_SHORT:
2892 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_INT;
2893 break;
2894 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT:
2895 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT;
2896 break;
2897 case nsISchemaBuiltinType::BUILTIN_TYPE_LANGUAGE:
2898 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN;
2899 break;
2900 case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKEN:
2901 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN;
2902 break;
2903 case nsISchemaBuiltinType::BUILTIN_TYPE_NAME:
2904 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN;
2905 break;
2906 case nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME:
2907 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NAME;
2908 break;
2909 case nsISchemaBuiltinType::BUILTIN_TYPE_ID:
2910 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME;
2911 break;
2912 case nsISchemaBuiltinType::BUILTIN_TYPE_IDREF:
2913 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME;
2914 break;
2915 case nsISchemaBuiltinType::BUILTIN_TYPE_ENTITY:
2916 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME;
2917 break;
2918 case nsISchemaBuiltinType::BUILTIN_TYPE_IDREFS:
2919 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_IDREF;
2920 break;
2921 case nsISchemaBuiltinType::BUILTIN_TYPE_ENTITIES:
2922 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_ENTITY;
2923 break;
2924 case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKENS:
2925 parentType = nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKEN;
2926 break;
2929 builtString.AppendLiteral(NS_NAMESPACE_XML_SCHEMA);
2930 builtString.AppendLiteral("#");
2931 builtString.Append(typeString);
2932 aNameArray->AppendString(builtString);
2934 if (parentType)
2935 return GetBuiltinTypesNames(parentType, aNameArray);
2937 return NS_OK;
2940 nsresult
2941 nsXFormsModelElement::WalkTypeChainInternal(nsISchemaType *aType,
2942 PRBool aFindRootBuiltin,
2943 PRUint16 *aBuiltinType,
2944 nsStringArray *aTypeArray)
2946 PRUint16 schemaTypeValue = 0;
2947 aType->GetSchemaType(&schemaTypeValue);
2948 NS_ENSURE_STATE(schemaTypeValue);
2949 nsresult rv = NS_OK;
2950 nsCOMPtr<nsISchemaSimpleType> simpleType;
2952 if (schemaTypeValue == nsISchemaType::SCHEMA_TYPE_SIMPLE) {
2953 simpleType = do_QueryInterface(aType);
2954 NS_ENSURE_STATE(simpleType);
2955 PRUint16 simpleTypeValue;
2956 simpleType->GetSimpleType(&simpleTypeValue);
2957 NS_ENSURE_STATE(simpleTypeValue);
2959 switch (simpleTypeValue) {
2960 case nsISchemaSimpleType::SIMPLE_TYPE_BUILTIN:
2962 nsCOMPtr<nsISchemaBuiltinType> builtinType(do_QueryInterface(aType));
2963 NS_ENSURE_STATE(builtinType);
2965 if (aFindRootBuiltin)
2966 return BuiltinTypeToPrimative(builtinType, aBuiltinType);
2968 PRUint16 builtinTypeVal;
2969 rv = builtinType->GetBuiltinType(&builtinTypeVal);
2970 NS_ENSURE_SUCCESS(rv, rv);
2972 if (aBuiltinType)
2973 *aBuiltinType = builtinTypeVal;
2975 if (aTypeArray)
2976 return GetBuiltinTypesNames(builtinTypeVal, aTypeArray);
2978 return NS_OK;
2980 case nsISchemaSimpleType::SIMPLE_TYPE_RESTRICTION:
2982 nsCOMPtr<nsISchemaRestrictionType> restType(do_QueryInterface(aType));
2983 NS_ENSURE_STATE(restType);
2984 restType->GetBaseType(getter_AddRefs(simpleType));
2986 break;
2988 case nsISchemaSimpleType::SIMPLE_TYPE_LIST:
2990 nsCOMPtr<nsISchemaListType> listType(do_QueryInterface(aType));
2991 NS_ENSURE_STATE(listType);
2992 listType->GetListType(getter_AddRefs(simpleType));
2994 break;
2996 case nsISchemaSimpleType::SIMPLE_TYPE_UNION:
2998 // For now union types aren't supported. A union means that the type
2999 // could be of any type listed in the union and still be valid. But we
3000 // don't know which path it will take since we'd basically have to
3001 // validate the node value to know. Someday we may have to figure out
3002 // how to properly handle this, though we may never need to if no other
3003 // processor supports it. Strictly interpreting the spec, we don't
3004 // need to handle unions as far as determining whether a control can
3005 // bind to data of a given type. Just the types defined in the spec
3006 // and restrictions of those types.
3007 return NS_ERROR_XFORMS_UNION_TYPE;
3009 default:
3010 // We only anticipate the 4 types listed above. Definitely an error
3011 // if we get something else.
3012 return NS_ERROR_FAILURE;
3015 } else if (schemaTypeValue == nsISchemaType::SCHEMA_TYPE_COMPLEX) {
3016 nsCOMPtr<nsISchemaComplexType> complexType(do_QueryInterface(aType));
3017 NS_ENSURE_STATE(complexType);
3018 PRUint16 complexTypeValue = 0;
3019 complexType->GetDerivation(&complexTypeValue);
3020 NS_ENSURE_STATE(complexTypeValue);
3021 if ((complexTypeValue ==
3022 nsISchemaComplexType::DERIVATION_RESTRICTION_SIMPLE) ||
3023 (complexTypeValue ==
3024 nsISchemaComplexType::DERIVATION_EXTENSION_SIMPLE)) {
3025 complexType->GetSimpleBaseType(getter_AddRefs(simpleType));
3026 } else {
3027 return NS_ERROR_FAILURE;
3029 } else {
3030 return NS_ERROR_FAILURE;
3033 // For SIMPLE_TYPE_LIST and SIMPLE_TYPE_RESTRICTION we need to go around
3034 // the horn again with the next simpleType. Same with
3035 // DERIVATION_RESTRICTION_SIMPLE and DERIVATION_EXTENSION_SIMPLE. All other
3036 // types should not reach here.
3038 NS_ENSURE_STATE(simpleType);
3040 if (aTypeArray) {
3041 nsAutoString builtString;
3042 rv = aType->GetTargetNamespace(builtString);
3043 NS_ENSURE_SUCCESS(rv, rv);
3044 nsAutoString typeName;
3045 rv = aType->GetName(typeName);
3046 NS_ENSURE_SUCCESS(rv, rv);
3047 builtString.AppendLiteral("#");
3048 builtString.Append(typeName);
3049 aTypeArray->AppendString(builtString);
3052 return WalkTypeChainInternal(simpleType, aFindRootBuiltin, aBuiltinType,
3053 aTypeArray);
3057 nsresult
3058 nsXFormsModelElement::BuiltinTypeToPrimative(nsISchemaBuiltinType *aSchemaType,
3059 PRUint16 *aPrimType)
3061 NS_ENSURE_ARG(aSchemaType);
3062 NS_ENSURE_ARG_POINTER(aPrimType);
3064 PRUint16 builtinType = 0;
3065 nsresult rv = aSchemaType->GetBuiltinType(&builtinType);
3066 NS_ENSURE_SUCCESS(rv, rv);
3068 // Note: this won't return BUILTIN_TYPE_ANY since that is the root of all
3069 // types.
3071 switch (builtinType) {
3072 case nsISchemaBuiltinType::BUILTIN_TYPE_STRING:
3073 case nsISchemaBuiltinType::BUILTIN_TYPE_BOOLEAN:
3074 case nsISchemaBuiltinType::BUILTIN_TYPE_DECIMAL:
3075 case nsISchemaBuiltinType::BUILTIN_TYPE_FLOAT:
3076 case nsISchemaBuiltinType::BUILTIN_TYPE_DOUBLE:
3077 case nsISchemaBuiltinType::BUILTIN_TYPE_DURATION:
3078 case nsISchemaBuiltinType::BUILTIN_TYPE_DATETIME:
3079 case nsISchemaBuiltinType::BUILTIN_TYPE_TIME:
3080 case nsISchemaBuiltinType::BUILTIN_TYPE_DATE:
3081 case nsISchemaBuiltinType::BUILTIN_TYPE_GYEARMONTH:
3082 case nsISchemaBuiltinType::BUILTIN_TYPE_GYEAR:
3083 case nsISchemaBuiltinType::BUILTIN_TYPE_GMONTHDAY:
3084 case nsISchemaBuiltinType::BUILTIN_TYPE_GDAY:
3085 case nsISchemaBuiltinType::BUILTIN_TYPE_GMONTH:
3086 case nsISchemaBuiltinType::BUILTIN_TYPE_HEXBINARY:
3087 case nsISchemaBuiltinType::BUILTIN_TYPE_BASE64BINARY:
3088 case nsISchemaBuiltinType::BUILTIN_TYPE_ANYURI:
3089 case nsISchemaBuiltinType::BUILTIN_TYPE_QNAME:
3090 case nsISchemaBuiltinType::BUILTIN_TYPE_NOTATION:
3091 *aPrimType = builtinType;
3092 break;
3094 case nsISchemaBuiltinType::BUILTIN_TYPE_NORMALIZED_STRING:
3095 case nsISchemaBuiltinType::BUILTIN_TYPE_TOKEN:
3096 case nsISchemaBuiltinType::BUILTIN_TYPE_LANGUAGE:
3097 case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKEN:
3098 case nsISchemaBuiltinType::BUILTIN_TYPE_NAME:
3099 case nsISchemaBuiltinType::BUILTIN_TYPE_NCNAME:
3100 case nsISchemaBuiltinType::BUILTIN_TYPE_ID:
3101 case nsISchemaBuiltinType::BUILTIN_TYPE_IDREF:
3102 case nsISchemaBuiltinType::BUILTIN_TYPE_ENTITY:
3103 case nsISchemaBuiltinType::BUILTIN_TYPE_IDREFS:
3104 case nsISchemaBuiltinType::BUILTIN_TYPE_ENTITIES:
3105 case nsISchemaBuiltinType::BUILTIN_TYPE_NMTOKENS:
3106 *aPrimType = nsISchemaBuiltinType::BUILTIN_TYPE_STRING;
3107 break;
3108 case nsISchemaBuiltinType::BUILTIN_TYPE_BYTE:
3109 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDBYTE:
3110 case nsISchemaBuiltinType::BUILTIN_TYPE_INTEGER:
3111 case nsISchemaBuiltinType::BUILTIN_TYPE_NEGATIVEINTEGER:
3112 case nsISchemaBuiltinType::BUILTIN_TYPE_NONPOSITIVEINTEGER:
3113 case nsISchemaBuiltinType::BUILTIN_TYPE_LONG:
3114 case nsISchemaBuiltinType::BUILTIN_TYPE_NONNEGATIVEINTEGER:
3115 case nsISchemaBuiltinType::BUILTIN_TYPE_INT:
3116 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDINT:
3117 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDLONG:
3118 case nsISchemaBuiltinType::BUILTIN_TYPE_POSITIVEINTEGER:
3119 case nsISchemaBuiltinType::BUILTIN_TYPE_SHORT:
3120 case nsISchemaBuiltinType::BUILTIN_TYPE_UNSIGNEDSHORT:
3121 *aPrimType = nsISchemaBuiltinType::BUILTIN_TYPE_DECIMAL;
3122 break;
3123 default:
3124 // should never hit here
3125 NS_WARNING("nsXFormsModelElement::BuiltinTypeToPrimative: Unknown builtin type encountered.");
3126 return NS_ERROR_FAILURE;
3129 return NS_OK;
3132 NS_IMETHODIMP
3133 nsXFormsModelElement::GetDerivedTypeList(const nsAString &aType,
3134 const nsAString &aNamespace,
3135 nsAString &aTypeList)
3137 nsCOMPtr<nsISchemaCollection> schemaColl = do_QueryInterface(mSchemas);
3138 NS_ENSURE_STATE(schemaColl);
3140 nsCOMPtr<nsISchemaType> schemaType;
3141 schemaColl->GetType(aType, aNamespace, getter_AddRefs(schemaType));
3142 NS_ENSURE_STATE(schemaType);
3144 nsStringArray typeArray;
3145 nsresult rv = WalkTypeChainInternal(schemaType, PR_FALSE, nsnull, &typeArray);
3146 if (NS_SUCCEEDED(rv)) {
3147 nsCOMPtr<nsIStringEnumerator> stringEnum;
3148 rv = NS_NewStringEnumerator(getter_AddRefs(stringEnum), &typeArray);
3149 if (NS_SUCCEEDED(rv)) {
3150 nsAutoString constructorString;
3151 PRBool hasMore = PR_FALSE;
3152 rv = stringEnum->HasMore(&hasMore);
3153 while (NS_SUCCEEDED(rv) && hasMore) {
3154 nsAutoString tempString;
3155 rv = stringEnum->GetNext(tempString);
3156 if (NS_SUCCEEDED(rv)) {
3157 constructorString.Append(tempString);
3158 stringEnum->HasMore(&hasMore);
3159 if (hasMore) {
3160 constructorString.AppendLiteral(" ");
3165 if (NS_SUCCEEDED(rv)) {
3166 aTypeList.Assign(constructorString);
3171 if (NS_FAILED(rv)) {
3172 aTypeList.Assign(EmptyString());
3175 typeArray.Clear();
3177 return rv;
3180 NS_IMETHODIMP
3181 nsXFormsModelElement::GetBuiltinTypeNameForControl(nsIXFormsControl *aControl,
3182 nsAString& aTypeName)
3184 NS_ENSURE_ARG(aControl);
3186 nsCOMPtr<nsISchemaType> schemaType;
3187 nsresult rv = GetTypeForControl(aControl, getter_AddRefs(schemaType));
3188 NS_ENSURE_SUCCESS(rv, rv);
3190 PRUint16 builtinType;
3191 rv = WalkTypeChainInternal(schemaType, PR_FALSE, &builtinType);
3192 NS_ENSURE_SUCCESS(rv, rv);
3194 return GetBuiltinTypeName(builtinType, aTypeName);
3197 NS_IMETHODIMP
3198 nsXFormsModelElement::GetRootBuiltinType(nsISchemaType *aType,
3199 PRUint16 *aBuiltinType)
3201 NS_ENSURE_ARG(aType);
3202 NS_ENSURE_ARG_POINTER(aBuiltinType);
3204 return WalkTypeChainInternal(aType, PR_TRUE, aBuiltinType);
3207 /* static */ void
3208 nsXFormsModelElement::Startup()
3210 sModelPropsList[eModel_type] = nsXFormsAtoms::type;
3211 sModelPropsList[eModel_readonly] = nsXFormsAtoms::readonly;
3212 sModelPropsList[eModel_required] = nsXFormsAtoms::required;
3213 sModelPropsList[eModel_relevant] = nsXFormsAtoms::relevant;
3214 sModelPropsList[eModel_calculate] = nsXFormsAtoms::calculate;
3215 sModelPropsList[eModel_constraint] = nsXFormsAtoms::constraint;
3216 sModelPropsList[eModel_p3ptype] = nsXFormsAtoms::p3ptype;
3219 already_AddRefed<nsIDOMElement>
3220 nsXFormsModelElement::GetDOMElement()
3222 nsIDOMElement* element = nsnull;
3223 NS_IF_ADDREF(element = mElement);
3224 return element;
3227 static void
3228 DeleteBindList(void *aObject,
3229 nsIAtom *aPropertyName,
3230 void *aPropertyValue,
3231 void *aData)
3233 delete static_cast<nsCOMArray<nsIXFormsControl> *>(aPropertyValue);
3236 /* static */ nsresult
3237 nsXFormsModelElement::DeferElementBind(nsIXFormsControl *aControl)
3239 NS_ENSURE_ARG_POINTER(aControl);
3240 nsCOMPtr<nsIDOMElement> element;
3241 nsresult rv = aControl->GetElement(getter_AddRefs(element));
3242 NS_ENSURE_SUCCESS(rv, rv);
3244 nsCOMPtr<nsIContent> content(do_QueryInterface(element));
3245 NS_ASSERTION(content, "nsIDOMElement not implementing nsIContent?!");
3247 nsCOMPtr<nsIDocument> doc = content->GetCurrentDoc();
3248 if (!doc) {
3249 // We do not care about elements without a document. If they get added to
3250 // a document at some point in time, they'll try to bind again.
3251 return NS_OK;
3254 // We are using a PRBool on each control to mark whether the control is on the
3255 // deferredBindList. We are running into too many scenarios where a control
3256 // could be added more than once which will lead to inefficiencies because
3257 // calling bind and refresh on some controls is getting pretty expensive.
3258 // We need to keep the document order of the controls AND don't want
3259 // to walk the deferredBindList every time we want to check about adding a
3260 // control.
3261 PRBool onList = PR_FALSE;
3262 aControl->GetOnDeferredBindList(&onList);
3263 if (onList) {
3264 return NS_OK;
3267 nsCOMArray<nsIXFormsControl> *deferredBindList =
3268 static_cast<nsCOMArray<nsIXFormsControl> *>
3269 (doc->GetProperty(nsXFormsAtoms::deferredBindListProperty));
3271 if (!deferredBindList) {
3272 deferredBindList = new nsCOMArray<nsIXFormsControl>(16);
3273 NS_ENSURE_TRUE(deferredBindList, NS_ERROR_OUT_OF_MEMORY);
3275 doc->SetProperty(nsXFormsAtoms::deferredBindListProperty, deferredBindList,
3276 DeleteBindList);
3279 // always append to the end of the list. We need to keep the elements in
3280 // document order when we process the binds later. Otherwise we have trouble
3281 // when an element is trying to bind and should use its parent as a context
3282 // for the xpath evaluation but the parent isn't bound yet.
3283 deferredBindList->AppendObject(aControl);
3284 aControl->SetOnDeferredBindList(PR_TRUE);
3286 return NS_OK;
3289 /* static */ void
3290 nsXFormsModelElement::ProcessDeferredBinds(nsIDOMDocument *aDoc)
3292 #ifdef DEBUG_MODEL
3293 printf("nsXFormsModelElement::ProcessDeferredBinds()\n");
3294 #endif
3296 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
3298 if (!doc) {
3299 return;
3302 nsPostRefresh postRefresh = nsPostRefresh();
3304 doc->SetProperty(nsXFormsAtoms::readyForBindProperty, doc);
3306 nsCOMArray<nsIXFormsControl> *deferredBindList =
3307 static_cast<nsCOMArray<nsIXFormsControl> *>
3308 (doc->GetProperty(nsXFormsAtoms::deferredBindListProperty));
3310 if (deferredBindList) {
3311 for (PRInt32 i = 0; i < deferredBindList->Count(); ++i) {
3312 nsIXFormsControl *control = deferredBindList->ObjectAt(i);
3313 if (control) {
3314 control->BindToModel(PR_FALSE);
3315 control->SetOnDeferredBindList(PR_FALSE);
3319 doc->DeleteProperty(nsXFormsAtoms::deferredBindListProperty);
3323 nsresult
3324 nsXFormsModelElement::HandleLoad(nsIDOMEvent* aEvent)
3326 if (!mInstancesInitialized) {
3327 // XXX This is for Bug 308106. In Gecko 1.8 DoneAddingChildren is not
3328 // called in XUL if the element doesn't have any child nodes.
3329 InitializeInstances();
3332 mDocumentLoaded = PR_TRUE;
3334 nsCOMPtr<nsIDOMDocument> document;
3335 mElement->GetOwnerDocument(getter_AddRefs(document));
3336 NS_ENSURE_STATE(document);
3337 nsXFormsUtils::DispatchDeferredEvents(document);
3339 // dispatch xforms-model-construct, xforms-rebuild, xforms-recalculate,
3340 // xforms-revalidate
3342 // We wait until DOMContentLoaded to dispatch xforms-model-construct,
3343 // since the model may have an action handler for this event and Mozilla
3344 // doesn't register XML Event listeners until the document is loaded.
3346 // xforms-model-construct is not cancellable, so always proceed.
3348 nsXFormsUtils::DispatchEvent(mElement, eEvent_ModelConstruct);
3350 if (mPendingInlineSchemas.Count() > 0) {
3351 nsCOMPtr<nsIDOMElement> el;
3352 nsresult rv = NS_OK;
3353 for (PRInt32 i=0; i<mPendingInlineSchemas.Count(); ++i) {
3354 GetSchemaElementById(mElement, *mPendingInlineSchemas[i],
3355 getter_AddRefs(el));
3356 if (!el) {
3357 rv = NS_ERROR_UNEXPECTED;
3358 } else {
3359 // According to section 3.3.1 of the spec, more than one schema per
3360 // namespace isn't allowed, but it also doesn't spell out very well
3361 // what this means a processor should do about it. Since most
3362 // processors ignore this rule and it isn't specifically a fatal error,
3363 // we won't make this a failure here.
3364 if (!IsDuplicateSchema(el)) {
3365 nsCOMPtr<nsISchema> schema;
3366 // no need to observe errors via the callback. instead, rely on
3367 // this method returning a failure code when it encounters errors.
3368 rv = mSchemas->ProcessSchemaElement(el, nsnull,
3369 getter_AddRefs(schema));
3370 if (NS_SUCCEEDED(rv))
3371 mSchemaCount++;
3374 if (NS_FAILED(rv)) {
3375 // this is a fatal error
3376 nsXFormsUtils::ReportError(NS_LITERAL_STRING("schemaLoadError"), mElement);
3377 nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkException);
3378 return NS_OK;
3381 if (IsComplete()) {
3382 rv = FinishConstruction();
3383 NS_ENSURE_SUCCESS(rv, rv);
3385 mPendingInlineSchemas.Clear();
3388 // We may still be waiting on external documents to load.
3389 MaybeNotifyCompletion();
3391 return NS_OK;
3394 nsresult
3395 nsXFormsModelElement::HandleUnload(nsIDOMEvent* aEvent)
3397 // due to fastback changes, had to move this notification out from under
3398 // model's WillChangeDocument override.
3399 nsXFormsUtils::DispatchEvent(mElement, eEvent_ModelDestruct);
3401 // Releasing references as early as possible, see bug 375320.
3402 mSchemas = nsnull;
3404 if (mInstanceDocuments)
3405 mInstanceDocuments->DropReferences();
3407 mFormControls.Clear();
3408 mControlListHash.Clear();
3409 return NS_OK;
3412 PRBool
3413 nsXFormsModelElement::IsDuplicateSchema(nsIDOMElement *aSchemaElement)
3415 nsCOMPtr<nsISchemaCollection> schemaColl = do_QueryInterface(mSchemas);
3416 if (!schemaColl)
3417 return PR_FALSE;
3419 const nsAFlatString& empty = EmptyString();
3420 nsAutoString targetNamespace;
3421 aSchemaElement->GetAttributeNS(empty,
3422 NS_LITERAL_STRING("targetNamespace"),
3423 targetNamespace);
3424 targetNamespace.Trim(" \r\n\t");
3426 nsCOMPtr<nsISchema> schema;
3427 schemaColl->GetSchema(targetNamespace, getter_AddRefs(schema));
3428 if (!schema)
3429 return PR_FALSE;
3431 // A schema with the same target namespace already exists in the
3432 // schema collection and the first instance has already been processed.
3433 // Report an error to the JS console and dispatch the LinkError event,
3434 // but do not consider it a fatal error.
3435 const nsPromiseFlatString& flat = PromiseFlatString(targetNamespace);
3436 const PRUnichar *strings[] = { flat.get() };
3437 nsXFormsUtils::ReportError(NS_LITERAL_STRING("duplicateSchema"),
3438 strings, 1, aSchemaElement, aSchemaElement,
3439 nsnull);
3440 nsXFormsUtils::DispatchEvent(mElement, eEvent_LinkError);
3441 return PR_TRUE;
3444 nsresult
3445 NS_NewXFormsModelElement(nsIXTFElement **aResult)
3447 *aResult = new nsXFormsModelElement();
3448 if (!*aResult)
3449 return NS_ERROR_OUT_OF_MEMORY;
3451 NS_ADDREF(*aResult);
3452 return NS_OK;
3456 // ---------------------------- //
3458 // nsXFormsModelInstanceDocuments
3460 NS_IMPL_ISUPPORTS2(nsXFormsModelInstanceDocuments, nsIDOMNodeList, nsIClassInfo)
3462 nsXFormsModelInstanceDocuments::nsXFormsModelInstanceDocuments()
3463 : mInstanceList(16)
3467 NS_IMETHODIMP
3468 nsXFormsModelInstanceDocuments::GetLength(PRUint32* aLength)
3470 *aLength = mInstanceList.Count();
3472 return NS_OK;
3475 NS_IMETHODIMP
3476 nsXFormsModelInstanceDocuments::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
3478 *aReturn = nsnull;
3479 nsIInstanceElementPrivate* instance = mInstanceList.SafeObjectAt(aIndex);
3480 if (instance) {
3481 nsCOMPtr<nsIDOMDocument> doc;
3482 if (NS_SUCCEEDED(instance->GetInstanceDocument(getter_AddRefs(doc))) && doc) {
3483 NS_ADDREF(*aReturn = doc);
3487 return NS_OK;
3490 nsIInstanceElementPrivate*
3491 nsXFormsModelInstanceDocuments::GetInstanceAt(PRUint32 aIndex)
3493 return mInstanceList.ObjectAt(aIndex);
3496 void
3497 nsXFormsModelInstanceDocuments::AddInstance(nsIInstanceElementPrivate *aInst)
3499 // always append to the end of the list. We need to keep the elements in
3500 // document order since the first instance element is the default instance
3501 // document for the model.
3502 mInstanceList.AppendObject(aInst);
3505 void
3506 nsXFormsModelInstanceDocuments::RemoveInstance(nsIInstanceElementPrivate *aInst)
3508 mInstanceList.RemoveObject(aInst);
3511 void
3512 nsXFormsModelInstanceDocuments::DropReferences()
3514 mInstanceList.Clear();
3517 // nsIClassInfo implementation
3519 static const nsIID sInstScriptingIIDs[] = {
3520 NS_IDOMNODELIST_IID
3523 NS_IMETHODIMP
3524 nsXFormsModelInstanceDocuments::GetInterfaces(PRUint32 *aCount,
3525 nsIID * **aArray)
3527 return
3528 nsXFormsUtils::CloneScriptingInterfaces(sInstScriptingIIDs,
3529 NS_ARRAY_LENGTH(sInstScriptingIIDs),
3530 aCount, aArray);
3533 NS_IMETHODIMP
3534 nsXFormsModelInstanceDocuments::GetHelperForLanguage(PRUint32 language,
3535 nsISupports **_retval)
3537 *_retval = nsnull;
3538 return NS_OK;
3541 NS_IMETHODIMP
3542 nsXFormsModelInstanceDocuments::GetContractID(char * *aContractID)
3544 *aContractID = nsnull;
3545 return NS_OK;
3548 NS_IMETHODIMP
3549 nsXFormsModelInstanceDocuments::GetClassDescription(char * *aClassDescription)
3551 *aClassDescription = nsnull;
3552 return NS_OK;
3555 NS_IMETHODIMP
3556 nsXFormsModelInstanceDocuments::GetClassID(nsCID * *aClassID)
3558 *aClassID = nsnull;
3559 return NS_OK;
3562 NS_IMETHODIMP
3563 nsXFormsModelInstanceDocuments::GetImplementationLanguage(PRUint32 *aLang)
3565 *aLang = nsIProgrammingLanguage::CPLUSPLUS;
3566 return NS_OK;
3569 NS_IMETHODIMP
3570 nsXFormsModelInstanceDocuments::GetFlags(PRUint32 *aFlags)
3572 *aFlags = nsIClassInfo::DOM_OBJECT;
3573 return NS_OK;
3576 NS_IMETHODIMP
3577 nsXFormsModelInstanceDocuments::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
3579 return NS_ERROR_NOT_AVAILABLE;