Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsControlStub.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla XForms support.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Novell, Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Allan Beaufour <abeaufour@novell.com>
00024  *  Olli Pettay <Olli.Pettay@helsinki.fi>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsIModelElementPrivate.h"
00041 #include "nsXFormsControlStub.h"
00042 #include "nsXFormsMDGEngine.h"
00043 
00044 #include "nsIDOMDocument.h"
00045 #include "nsIDOMEvent.h"
00046 #include "nsIDOMKeyEvent.h"
00047 #include "nsIDOMEventTarget.h"
00048 #include "nsIDOMXPathResult.h"
00049 #include "nsIDocument.h"
00050 #include "nsXFormsModelElement.h"
00051 #include "nsPIDOMWindow.h"
00052 #include "nsIFocusController.h"
00053 #include "nsIServiceManager.h"
00054 #include "nsIEventStateManager.h"
00055 #include "nsIContent.h"
00056 #include "nsIDOM3Node.h"
00057 #include "nsIDOMAttr.h"
00058 
00060 class nsXFormsHintHelpListener : public nsIDOMEventListener {
00061 public:
00062   NS_DECL_ISUPPORTS
00063   NS_DECL_NSIDOMEVENTLISTENER
00064 };
00065 
00066 NS_IMPL_ISUPPORTS1(nsXFormsHintHelpListener, nsIDOMEventListener)
00067 
00068 NS_IMETHODIMP
00069 nsXFormsHintHelpListener::HandleEvent(nsIDOMEvent* aEvent)
00070 {
00071   if (!aEvent)
00072     return NS_ERROR_UNEXPECTED;
00073 
00074   nsCOMPtr<nsIDOMEventTarget> target;
00075   aEvent->GetCurrentTarget(getter_AddRefs(target));
00076   nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(target));
00077   if (nsXFormsUtils::EventHandlingAllowed(aEvent, targetNode)) {
00078     nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
00079     if (keyEvent) {
00080       PRUint32 code = 0;
00081       keyEvent->GetKeyCode(&code);
00082       if (code == nsIDOMKeyEvent::DOM_VK_F1) {
00083         PRBool defaultEnabled = PR_TRUE;
00084         nsresult rv = nsXFormsUtils::DispatchEvent(targetNode, eEvent_Help,
00085                                                    &defaultEnabled);
00086 
00087         // If the xforms event's default behavior was disabled, prevent the DOM
00088         // event's default action as well.  This means that the F1 key was
00089         // handled (a help dialog was shown) and we don't want the browser to
00090         // show it's help dialog.
00091         if (NS_SUCCEEDED(rv) && !defaultEnabled)
00092           aEvent->PreventDefault();
00093       }
00094     } else {
00095       nsAutoString type;
00096       aEvent->GetType(type);
00097       nsXFormsUtils::DispatchEvent(targetNode,
00098                                    (type.EqualsLiteral("mouseover") ||
00099                                     type.EqualsLiteral("focus"))
00100                                    ? eEvent_Hint : eEvent_MozHintOff);
00101     }
00102   }
00103 
00104   return NS_OK;
00105 }
00106 
00107 NS_IMETHODIMP
00108 nsXFormsControlStubBase::GetBoundNode(nsIDOMNode **aBoundNode)
00109 {
00110   NS_IF_ADDREF(*aBoundNode = mBoundNode);
00111   return NS_OK;  
00112 }
00113 
00114 NS_IMETHODIMP
00115 nsXFormsControlStubBase::GetDependencies(nsCOMArray<nsIDOMNode> **aDependencies)
00116 {
00117   if (aDependencies)
00118     *aDependencies = &mDependencies;
00119   return NS_OK;  
00120 }
00121 
00122 NS_IMETHODIMP
00123 nsXFormsControlStubBase::GetElement(nsIDOMElement **aElement)
00124 {
00125   NS_IF_ADDREF(*aElement = mElement);
00126   return NS_OK;  
00127 }
00128 
00129 void
00130 nsXFormsControlStubBase::RemoveIndexListeners()
00131 {
00132   if (!mIndexesUsed.Count())
00133     return;
00134 
00135   for (PRInt32 i = 0; i < mIndexesUsed.Count(); ++i) {
00136     nsCOMPtr<nsIXFormsRepeatElement> rep = mIndexesUsed[i];
00137     rep->RemoveIndexUser(this);
00138   }
00139 
00140   mIndexesUsed.Clear();
00141 }
00142 
00143 NS_IMETHODIMP
00144 nsXFormsControlStubBase::ResetBoundNode(const nsString &aBindAttribute,
00145                                         PRUint16        aResultType,
00146                                         PRBool         *aContextChanged)
00147 {
00148   NS_ENSURE_ARG(aContextChanged);
00149 
00150   // Clear existing bound node, etc.
00151   *aContextChanged = mBoundNode ? PR_TRUE : PR_FALSE;
00152   nsCOMPtr<nsIDOMNode> oldBoundNode;
00153   oldBoundNode.swap(mBoundNode);
00154   mUsesModelBinding = PR_FALSE;
00155   mAppearDisabled = PR_FALSE;
00156   mDependencies.Clear();
00157   RemoveIndexListeners();
00158 
00159   if (!mHasParent || !mHasDoc || !HasBindingAttribute())
00160     return NS_OK_XFORMS_NOTREADY;
00161 
00162   nsCOMPtr<nsIDOMXPathResult> result;
00163   nsresult rv = ProcessNodeBinding(aBindAttribute, aResultType,
00164                                    getter_AddRefs(result));
00165 
00166   if (NS_FAILED(rv)) {
00167     nsXFormsUtils::ReportError(NS_LITERAL_STRING("controlBindError"), mElement);
00168     return rv;
00169   }
00170 
00171   if (rv == NS_OK_XFORMS_DEFERRED || rv == NS_OK_XFORMS_NOTREADY || !result) {
00172     // Binding was deferred, or not bound
00173     return rv;
00174   }
00175 
00176   // Get context node, if any
00177   if (mUsesModelBinding) {
00178     // When bound via @bind, we'll get a snapshot back
00179     result->SnapshotItem(0, getter_AddRefs(mBoundNode));
00180   } else {
00181     result->GetSingleNodeValue(getter_AddRefs(mBoundNode));
00182   }
00183 
00184   *aContextChanged = (oldBoundNode != mBoundNode);
00185 
00186   // Some controls may not be bound to certain types of content. If the content
00187   // is a disallowed type, report the error and dispatch a binding exception
00188   // event.
00189   PRBool isAllowed = IsContentAllowed();
00190 
00191   if (!mBoundNode || !isAllowed) {
00192     // If there's no result (ie, no instance node) returned by the above, it
00193     // means that the binding is not pointing to an instance data node, so we
00194     // should disable the control.
00195     mAppearDisabled = PR_TRUE;
00196 
00197     if (!isAllowed) {
00198       // build the error string that we want output to the ErrorConsole
00199       nsAutoString localName;
00200       mElement->GetLocalName(localName);
00201       const PRUnichar *strings[] = { localName.get() };
00202 
00203       nsXFormsUtils::ReportError(
00204         NS_LITERAL_STRING("boundTypeErrorComplexContent"),
00205         strings, 1, mElement, mElement);
00206 
00207       nsXFormsUtils::DispatchEvent(mElement, eEvent_BindingException);
00208     }
00209 
00210     nsCOMPtr<nsIXTFElementWrapper> wrapper(do_QueryInterface(mElement));
00211     NS_ENSURE_STATE(wrapper);
00212 
00213     PRInt32 iState;
00214     GetDisabledIntrinsicState(&iState);
00215     return wrapper->SetIntrinsicState(iState);
00216   }
00217 
00218   // Check for presence of @xsi:type on bound node and add as a dependency
00219   nsCOMPtr<nsIDOMElement> boundEl(do_QueryInterface(mBoundNode));
00220   if (boundEl) {
00221     nsCOMPtr<nsIDOMAttr> attrNode;
00222     rv = boundEl->GetAttributeNodeNS(NS_LITERAL_STRING(NS_NAMESPACE_XML_SCHEMA_INSTANCE),
00223                                      NS_LITERAL_STRING("type"),
00224                                      getter_AddRefs(attrNode));
00225     if (NS_SUCCEEDED(rv) && attrNode) {
00226       mDependencies.AppendObject(attrNode);
00227     }
00228   }
00229 
00230   return NS_OK;
00231 }
00232 
00233 NS_IMETHODIMP
00234 nsXFormsControlStubBase::Bind(PRBool* aContextChanged)
00235 {
00236   return ResetBoundNode(NS_LITERAL_STRING("ref"),
00237                         nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE,
00238                         aContextChanged);
00239 }
00240 
00241 NS_IMETHODIMP
00242 nsXFormsControlStubBase::Refresh()
00243 {
00244   // XXX: In theory refresh should never be called when there is no model,
00245   // but that's definately not the case now.
00246   return (mModel && !mAppearDisabled) ? mModel->SetStates(this, mBoundNode)
00247                                       : NS_OK;
00248 }
00249 
00250 NS_IMETHODIMP
00251 nsXFormsControlStubBase::TryFocus(PRBool* aOK)
00252 {
00253   *aOK = PR_FALSE;
00254   return NS_OK;
00255 }
00256   
00257 NS_IMETHODIMP
00258 nsXFormsControlStubBase::IsEventTarget(PRBool *aOK)
00259 {
00260   *aOK = PR_TRUE;
00261   return NS_OK;
00262 }
00263 
00264 NS_IMETHODIMP
00265 nsXFormsControlStubBase::GetUsesModelBinding(PRBool *aRes)
00266 {
00267   *aRes = mUsesModelBinding;
00268   return NS_OK;
00269 }
00270 
00271 NS_IMETHODIMP
00272 nsXFormsControlStubBase::GetUsesSingleNodeBinding(PRBool *aRes)
00273 {
00274   *aRes = PR_TRUE;
00275   return NS_OK;
00276 }
00277 
00278 NS_IMETHODIMP
00279 nsXFormsControlStubBase::GetOnDeferredBindList(PRBool *aOnList)
00280 {
00281   NS_ENSURE_ARG_POINTER(aOnList);
00282   *aOnList = mOnDeferredBindList;
00283   return NS_OK;
00284 }
00285 
00286 NS_IMETHODIMP
00287 nsXFormsControlStubBase::SetOnDeferredBindList(PRBool aPutOnList)
00288 {
00289   mOnDeferredBindList = aPutOnList;
00290   return NS_OK;
00291 }
00292 
00293 NS_IMETHODIMP
00294 nsXFormsControlStubBase::GetDefaultIntrinsicState(PRInt32 *aState)
00295 {
00296   NS_ENSURE_ARG_POINTER(aState);
00297   *aState = kDefaultIntrinsicState;
00298   return NS_OK;
00299 }
00300 
00301 NS_IMETHODIMP
00302 nsXFormsControlStubBase::GetDisabledIntrinsicState(PRInt32 *aState)
00303 {
00304   NS_ENSURE_ARG_POINTER(aState);
00305   *aState = kDisabledIntrinsicState;
00306   return NS_OK;
00307 }
00308 
00309 nsresult
00310 nsXFormsControlStubBase::MaybeAddToModel(nsIModelElementPrivate *aOldModel,
00311                                          nsIXFormsControl       *aParent)
00312 {
00313   if (GetRepeatState() == eType_Template) {
00314     // No sense going any further.  A template control has no need to bind,
00315     // refresh, etc. so no reason to put it on the model's control list
00316     return NS_OK;
00317   }
00318 
00319   // XXX: just doing pointer comparison would be nice....
00320   PRBool sameModel = PR_FALSE;
00321   nsresult rv;
00322 
00323   if (mModel) {
00324     nsCOMPtr<nsIDOM3Node> n3Model(do_QueryInterface(mModel));
00325     nsCOMPtr<nsIDOMNode> nOldModel(do_QueryInterface(aOldModel));
00326     NS_ASSERTION(n3Model, "model element not supporting nsIDOM3Node?!");
00327     rv = n3Model->IsSameNode(nOldModel, &sameModel);
00328     NS_ENSURE_SUCCESS(rv, rv);
00329   } else {
00330     sameModel = !aOldModel;
00331   }
00332 
00333   if (!sameModel) {
00334     if (aOldModel) {
00335       rv = aOldModel->RemoveFormControl(this);
00336       NS_ENSURE_SUCCESS(rv, rv);
00337     }
00338     if (mModel) {
00339       rv = mModel->AddFormControl(this, aParent);
00340       NS_ENSURE_SUCCESS(rv, rv);
00341     }
00342   }
00343   return NS_OK;
00344 }
00345 
00346 
00347 nsresult
00348 nsXFormsControlStubBase::ProcessNodeBinding(const nsString          &aBindingAttr,
00349                                             PRUint16                 aResultType,
00350                                             nsIDOMXPathResult      **aResult,
00351                                             nsIModelElementPrivate **aModel)
00352 {
00353   nsStringArray indexesUsed;
00354 
00355   if (aResult) {
00356     *aResult = nsnull;
00357   }
00358 
00359   if (aModel) {
00360     *aModel = nsnull;
00361   }
00362 
00363   // let's not go through all of this rigamarol if we don't have a chance
00364   // in heck of binding anyhow.  Check to see if the models will be receptive
00365   // to some binding.  readyForBindProperty is set when they are.  Make sure
00366   // to return NS_OK so that we don't start complaining about binding
00367   // failures in this situation.
00368 
00369   if (!nsXFormsUtils::IsDocumentReadyForBind(mElement)) {
00370     nsXFormsModelElement::DeferElementBind(this);
00371     return NS_OK_XFORMS_DEFERRED;
00372   }
00373 
00374   nsresult rv;
00375   PRBool usesModelBinding;
00376   nsCOMPtr<nsIModelElementPrivate> oldModel(mModel);
00377   nsCOMPtr<nsIXFormsControl> parentControl;
00378   rv = nsXFormsUtils::EvaluateNodeBinding(mElement,
00379                                           kElementFlags,
00380                                           aBindingAttr,
00381                                           EmptyString(),
00382                                           aResultType,
00383                                           getter_AddRefs(mModel),
00384                                           aResult,
00385                                           &usesModelBinding,
00386                                           getter_AddRefs(parentControl),
00387                                           &mDependencies,
00388                                           &indexesUsed);
00389   NS_ENSURE_SUCCESS(rv, rv);
00390 
00391   // if NS_OK_XFORMS_NOTREADY, mModel probably won't exist or might no longer
00392   // be valid, so shouldn't continue.
00393   if (rv == NS_OK_XFORMS_NOTREADY) {
00394     return rv;
00395   }
00396 
00397   NS_ENSURE_STATE(mModel);
00398 
00399   rv = MaybeAddToModel(oldModel, parentControl);
00400   NS_ENSURE_SUCCESS(rv, rv);
00401 
00402   if (aModel)
00403     NS_ADDREF(*aModel = mModel);
00404 
00405   mUsesModelBinding = usesModelBinding;
00406 
00407   if (indexesUsed.Count()) {
00408     // add index listeners on repeat elements
00409 
00410     for (PRInt32 i = 0; i < indexesUsed.Count(); ++i) {
00411       // Find the repeat element and add |this| as a listener
00412       nsCOMPtr<nsIDOMElement> repElem;
00413       nsXFormsUtils::GetElementByContextId(mElement, *(indexesUsed[i]),
00414                                            getter_AddRefs(repElem));
00415       nsCOMPtr<nsIXFormsRepeatElement> rep(do_QueryInterface(repElem));
00416       if (!rep)
00417         continue;
00418 
00419       rv = rep->AddIndexUser(this);
00420       NS_ENSURE_SUCCESS(rv, rv);
00421 
00422       rv = mIndexesUsed.AppendObject(rep);
00423       NS_ENSURE_SUCCESS(rv, rv);
00424     }
00425   }
00426 
00427   return NS_OK;
00428 }
00429 
00430 NS_IMETHODIMP
00431 nsXFormsControlStubBase::BindToModel(PRBool aSetBoundNode)
00432 {
00433   if (GetRepeatState() == eType_Template) {
00434     // No sense going any further.  A template control has no need to bind,
00435     // refresh, etc. so no reason to put it on the model's control list
00436     return NS_OK;
00437   }
00438 
00439   nsCOMPtr<nsIModelElementPrivate> oldModel(mModel);
00440 
00441   nsCOMPtr<nsIXFormsControl> parentControl;
00442   nsCOMPtr<nsIDOMNode> boundNode;
00443   mModel = nsXFormsUtils::GetModel(mElement, getter_AddRefs(parentControl),
00444                                    kElementFlags,
00445                                    getter_AddRefs(boundNode));
00446   if (aSetBoundNode) {
00447     mBoundNode.swap(boundNode);
00448   }
00449 
00450   return MaybeAddToModel(oldModel, parentControl);
00451 }
00452 
00453 void
00454 nsXFormsControlStubBase::ResetHelpAndHint(PRBool aInitialize)
00455 {
00456   nsCOMPtr<nsIDOMEventTarget> targ(do_QueryInterface(mElement));
00457   if (!targ)
00458     return;
00459 
00460   NS_NAMED_LITERAL_STRING(mouseover, "mouseover");
00461   NS_NAMED_LITERAL_STRING(mouseout, "mouseout");
00462   NS_NAMED_LITERAL_STRING(focus, "focus");
00463   NS_NAMED_LITERAL_STRING(blur, "blur");
00464   NS_NAMED_LITERAL_STRING(keypress, "keypress");
00465 
00466   if (mEventListener) {
00467     targ->RemoveEventListener(mouseover, mEventListener, PR_TRUE);
00468     targ->RemoveEventListener(mouseout, mEventListener, PR_TRUE);
00469     targ->RemoveEventListener(focus, mEventListener, PR_TRUE);
00470     targ->RemoveEventListener(blur, mEventListener, PR_TRUE);
00471     targ->RemoveEventListener(keypress, mEventListener, PR_TRUE);
00472     mEventListener = nsnull;
00473   }
00474 
00475   if (aInitialize) {
00476     mEventListener = new nsXFormsHintHelpListener();
00477     if (!mEventListener)
00478       return;
00479 
00480     targ->AddEventListener(mouseover, mEventListener, PR_TRUE);
00481     targ->AddEventListener(mouseout, mEventListener, PR_TRUE);
00482     targ->AddEventListener(focus, mEventListener, PR_TRUE);
00483     targ->AddEventListener(blur, mEventListener, PR_TRUE);
00484     targ->AddEventListener(keypress, mEventListener, PR_TRUE);
00485   }
00486 }
00487 
00488 PRBool
00489 nsXFormsControlStubBase::GetRelevantState()
00490 {
00491   PRBool res = PR_FALSE;
00492   nsCOMPtr<nsIContent> content(do_QueryInterface(mElement));
00493   if (content && (content->IntrinsicState() & NS_EVENT_STATE_ENABLED)) {
00494     res = PR_TRUE;
00495   }  
00496   return res;
00497 }
00498 
00499 nsresult
00500 nsXFormsControlStubBase::HandleDefault(nsIDOMEvent *aEvent,
00501                                        PRBool      *aHandled)
00502 {
00503   NS_ENSURE_ARG(aHandled);
00504   *aHandled = PR_FALSE;
00505 
00506   if (nsXFormsUtils::EventHandlingAllowed(aEvent, mElement)) {
00507 
00508     // Check that we are the target of the event
00509     nsCOMPtr<nsIDOMEventTarget> target;
00510     aEvent->GetTarget(getter_AddRefs(target));
00511     nsCOMPtr<nsIDOMElement> targetE(do_QueryInterface(target));
00512     if (targetE && targetE != mElement) {
00513       return NS_OK;
00514     }
00515 
00516     // Handle event
00517     nsAutoString type;
00518     aEvent->GetType(type);
00519 
00520     if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Focus].name)) {
00521       TryFocus(aHandled);
00522     } else if (type.Equals(NS_LITERAL_STRING("keypress"))) { 
00523       nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
00524       if (keyEvent) {
00525         PRUint32 keycode;
00526         keyEvent->GetKeyCode(&keycode);
00527         if (keycode == nsIDOMKeyEvent::DOM_VK_TAB) {
00528           PRBool extraKey = PR_FALSE;
00529 
00530           keyEvent->GetAltKey(&extraKey);
00531           if (extraKey) {
00532             return NS_OK;
00533           }
00534 
00535           keyEvent->GetCtrlKey(&extraKey);
00536           if (extraKey) {
00537             return NS_OK;
00538           }
00539 
00540           keyEvent->GetMetaKey(&extraKey);
00541           if (extraKey) {
00542             return NS_OK;
00543           }
00544 
00545           keyEvent->GetShiftKey(&extraKey);
00546           mPreventLoop = PR_TRUE;
00547           if (extraKey) {
00548             nsXFormsUtils::DispatchEvent(mElement, eEvent_Previous);
00549           } else {
00550             nsXFormsUtils::DispatchEvent(mElement, eEvent_Next);
00551           }
00552         }
00553       }
00554     } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_Next].name) ||     
00555                type.EqualsASCII(sXFormsEventsEntries[eEvent_Previous].name)) { 
00556 
00557       // only continue this processing if xforms-next or xforms-previous were
00558       // dispatched by the form and not as part of the 'tab' and 'shift+tab'
00559       // processing
00560       if (mPreventLoop) {
00561         mPreventLoop = PR_FALSE;
00562         return NS_OK;
00563       }
00564 
00565       nsCOMPtr<nsIDOMDocument> domDoc;
00566       mElement->GetOwnerDocument(getter_AddRefs(domDoc));
00567 
00568       nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
00569       // element isn't in a document, yet?  Odd, indeed.  Well, if not in
00570       // document, these two events have no meaning.
00571       NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
00572     
00573       // An inelegant way to retrieve this to be sure, but we are
00574       // guaranteed that the focus controller outlives us, so it
00575       // is safe to hold on to it (since we can't die until it has
00576       // died).
00577       nsIFocusController *focusController =
00578         doc->GetWindow()->GetRootFocusController();
00579       if (focusController &&
00580           type.EqualsASCII(sXFormsEventsEntries[eEvent_Next].name)) {
00581         focusController->MoveFocus(PR_TRUE, nsnull);
00582       } else {
00583         focusController->MoveFocus(PR_FALSE, nsnull);
00584       }
00585     } else if (type.EqualsASCII(sXFormsEventsEntries[eEvent_BindingException].name)) {
00586       // we threw up a popup during the nsXFormsUtils::DispatchEvent that sent
00587       // this error to this control
00588       *aHandled = PR_TRUE;
00589     }
00590   }
00591   
00592   return NS_OK;
00593 }
00594 
00595 #ifdef DEBUG_smaug
00596 static nsVoidArray* sControlList = nsnull;
00597 class ControlDebug
00598 {
00599 public:
00600   ControlDebug() {
00601     sControlList = new nsVoidArray();
00602     NS_ASSERTION(sControlList, "Out of memory!");
00603   }
00604 
00605   ~ControlDebug() {
00606     for (PRInt32 i = 0; i < sControlList->Count(); ++i) {
00607       nsXFormsControlStubBase* control =
00608         NS_STATIC_CAST(nsXFormsControlStubBase*, sControlList->ElementAt(i));
00609       if (control) {
00610         printf("Possible leak, <xforms:%s>\n", control->Name());
00611       }
00612     }
00613     delete sControlList;
00614     sControlList = nsnull;
00615   }
00616 };
00617 
00618 static ControlDebug tester = ControlDebug();
00619 #endif
00620 
00621 nsresult
00622 nsXFormsControlStubBase::Create(nsIXTFElementWrapper *aWrapper)
00623 {
00624   aWrapper->SetNotificationMask(kStandardNotificationMask);
00625 
00626   // It's ok to keep a weak pointer to mElement.  mElement will have an
00627   // owning reference to this object, so as long as we null out mElement in
00628   // OnDestroyed, it will always be valid.
00629   nsCOMPtr<nsIDOMElement> node;
00630   aWrapper->GetElementNode(getter_AddRefs(node));
00631   mElement = node;
00632   NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon");
00633 
00634 #ifdef DEBUG_smaug
00635   sControlList->AppendElement(this);
00636 #endif
00637 
00638   return NS_OK;
00639 }
00640 
00641 nsresult
00642 nsXFormsControlStubBase::OnDestroyed()
00643 {
00644   RemoveIndexListeners();
00645   mDependencies.Clear();
00646 
00647   if (mModel) {
00648     mModel->RemoveFormControl(this);
00649     mModel = nsnull;
00650   }
00651 
00652   mAbortedBindListContainer = nsnull;
00653 
00654 #ifdef DEBUG_smaug
00655   sControlList->RemoveElement(this);
00656 #endif
00657 
00658   mElement = nsnull;
00659   return NS_OK;
00660 }
00661 
00662 nsresult
00663 nsXFormsControlStubBase::ForceModelDetach(PRBool aRebind)
00664 {
00665   // We shouldn't bother binding if the control is part of a template, but we
00666   // need to run through RemoveFormControl in the event that this control
00667   // was just moved under a repeat or itemset and wasn't there originally.
00668   if (mModel) {
00669     // Remove from model, so Bind() will be forced to reattach
00670     mModel->RemoveFormControl(this);
00671     mModel = nsnull;
00672   }
00673 
00674   if (!aRebind || GetRepeatState() == eType_Template) {
00675     return NS_OK;
00676   }
00677 
00678   PRBool dummy;
00679   nsresult rv = Bind(&dummy);
00680   NS_ENSURE_SUCCESS(rv, rv);
00681 
00682   if (rv == NS_OK_XFORMS_DEFERRED || rv == NS_OK_XFORMS_NOTREADY) {
00683     return NS_OK;
00684   }
00685 
00686   // If there were any controls that had already tried to bind but failed
00687   // to because this control wasn't ready yet, then try them again.
00688   PRInt32 arraySize = mAbortedBindList.Count();
00689   if (arraySize) {
00690     for (PRInt32 i = 0; i < arraySize; ++i) {
00691       nsCOMPtr<nsIXFormsControl> control = mAbortedBindList.ObjectAt(i);
00692       if (control) {
00693         control->RebindAndRefresh();
00694         control->SetAbortedBindListContainer(nsnull);
00695       }
00696     }
00697   
00698     mAbortedBindList.Clear();
00699   }
00700 
00701   return Refresh();
00702 }
00703 
00704 nsresult
00705 nsXFormsControlStubBase::WillChangeDocument(nsIDOMDocument *aNewDocument)
00706 {
00707   // This control is moving to another document or just plain getting removed
00708   // from its current document.  In either case, we know that we don't care
00709   // about the bind-ready notification we might have been waiting on.
00710   nsCOMPtr<nsIXFormsContextControl> ctxtControl;
00711   GetAbortedBindListContainer(getter_AddRefs(ctxtControl));
00712   if (ctxtControl) {
00713     ctxtControl->AddRemoveAbortedControl(this, PR_FALSE);
00714   }
00715 
00716   // If we are in the middle of getting set up in the current document then
00717   // then we need to make sure that if other controls getting set up in
00718   // the current document are depending on this control to help them, that
00719   // we remove that dependency.
00720   PRInt32 arraySize = mAbortedBindList.Count();
00721   if (arraySize) {
00722     for (PRInt32 i = 0; i < arraySize; ++i) {
00723       nsCOMPtr<nsIXFormsControl> control = mAbortedBindList.ObjectAt(i);
00724       control->SetAbortedBindListContainer(nsnull);
00725     }
00726 
00727     mAbortedBindList.Clear();
00728   }
00729 
00730   SetRepeatState(eType_Unknown);
00731   ResetHelpAndHint(PR_FALSE);
00732   return NS_OK;
00733 }
00734 
00735 nsresult
00736 nsXFormsControlStubBase::DocumentChanged(nsIDOMDocument *aNewDocument)
00737 {
00738   mHasDoc = aNewDocument != nsnull;
00739 
00740   if (aNewDocument) {
00741     ResetHelpAndHint(PR_TRUE);
00742 
00743     // If we are inserted into a document and we have no model, we are probably
00744     // being initialized, so we should set our intrinsic state to the default
00745     // value
00746     if (!mModel && mElement) {
00747       nsCOMPtr<nsIXTFElementWrapper> xtfWrap(do_QueryInterface(mElement));
00748       NS_ENSURE_STATE(xtfWrap);
00749       PRInt32 iState;
00750       GetDefaultIntrinsicState(&iState);
00751       xtfWrap->SetIntrinsicState(iState);
00752     }
00753   }
00754 
00755   nsCOMPtr<nsIDOMNode> parent;
00756   mElement->GetParentNode(getter_AddRefs(parent));
00757   UpdateRepeatState(parent);
00758 
00759   return ForceModelDetach(mHasParent && mHasDoc);
00760 }
00761 
00762 nsresult
00763 nsXFormsControlStubBase::WillChangeParent(nsIDOMElement *aNewParent)
00764 {
00765   nsCOMPtr<nsIXFormsContextControl> ctxtControl;
00766   GetAbortedBindListContainer(getter_AddRefs(ctxtControl));
00767   if (ctxtControl) {
00768     ctxtControl->AddRemoveAbortedControl(this, PR_FALSE);
00769   }
00770 
00771   SetRepeatState(eType_Unknown);
00772   return NS_OK;
00773 }
00774 
00775 
00776 nsresult
00777 nsXFormsControlStubBase::ParentChanged(nsIDOMElement *aNewParent)
00778 {
00779   mHasParent = aNewParent != nsnull;
00780 
00781   UpdateRepeatState(aNewParent);
00782 
00783   // We need to re-evaluate our instance data binding when our parent changes,
00784   // since xmlns declarations or our context could have changed.
00785   return ForceModelDetach(mHasParent && mHasDoc);
00786 }
00787 
00788 nsresult
00789 nsXFormsControlStubBase::WillSetAttribute(nsIAtom *aName, const nsAString &aValue)
00790 {
00791   BeforeSetAttribute(aName, aValue);
00792   return NS_OK;
00793 }
00794 
00795 nsresult
00796 nsXFormsControlStubBase::AttributeSet(nsIAtom *aName, const nsAString &aValue)
00797 {
00798   AfterSetAttribute(aName);
00799   return NS_OK;
00800 }
00801 
00802 nsresult
00803 nsXFormsControlStubBase::WillRemoveAttribute(nsIAtom *aName)
00804 {
00805   BeforeSetAttribute(aName, EmptyString());
00806   return NS_OK;
00807 }
00808 
00809 nsresult
00810 nsXFormsControlStubBase::AttributeRemoved(nsIAtom *aName)
00811 {
00812   AfterSetAttribute(aName);
00813   return NS_OK;
00814 }
00815 
00816 NS_IMETHODIMP
00817 nsXFormsControlStubBase::RebindAndRefresh()
00818 {
00819   ForceModelDetach(PR_TRUE);
00820   return NS_OK;
00821 }
00822 
00823 // nsIXFormsContextControl
00824 
00825 NS_IMETHODIMP
00826 nsXFormsControlStubBase::SetContext(nsIDOMNode *aContextNode,
00827                                     PRInt32     aContextPosition,
00828                                     PRInt32     aContextSize)
00829 {
00830   return NS_ERROR_NOT_IMPLEMENTED;
00831 }
00832 
00833 NS_IMETHODIMP
00834 nsXFormsControlStubBase::GetContext(nsAString      &aModelID,
00835                                     nsIDOMNode    **aContextNode,
00836                                     PRInt32        *aContextPosition,
00837                                     PRInt32        *aContextSize)
00838 {
00839   NS_ENSURE_ARG(aContextSize);
00840   NS_ENSURE_ARG(aContextPosition);
00841   NS_ENSURE_ARG_POINTER(aContextNode);
00842 
00843   *aContextPosition = 1;
00844   *aContextSize = 1;
00845   *aContextNode = nsnull;
00846 
00847   if (aContextNode) {
00848     if (mBoundNode) {
00849       // We are bound to a node: This is the context node
00850       NS_ADDREF(*aContextNode = mBoundNode);
00851     } else if (mModel) {
00852       // If there is no bound node, it could be because everything isn't ready,
00853       // yet.  If mHasDoc or mHasParent are false, then we haven't had a chance
00854       // to bind.  This could happen if the nodes in this chain are being added
00855       // via DOM manipulation rather than via the parser.
00856       if (HasBindingAttribute() && (!mHasDoc || !mHasParent)) {
00857         return NS_OK_XFORMS_NOTREADY;
00858       }
00859 
00860       // We are bound to a model: The document element of its default instance
00861       // document is the context node
00862       nsCOMPtr<nsIDOMDocument> instanceDoc;
00863       mModel->GetInstanceDocument(EmptyString(), getter_AddRefs(instanceDoc));
00864       NS_ENSURE_STATE(instanceDoc);
00865 
00866       nsIDOMElement* docElement;
00867       instanceDoc->GetDocumentElement(&docElement); // addrefs
00868       NS_ENSURE_STATE(docElement);
00869       *aContextNode = docElement; // addref'ed above
00870     } else {
00871       if (HasBindingAttribute() && (!mHasDoc || !mHasParent)) {
00872         return NS_OK_XFORMS_NOTREADY;
00873       }
00874     }
00875   }
00876 
00879   nsCOMPtr<nsIDOMElement> model = do_QueryInterface(mModel);
00880   if (model) {
00881     model->GetAttribute(NS_LITERAL_STRING("id"), aModelID);
00882   }
00883   
00884   return NS_OK;
00885 }
00886 
00887 NS_IMETHODIMP
00888 nsXFormsControlStubBase::AddRemoveAbortedControl(nsIXFormsControl *aControl,
00889                                              PRBool            aAdd)
00890 {
00891   nsresult rv = NS_OK;
00892   if (aAdd) {
00893     rv = mAbortedBindList.AppendObject(aControl);
00894     aControl->SetAbortedBindListContainer(this);
00895   } else {
00896     rv = mAbortedBindList.RemoveObject(aControl);
00897     aControl->SetAbortedBindListContainer(nsnull);
00898   }
00899   return rv;
00900 }
00901 
00902 NS_IMETHODIMP
00903 nsXFormsControlStubBase::GetAbortedBindListContainer(
00904   nsIXFormsContextControl **aControlContainingList)
00905 {
00906   NS_ENSURE_ARG_POINTER(aControlContainingList);
00907 
00908   NS_IF_ADDREF(*aControlContainingList = mAbortedBindListContainer);
00909   return NS_OK;
00910 }
00911 
00912 NS_IMETHODIMP
00913 nsXFormsControlStubBase::SetAbortedBindListContainer(
00914   nsIXFormsContextControl *aControlContainingList)
00915 {
00916   // A control should never be on more than one aborted bind list.  There is
00917   // probably a chance through some crazy DOM stirring for a control to be
00918   // waiting on notification from a context control and then the control finds
00919   // itself trying to bind again under a whole different parent tree.  So we'll
00920   // have the control remove itself from the old list and link itself to the
00921   // new list.
00922 
00923   if (mAbortedBindListContainer && aControlContainingList) {
00924     if (!SameCOMIdentity(mAbortedBindListContainer, aControlContainingList)) {
00925       mAbortedBindListContainer->AddRemoveAbortedControl(this, PR_FALSE);
00926     }
00927   }
00928 
00929   mAbortedBindListContainer = aControlContainingList;
00930   return NS_OK;
00931 }
00932 
00933 void
00934 nsXFormsControlStubBase::AddRemoveSNBAttr(nsIAtom *aName, const nsAString &aValue) 
00935 {
00936   nsAutoString attrStr, attrValue;
00937   aName->ToString(attrStr);
00938   mElement->GetAttribute(attrStr, attrValue);
00939 
00940   // if we are setting a single node binding attribute that we don't already
00941   // have, bump the count.
00942   if (!aValue.IsEmpty() && attrValue.IsEmpty()) {
00943     ++mBindAttrsCount;
00944   } else if (!attrValue.IsEmpty()) { 
00945     // if we are setting a currently existing binding attribute to have an
00946     // empty value, treat it like the binding attr is being removed.
00947     --mBindAttrsCount;
00948     NS_ASSERTION(mBindAttrsCount>=0, "bad mojo!  mBindAttrsCount < 0!");
00949   }
00950 }
00951 
00952 PRBool
00953 nsXFormsControlStubBase::IsBindingAttribute(const nsIAtom *aAttr) const
00954 {
00955   if (aAttr == nsXFormsAtoms::bind ||
00956       aAttr == nsXFormsAtoms::ref  ||
00957       aAttr == nsXFormsAtoms::model) {
00958     return PR_TRUE;
00959   }
00960   
00961   return PR_FALSE;
00962 }
00963 
00964 void
00965 nsXFormsControlStubBase::AfterSetAttribute(nsIAtom *aName)
00966 {
00967   if (IsBindingAttribute(aName)) {
00968     PRBool dummy;
00969     nsresult rv = Bind(&dummy);
00970     if (NS_SUCCEEDED(rv) &&
00971         rv != NS_OK_XFORMS_DEFERRED && rv != NS_OK_XFORMS_NOTREADY)
00972       Refresh();
00973   }
00974 
00975 }
00976 
00977 void
00978 nsXFormsControlStubBase::BeforeSetAttribute(nsIAtom         *aName,
00979                                             const nsAString &aValue)
00980 {
00981   if (IsBindingAttribute(aName)) {
00982     AddRemoveSNBAttr(aName, aValue);
00983   }
00984 }
00985 
00986 nsresult
00987 nsXFormsControlStubBase::GetBoundBuiltinType(PRUint16 *aBuiltinType)
00988 {
00989   NS_ENSURE_ARG_POINTER(aBuiltinType);
00990   *aBuiltinType = 0;
00991 
00992   NS_ENSURE_STATE(mModel);
00993 
00994   // get type of bound instance data
00995   nsCOMPtr<nsISchemaType> schemaType;
00996   nsresult rv = mModel->GetTypeForControl(this, getter_AddRefs(schemaType));
00997   NS_ENSURE_SUCCESS(rv, rv);
00998 
00999   return mModel->GetRootBuiltinType(schemaType, aBuiltinType);
01000 }
01001 
01002 PRBool
01003 nsXFormsControlStubBase::IsContentAllowed()
01004 {
01005   return PR_TRUE;
01006 }
01007 
01008 PRBool
01009 nsXFormsControlStubBase::IsContentComplex()
01010 {
01011   PRBool isComplex = PR_FALSE;
01012 
01013   if (mBoundNode) {
01014     // If the bound node has any Element children, then it has complexContent.
01015     nsCOMPtr<nsIDOMNode> currentNode;
01016     mBoundNode->GetFirstChild(getter_AddRefs(currentNode));
01017     while (currentNode) {
01018       PRUint16 nodeType;
01019       currentNode->GetNodeType(&nodeType);
01020       if (nodeType == nsIDOMNode::ELEMENT_NODE) {
01021         isComplex = PR_TRUE;
01022         break;
01023       }
01024 
01025       nsCOMPtr<nsIDOMNode> node;
01026       currentNode->GetNextSibling(getter_AddRefs(node));
01027       currentNode.swap(node);
01028     }
01029   }
01030   return isComplex;
01031 }
01032 
01033 nsRepeatState
01034 nsXFormsControlStubBase::GetRepeatState()
01035 {
01036   return mRepeatState;
01037 }
01038 
01039 void
01040 nsXFormsControlStubBase::SetRepeatState(nsRepeatState aState)
01041 {
01042   mRepeatState = aState;
01043   return;
01044 }
01045 
01046 nsRepeatState
01047 nsXFormsControlStubBase::UpdateRepeatState(nsIDOMNode *aParent)
01048 {
01049   // Walk up the parent chain looking to see if the this control is contained
01050   // in an item.  If it is and that item is contained in a itemset, then we
01051   // know that this control was generated as a clone from the itemset's
01052   // template.  Similarly, we'll check to see if this control lives in a
01053   // contextcontainer (meaning it was cloned from a repeat's template).
01054   // Otherwise, if neither of these are the case but it lives under a repeat
01055   // or an itemset, then this control must be part of a template.  A template
01056   // is the content of a repeat or itemset that gets cloned once for every
01057   // node in the bound nodeset.
01058   //
01059   // If none of this applies, we'll return eType_NotApplicable to show that this
01060   // control isn't bound to a repeating nodeset.
01061   nsRepeatState repeatState = eType_NotApplicable;
01062 
01063   if (!mHasDoc || !mHasParent) {
01064     // If we don't have a document or a parent, none of these tests will work
01065     // correctly so no sense doing them now.  If either of these are false the
01066     // repeat state for the object should already be eType_Unknown so just
01067     // return that now.
01068     return eType_Unknown;
01069   }
01070 
01071   nsCOMPtr<nsIDOMNode> parent = aParent;
01072   PRBool childIsItem = PR_FALSE;
01073   while (parent) {
01074     if (nsXFormsUtils::IsXFormsElement(parent,
01075                                        NS_LITERAL_STRING("contextcontainer"))) {
01076       repeatState = eType_GeneratedContent;
01077       break;
01078     }
01079     if (nsXFormsUtils::IsXFormsElement(parent, NS_LITERAL_STRING("repeat"))) {
01080       repeatState = eType_Template;
01081       break;
01082     }
01083     if (nsXFormsUtils::IsXFormsElement(parent, NS_LITERAL_STRING("itemset"))) {
01084       if (childIsItem) {
01085         repeatState = eType_GeneratedContent;
01086         break;
01087       }
01088       repeatState = eType_Template;
01089       break;
01090     }
01091 
01092     if (nsXFormsUtils::IsXFormsElement(parent, NS_LITERAL_STRING("item"))) {
01093       childIsItem = PR_TRUE;
01094     } else {
01095 
01096       nsCOMPtr<nsIDOMElement> parentEle(do_QueryInterface(parent));
01097       if (!parentEle) {
01098         // I don't know how this can possibly happen, but if it does I guess
01099         // we should just ignore it and coninue on our way.
01100         break;
01101       }
01102 
01103       // if this control is contained underneath an element that contains
01104       // an xforms binding attribute that introduces an anonymous xf:repeat
01105       // then the control is part of a template
01106       PRBool repeatAttr = PR_FALSE;
01107       parentEle->HasAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
01108                                 NS_LITERAL_STRING("repeat-bind"),
01109                                 &repeatAttr);
01110       if (repeatAttr) {
01111         repeatState = eType_Template;
01112         break;
01113       }
01114 
01115       parentEle->HasAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
01116                                 NS_LITERAL_STRING("repeat-nodeset"),
01117                                 &repeatAttr);
01118       if (repeatAttr) {
01119         repeatState = eType_Template;
01120         break;
01121       }
01122     }
01123     nsCOMPtr<nsIDOMNode> tmp;
01124     parent->GetParentNode(getter_AddRefs(tmp));
01125     parent = tmp;
01126   }
01127 
01128   SetRepeatState(repeatState);
01129   return repeatState;
01130 }
01131 
01132 NS_IMPL_ISUPPORTS_INHERITED3(nsXFormsBindableControlStub,
01133                              nsXFormsBindableStub,
01134                              nsIXFormsContextControl,
01135                              nsIXFormsControl,
01136                              nsIXFormsControlBase)
01137