Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsMessageElement.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  * Olli Pettay.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Olli Pettay <Olli.Pettay@helsinki.fi> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsXFormsAtoms.h"
00040 #include "nsXFormsStubElement.h"
00041 #include "nsXFormsDelegateStub.h"
00042 #include "nsXFormsActionElement.h"
00043 #include "nsIXFormsActionModuleElement.h"
00044 #include "nsXFormsActionModuleBase.h"
00045 
00046 #include "nsIDOMText.h"
00047 #include "nsIDOM3Node.h"
00048 #include "nsIDOMElement.h"
00049 #include "nsIDOMNodeList.h"
00050 #include "nsIDOMDocument.h"
00051 #include "nsIDOMNSDocument.h"
00052 #include "nsIDOMDocumentView.h"
00053 #include "nsIDOMAbstractView.h"
00054 #include "nsIDOMHTMLDocument.h"
00055 #include "nsIDOMWindowInternal.h"
00056 #include "nsIDOMDOMImplementation.h"
00057 
00058 #include "nsIDOMEvent.h"
00059 #include "nsIDOMMouseEvent.h"
00060 #include "nsIDOMEventTarget.h"
00061 #include "nsIDOMEventListener.h"
00062 
00063 #include "nsIDOMViewCSS.h"
00064 #include "nsIDOMCSSValue.h"
00065 #include "nsIDOMCSSPrimitiveValue.h"
00066 #include "nsIDOMCSSStyleDeclaration.h"
00067 
00068 #include "nsITimer.h"
00069 #include "nsIDocument.h"
00070 #include "nsIBoxObject.h"
00071 #include "nsIServiceManager.h"
00072 
00073 #include "prmem.h"
00074 #include "plbase64.h"
00075 #include "nsAutoPtr.h"
00076 #include "nsIStringBundle.h"
00077 #include "nsIDOMSerializer.h"
00078 #include "nsIServiceManager.h"
00079 #include "nsIDelegateInternal.h"
00080 #include "nsISupportsArray.h"
00081 #include "nsISupportsPrimitives.h"
00082 #include "nsNetUtil.h"
00083 #include "nsIDOMDocumentEvent.h"
00084 #include "nsIChannelEventSink.h"
00085 #include "nsIXFormsEphemeralMessageUI.h"
00086 #include "nsIContent.h"
00087 
00088 #define MESSAGE_WINDOW_PROPERTIES \
00089   "centerscreen,chrome,dependent,dialog"
00090 
00091 #define MESSAGE_WINDOW_URL \
00092   "chrome://xforms/content/xforms-message.xul"
00093 
00094 // Defining a simple dialog for modeless and modal messages.
00095 
00096 #define SHOW_EPHEMERAL_TIMEOUT           750
00097 #define HIDE_EPHEMERAL_TIMEOUT           5000
00098 #define EPHEMERAL_POSITION_RESET_TIMEOUT 100
00099 
00100 class nsXFormsEventListener;
00106 class nsXFormsMessageElement : public nsXFormsDelegateStub,
00107                                public nsIDOMEventListener,
00108                                public nsIXFormsActionModuleElement,
00109                                public nsIStreamListener,
00110                                public nsIInterfaceRequestor,
00111                                public nsIChannelEventSink,
00112                                public nsXFormsActionModuleHelper
00113 {
00114 public:
00115   NS_DECL_ISUPPORTS_INHERITED
00116 
00117   // nsIXFormsDelegate
00118   NS_IMETHOD GetValue(nsAString& aValue);
00119 
00120   // nsIXTFElement overrides
00121   NS_IMETHOD OnCreated(nsIXTFBindableElementWrapper *aWrapper);
00122   NS_IMETHOD WillChangeDocument(nsIDOMDocument *aNewDocument);
00123   NS_IMETHOD OnDestroyed();
00124   NS_IMETHOD ParentChanged(nsIDOMElement *aNewParent);
00125   NS_IMETHOD WillChangeParent(nsIDOMElement *aNewParent);
00126   NS_IMETHOD AttributeSet(nsIAtom *aName, const nsAString &aSrc);
00127   NS_IMETHOD AttributeRemoved(nsIAtom *aName);
00128   NS_IMETHOD DoneAddingChildren();
00129 
00130   NS_DECL_NSICHANNELEVENTSINK
00131   NS_DECL_NSIREQUESTOBSERVER
00132   NS_DECL_NSISTREAMLISTENER
00133   NS_DECL_NSIINTERFACEREQUESTOR
00134   NS_DECL_NSIDOMEVENTLISTENER
00135   NS_DECL_NSIXFORMSACTIONMODULEELEMENT
00136 
00137   virtual nsIDOMElement* GetElement() { return mElement; }
00138   virtual nsresult HandleSingleAction(nsIDOMEvent *aEvent,
00139                                       nsIXFormsActionElement *aParentAction);
00140   // Start the timer, which is used to set the message visible
00141   void StartEphemeral();
00142   // Set the message visible and start timer to hide it later.
00143   void ShowEphemeral();
00144   // Hide the ephemeral message.
00145   void HideEphemeral();
00146   // Reset the position of the ephemeral message.
00147   void ResetEphemeralPosition()
00148   {
00149     mPosX = mPosY = -1;
00150   }
00151 
00152   enum MessageType {
00153     eType_Normal,
00154     eType_Hint,
00155     eType_Help,
00156     eType_Alert
00157   };
00158 
00159   enum StopType {
00160     eStopType_None,
00161     eStopType_Security,
00162     eStopType_LinkError
00163   };
00164 
00165 
00166   nsXFormsMessageElement(MessageType aType) :
00167     mType(aType), mPosX(-1), mPosY(-1), mDocument(nsnull),
00168     mStopType(eStopType_None), mSrcAttrText(""),
00169     mDoneAddingChildren(PR_FALSE) {}
00170 private:
00171   nsresult HandleEphemeralMessage(nsIDOMDocument* aDoc, nsIDOMEvent* aEvent);
00172   nsresult HandleModalAndModelessMessage(nsIDOMDocument* aDoc, nsAString& aLevel);
00173   void ImportNode(nsIDOMNode* aSrc, nsIDOMDocument* aDestDoc,
00174                   nsIDOMNode** aTarget);
00175   PRBool HandleInlineAlert(nsIDOMEvent* aEvent);
00176   nsresult ConstructMessageWindowURL(const nsAString& aData,
00177                                      PRBool aIsLink,
00178                                      /*out*/ nsAString& aURL);
00179 
00187   nsresult TestExternalFile();
00188 
00194   void AddRemoveExternalResource(PRBool aAdd);
00195 
00199   PRBool IsEphemeral();
00200 
00206   nsresult SetContextInfo(const char *aName, const nsAString &aValue);
00207 
00208   MessageType          mType;
00209 
00210   // The position of the ephemeral message
00211   PRInt32              mPosX;
00212   PRInt32              mPosY;
00213 
00214   nsCOMPtr<nsITimer>   mEphemeralTimer;
00215   nsIDOMDocument*      mDocument;
00216   nsCOMPtr<nsIChannel> mChannel;
00217   StopType             mStopType;
00218   nsCString            mSrcAttrText;
00219   PRBool               mDoneAddingChildren;
00220   // Context Info for events.
00221   nsCOMArray<nsIXFormsContextInfo> mContextInfo;
00222   nsString             mSrc;
00223 };
00224 
00225 NS_IMPL_ADDREF_INHERITED(nsXFormsMessageElement, nsXFormsDelegateStub)
00226 NS_IMPL_RELEASE_INHERITED(nsXFormsMessageElement, nsXFormsDelegateStub)
00227 
00228 NS_INTERFACE_MAP_BEGIN(nsXFormsMessageElement)
00229   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
00230   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
00231   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00232   NS_INTERFACE_MAP_ENTRY(nsIXFormsActionModuleElement)
00233   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
00234 NS_INTERFACE_MAP_END_INHERITING(nsXFormsDelegateStub)
00235 
00236 // nsIXTFElement
00237 
00238 NS_IMETHODIMP
00239 nsXFormsMessageElement::OnCreated(nsIXTFBindableElementWrapper *aWrapper)
00240 {
00241   nsresult rv = nsXFormsDelegateStub::OnCreated(aWrapper);
00242   NS_ENSURE_SUCCESS(rv, rv);
00243   
00244   aWrapper->SetNotificationMask(kStandardNotificationMask |
00245                                 nsIXTFElement::NOTIFY_WILL_CHANGE_PARENT |
00246                                 nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN);
00247   return NS_OK;
00248 }
00249 
00250 NS_IMETHODIMP
00251 nsXFormsMessageElement::WillChangeDocument(nsIDOMDocument *aNewDocument)
00252 {
00253   if (mDocument) {
00254     if (mEphemeralTimer) {
00255       mEphemeralTimer->Cancel();
00256       mEphemeralTimer = nsnull;
00257     }
00258 
00259     nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
00260     if (doc) {
00261       nsXFormsMessageElement *msg =
00262         NS_STATIC_CAST(nsXFormsMessageElement*,
00263                        doc->GetProperty(nsXFormsAtoms::messageProperty));
00264       if (msg == this)
00265         doc->UnsetProperty(nsXFormsAtoms::messageProperty);
00266     }
00267 
00268     // If we are currently trying to load an external message, cancel the
00269     // request.
00270     if (mChannel) {
00271       mChannel->Cancel(NS_BINDING_ABORTED);
00272     }
00273   }
00274 
00275   mDocument = aNewDocument;
00276   return nsXFormsDelegateStub::WillChangeDocument(aNewDocument);
00277 }
00278 
00279 NS_IMETHODIMP
00280 nsXFormsMessageElement::OnDestroyed()
00281 {
00282   mChannel = nsnull;
00283   mDocument = nsnull;
00284   return nsXFormsDelegateStub::OnDestroyed();
00285 }
00286 
00287 NS_IMETHODIMP
00288 nsXFormsMessageElement::HandleEvent(nsIDOMEvent* aEvent)
00289 {
00290   if (GetRepeatState() == eType_Template) {
00291     return NS_OK;
00292   }
00293 
00294   nsCOMPtr<nsIDOMEventTarget> target;
00295   aEvent->GetTarget(getter_AddRefs(target));
00296   nsCOMPtr<nsIContent> content(do_QueryInterface(target));
00297 
00298   return nsXFormsUtils::EventHandlingAllowed(aEvent, mElement) ?
00299            HandleAction(aEvent, nsnull) : NS_OK;
00300 }
00301 
00302 void
00303 nsXFormsMessageElement::ImportNode(nsIDOMNode* aSrc, nsIDOMDocument* aDestDoc,
00304                                    nsIDOMNode** aTarget)
00305 {
00306   nsAutoString ns;
00307   nsAutoString localName;
00308   aSrc->GetNamespaceURI(ns);
00309   aSrc->GetLocalName(localName);
00310   // Clone the visual content of the <output>.
00311   // According to the XForms Schema it is enough 
00312   // to support <output> here.
00313   if (ns.EqualsLiteral(NS_NAMESPACE_XFORMS) &&
00314       localName.EqualsLiteral("output")) {
00315     nsCOMPtr<nsIDelegateInternal> outEl(do_QueryInterface(aSrc));
00316     if (outEl) {
00317       nsCOMPtr<nsIDOMText> text;
00318       nsAutoString value;
00319       outEl->GetValue(value);
00320       aDestDoc->CreateTextNode(value, getter_AddRefs(text));
00321       NS_IF_ADDREF(*aTarget = text);
00322     }
00323     return;
00324   }
00325 
00326   // Clone other elements
00327   aDestDoc->ImportNode(aSrc, PR_FALSE, aTarget);
00328 
00329   if (!*aTarget)
00330     return;
00331 
00332   // Add the new children
00333   nsCOMPtr<nsIDOMNode> tmp;
00334   nsCOMPtr<nsIDOMNodeList> childNodes;
00335   aSrc->GetChildNodes(getter_AddRefs(childNodes));
00336 
00337   PRUint32 count = 0;
00338   if (childNodes)
00339     childNodes->GetLength(&count);
00340 
00341   for (PRUint32 i = 0; i < count; ++i) {
00342     nsCOMPtr<nsIDOMNode> child;
00343     childNodes->Item(i, getter_AddRefs(child));
00344     
00345     if (child) {
00346       nsCOMPtr<nsIDOMNode> clone;
00347       ImportNode(child, aDestDoc, getter_AddRefs(clone));
00348       if (clone)
00349         (*aTarget)->AppendChild(clone, getter_AddRefs(tmp));
00350     }
00351   }
00352 }
00353 
00354 NS_IMETHODIMP
00355 nsXFormsMessageElement::WillChangeParent(nsIDOMElement *aNewParent)
00356 {
00357   if (mType == eType_Normal)
00358     return nsXFormsDelegateStub::WillChangeParent(aNewParent);
00359   
00360   nsCOMPtr<nsIDOMNode> parent;
00361   mElement->GetParentNode(getter_AddRefs(parent));
00362   if (!parent)
00363     return nsXFormsDelegateStub::WillChangeParent(aNewParent);
00364 
00365   nsCOMPtr<nsIDOMEventTarget> targ(do_QueryInterface(parent));
00366   NS_ENSURE_STATE(targ);
00367   
00368   if (mType == eType_Hint) {
00369     targ->RemoveEventListener(NS_LITERAL_STRING("xforms-hint"), this, PR_FALSE);
00370     targ->RemoveEventListener(NS_LITERAL_STRING("xforms-moz-hint-off"),
00371                               this, PR_FALSE);
00372   } else if (mType == eType_Help) {
00373     targ->RemoveEventListener(NS_LITERAL_STRING("xforms-help"), this, PR_FALSE);      
00374   } else if (mType == eType_Alert) {
00375     targ->RemoveEventListener(NS_LITERAL_STRING("xforms-invalid"), this, PR_TRUE);
00376     targ->RemoveEventListener(NS_LITERAL_STRING("xforms-out-of-range"), this, PR_TRUE);
00377     targ->RemoveEventListener(NS_LITERAL_STRING("xforms-binding-exception"),this, PR_TRUE);
00378   }
00379 
00380   return nsXFormsDelegateStub::WillChangeParent(aNewParent);
00381 }
00382 
00383 NS_IMETHODIMP
00384 nsXFormsMessageElement::ParentChanged(nsIDOMElement *aNewParent)
00385 {
00386   if (mType == eType_Normal || !aNewParent)
00387     return nsXFormsDelegateStub::ParentChanged(aNewParent);
00388 
00389   nsCOMPtr<nsIDOMEventTarget> targ(do_QueryInterface(aNewParent));
00390   NS_ENSURE_STATE(targ);
00391 
00392   if (mType == eType_Hint) {
00393     targ->AddEventListener(NS_LITERAL_STRING("xforms-hint"), this, PR_FALSE);
00394     targ->AddEventListener(NS_LITERAL_STRING("xforms-moz-hint-off"),
00395                            this, PR_FALSE);
00396   } else if (mType == eType_Help) {
00397     targ->AddEventListener(NS_LITERAL_STRING("xforms-help"), this, PR_FALSE);
00398   } else if (mType == eType_Alert) {
00399     // Adding listeners for error events, which have a form control as a target.
00400     targ->AddEventListener(NS_LITERAL_STRING("xforms-invalid"), this, PR_TRUE);
00401     targ->AddEventListener(NS_LITERAL_STRING("xforms-out-of-range"), this, PR_TRUE);
00402     targ->AddEventListener(NS_LITERAL_STRING("xforms-binding-exception"), this, PR_TRUE);
00403   }
00404 
00405   return nsXFormsDelegateStub::ParentChanged(aNewParent);
00406 }
00407 
00408 NS_IMETHODIMP
00409 nsXFormsMessageElement::AttributeSet(nsIAtom *aName, const nsAString &aValue)
00410 {
00411   if (mDoneAddingChildren) {
00412     if (aName == nsXFormsAtoms::src) {
00413       // If we are currently trying to load an external message, cancel the
00414       // request.
00415       if (mChannel) {
00416         mChannel->Cancel(NS_BINDING_ABORTED);
00417       }
00418 
00419       mStopType = eStopType_None;
00420 
00421       // The src attribute has changed so retest the external file.
00422       TestExternalFile();
00423     }
00424   }
00425 
00426   return nsXFormsDelegateStub::AttributeSet(aName, aValue);
00427 }
00428 
00429 NS_IMETHODIMP
00430 nsXFormsMessageElement::AttributeRemoved(nsIAtom *aName)
00431 {
00432   if (mDoneAddingChildren) {
00433     if (aName == nsXFormsAtoms::src) {
00434       // If we are currently trying to test an external resource, cancel the
00435       // request.
00436       if (mChannel) {
00437         mChannel->Cancel(NS_BINDING_ABORTED);
00438       }
00439 
00440       mSrcAttrText.Truncate();
00441       mStopType = eStopType_None;
00442     }
00443   }
00444 
00445   return nsXFormsDelegateStub::AttributeRemoved(aName);
00446 }
00447 
00448 NS_IMETHODIMP
00449 nsXFormsMessageElement::DoneAddingChildren()
00450 {
00451   mDoneAddingChildren = PR_TRUE;
00452   TestExternalFile();
00453 
00454   return NS_OK;
00455 }
00456 
00457 NS_IMETHODIMP
00458 nsXFormsMessageElement::HandleAction(nsIDOMEvent *aEvent,
00459                                      nsIXFormsActionElement *aParentAction)
00460 {
00461   return nsXFormsActionModuleBase::DoHandleAction(this, aEvent, aParentAction);
00462 }
00463 
00464 nsresult
00465 nsXFormsMessageElement::HandleSingleAction(nsIDOMEvent *aEvent,
00466                                            nsIXFormsActionElement *aParentAction)
00467 {
00468   // If TestExternalFile fails, then there is an external link that we need
00469   // to use that we can't reach right now.  If it won't load, then might as
00470   // well stop here.  We don't want to be popping up empty windows
00471   // or windows that will just end up showing 404 messages.
00472   // We also stop if we were not allowed to access the given resource.
00473   if (mStopType == eStopType_LinkError ||
00474       mStopType == eStopType_Security) {
00475     // we couldn't successfully link to our external resource.  Better throw
00476     // the xforms-link-error event
00477     nsCOMPtr<nsIModelElementPrivate> modelPriv =
00478       nsXFormsUtils::GetModel(mElement);
00479     nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
00480 
00481     // Context Info: 'resource-uri'
00482     // The URI associated with the failed link.
00483     SetContextInfo("resource-uri", mSrc);
00484 
00485     nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, nsnull,
00486                                  &mContextInfo);
00487     return NS_OK;
00488   }
00489 
00490   // If there is still a channel, then someone must have changed the value of
00491   // the src attribute since the document finished loading and we haven't yet
00492   // determined whether the new link is valid or not.  For now we'll assume
00493   // that the value is good rather than returning a link error.  Also
00494   // canceling the channel since it is too late now.
00495   if (mChannel) {
00496     mChannel->Cancel(NS_BINDING_ABORTED);
00497   }
00498 
00499   if (mType != eType_Normal) {
00500     nsCOMPtr<nsIDOMEventTarget> target;
00501 
00502     if (mType == eType_Alert) {
00503       // Alert should fire only if target is the parent element.
00504       aEvent->GetTarget(getter_AddRefs(target));
00505     } else {
00506       // If <help> or <hint> is inside <action>, we don't want to fire them, 
00507       // unless xforms-help/xforms-hint is dispatched to the <action>.
00508       aEvent->GetCurrentTarget(getter_AddRefs(target));
00509     }
00510     nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(target));
00511     nsCOMPtr<nsIDOMNode> parent;
00512     mElement->GetParentNode(getter_AddRefs(parent));
00513     if (!parent || targetNode != parent)
00514       return NS_OK;
00515   }
00516 
00517   nsAutoString level;
00518   
00519   switch (mType) {
00520     case eType_Normal:
00521       mElement->GetAttribute(NS_LITERAL_STRING("level"), level);
00522       break;
00523     case eType_Hint:
00524       level.AssignLiteral("ephemeral");
00525       // Using the innermost <hint>.
00526       aEvent->StopPropagation();
00527       break;
00528     case eType_Help:
00529       level.AssignLiteral("modeless");
00530       // <help> is equivalent to a
00531       // <message level="modeless" ev:event="xforms-help" ev:propagate="stop>.
00532       aEvent->StopPropagation();
00533       aEvent->PreventDefault();
00534       break;
00535     case eType_Alert:
00536       if (HandleInlineAlert(aEvent))
00537         return NS_OK;
00538 
00539       level.AssignLiteral("modal");
00540       break;
00541   }
00542 
00543   if (level.IsEmpty())
00544     return NS_OK;
00545 
00546   nsCOMPtr<nsIDOMDocument> doc;
00547   mElement->GetOwnerDocument(getter_AddRefs(doc));
00548 
00549   return level.EqualsLiteral("ephemeral")
00550     ? HandleEphemeralMessage(doc, aEvent)
00551     : HandleModalAndModelessMessage(doc, level);
00552 }
00553 
00554 PRBool
00555 nsXFormsMessageElement::HandleInlineAlert(nsIDOMEvent* aEvent)
00556 {
00557   nsCOMPtr<nsIDOMDocument> doc;
00558   nsCOMPtr<nsIDOMWindowInternal> internal;
00559   mElement->GetOwnerDocument(getter_AddRefs(doc));
00560   nsXFormsUtils::GetWindowFromDocument(doc, getter_AddRefs(internal));
00561   if (!internal) {
00562     return PR_FALSE;
00563   }
00564 
00565   nsCOMPtr<nsIDOMViewCSS> cssView(do_QueryInterface(internal));
00566   if (!cssView)
00567     return PR_FALSE;
00568   
00569   nsAutoString tmp;
00570   nsCOMPtr<nsIDOMCSSStyleDeclaration> styles;
00571   cssView->GetComputedStyle(mElement, tmp, getter_AddRefs(styles));
00572   nsCOMPtr<nsIDOMCSSValue> display;
00573   styles->GetPropertyCSSValue(NS_LITERAL_STRING("display"),
00574                               getter_AddRefs(display));
00575   if (display) {
00576     nsCOMPtr<nsIDOMCSSPrimitiveValue> displayValue(do_QueryInterface(display));
00577     if (displayValue) {
00578       nsAutoString type;
00579       displayValue->GetStringValue(type);
00580       return !type.EqualsLiteral("none");
00581     }
00582   }
00583   return PR_FALSE;
00584 }
00585 
00586 nsresult
00587 nsXFormsMessageElement::HandleEphemeralMessage(nsIDOMDocument* aDoc,
00588                                                nsIDOMEvent* aEvent)
00589 {
00590   if (!aEvent)
00591     return NS_OK;
00592 
00593   nsAutoString eventType;
00594   aEvent->GetType(eventType);
00595 
00596   if (mType == eType_Hint) {
00597     // If this is a <hint> element, try to make it work more like a tooltip:
00598     // - if we get an xforms-moz-hint-off event, hide the element.
00599     // - if the <hint> is active and we get a new xforms-hint, then do nothing.
00600     nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDoc));
00601     if (!doc)
00602       return NS_OK;
00603 
00604     nsXFormsMessageElement *msg =
00605     NS_STATIC_CAST(nsXFormsMessageElement*,
00606                    doc->GetProperty(nsXFormsAtoms::messageProperty));
00607     if (msg == this) {
00608       if (eventType.EqualsLiteral("xforms-moz-hint-off")) {
00609         if (mEphemeralTimer) {
00610           mEphemeralTimer->Cancel();
00611           mEphemeralTimer = nsnull;
00612         }
00613         doc->UnsetProperty(nsXFormsAtoms::messageProperty);
00614 
00615         nsCOMPtr<nsIXFormsEphemeralMessageUI> ui(do_QueryInterface(mElement));
00616         if (ui) {
00617           ui->Hide();
00618         }
00619         ResetEphemeralPosition();
00620       }
00621 
00622       return NS_OK;
00623     }
00624   }
00625 
00628   nsCOMPtr<nsIDOMEventTarget> target;
00629   aEvent->GetTarget(getter_AddRefs(target));
00630   nsCOMPtr<nsIDOMElement> targetEl(do_QueryInterface(target));
00631   if (targetEl) {
00632     nsCOMPtr<nsIDOMNSDocument> nsDoc(do_QueryInterface(aDoc));
00633     if (nsDoc) {
00634       PRInt32 oldX = mPosX;
00635       PRInt32 oldY = mPosY;
00636       nsCOMPtr<nsIBoxObject> box;
00637       nsDoc->GetBoxObjectFor(targetEl, getter_AddRefs(box));
00638       if (box) {
00639         box->GetX(&mPosX);
00640         box->GetY(&mPosY);
00641         PRInt32 height;
00642         box->GetHeight(&height);
00643 
00644         mPosX += 10;
00645         mPosY = mPosY + height;
00646 
00647         // Move the ephemeral message a bit upwards if the
00648         // box object of the event target is large enough.
00649         // This makes it more clear to which element the 
00650         // message is related to.
00651         if (height > 20)
00652           mPosY -= height > 30 ? 10 : 10 - (30 - height);
00653       }
00654 
00655       // A special case for hints to make them work more like
00656       // normal tooltips.
00657       if (eventType.EqualsLiteral("xforms-hint") &&
00658           mPosX == oldX && mPosY == oldY) {
00659         return NS_OK;
00660       }
00661 
00662       StartEphemeral();
00663     }
00664   }
00665   return NS_OK;
00666 }
00667 
00668 nsresult
00669 nsXFormsMessageElement::HandleModalAndModelessMessage(nsIDOMDocument* aDoc,
00670                                                       nsAString& aLevel)
00671 {
00672   nsCOMPtr<nsIDOMWindowInternal> internal;
00673   nsXFormsUtils::GetWindowFromDocument(aDoc, getter_AddRefs(internal));
00674   if (!internal) {
00675     return NS_OK;
00676   }
00677 
00678   nsAutoString messageURL;
00679 
00680   // Order of precedence is single-node binding, linking attribute then
00681   // inline text.
00682 
00683   nsAutoString instanceData;
00684   PRBool hasBinding = nsXFormsUtils::GetSingleNodeBindingValue(mElement,
00685                                                                instanceData);
00686 
00687   nsAutoString options;
00688   options.AssignLiteral(MESSAGE_WINDOW_PROPERTIES);
00689 
00690   nsAutoString src;
00691   if (!hasBinding) {
00692     mElement->GetAttribute(NS_LITERAL_STRING("src"), src);
00693   }
00694 
00695   nsresult rv;
00696   if (!hasBinding && !src.IsEmpty()) {
00697     // Creating a normal window for messages with src attribute.
00698     options.AppendLiteral(",resizable");
00699     // Create a new URI so that we properly convert relative urls to absolute.
00700     nsCOMPtr<nsIDocument> doc(do_QueryInterface(aDoc));
00701     NS_ENSURE_STATE(doc);
00702     nsCOMPtr<nsIURI> uri;
00703     NS_NewURI(getter_AddRefs(uri), src, doc->GetDocumentCharacterSet().get(),
00704               doc->GetDocumentURI());
00705     NS_ENSURE_STATE(uri);
00706     nsCAutoString uriSpec;
00707     uri->GetSpec(uriSpec);
00708     messageURL = NS_ConvertUTF8toUTF16(uriSpec);
00709   } else {
00710     // Cloning the content of the xf:message and creating a
00711     // dialog for it.
00712     nsCOMPtr<nsIDOMDocument> ddoc;
00713     nsCOMPtr<nsIDOMDOMImplementation> domImpl;
00714     rv = aDoc->GetImplementation(getter_AddRefs(domImpl));
00715     NS_ENSURE_SUCCESS(rv, rv);
00716 
00717     rv = domImpl->CreateDocument(EmptyString(), EmptyString(), nsnull,
00718                                  getter_AddRefs(ddoc));
00719     NS_ENSURE_SUCCESS(rv, rv);
00720     if (!ddoc)
00721       return NS_OK;
00722 
00723     nsCOMPtr<nsIDOMNode> tmp;
00724     nsCOMPtr<nsIDOMElement> htmlEl;
00725     rv = ddoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML),
00726                                NS_LITERAL_STRING("html"),
00727                                getter_AddRefs(htmlEl));
00728     NS_ENSURE_SUCCESS(rv, rv);
00729     htmlEl->SetAttribute(NS_LITERAL_STRING("style"),
00730                        NS_LITERAL_STRING("background-color: -moz-Dialog;"));
00731     ddoc->AppendChild(htmlEl, getter_AddRefs(tmp));
00732 
00733     nsCOMPtr<nsIDOMElement> bodyEl;
00734     rv = ddoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XHTML),
00735                                NS_LITERAL_STRING("body"),
00736                                getter_AddRefs(bodyEl));
00737     NS_ENSURE_SUCCESS(rv, rv);
00738     htmlEl->AppendChild(bodyEl, getter_AddRefs(tmp));
00739 
00740     // If we have a binding, it is enough to show a simple message
00741     if (hasBinding) {
00742       nsCOMPtr<nsIDOM3Node> body3(do_QueryInterface(bodyEl));
00743       if (body3)
00744         body3->SetTextContent(instanceData);
00745     } else {
00746       // Otherwise copying content from the original document to
00747       // the modeless/modal message document.
00748       nsCOMPtr<nsIDOMNode> tmp;
00749       nsCOMPtr<nsIDOMNodeList> childNodes;
00750       mElement->GetChildNodes(getter_AddRefs(childNodes));
00751 
00752       PRUint32 count = 0;
00753       if (childNodes)
00754         childNodes->GetLength(&count);
00755 
00756       for (PRUint32 i = 0; i < count; ++i) {
00757         nsCOMPtr<nsIDOMNode> child;
00758         childNodes->Item(i, getter_AddRefs(child));
00759         
00760         if (child) {
00761           nsCOMPtr<nsIDOMNode> clone;
00762           ImportNode(child, ddoc, getter_AddRefs(clone));
00763           if (clone)
00764             bodyEl->AppendChild(clone, getter_AddRefs(tmp));
00765         }
00766       }
00767     }
00768 
00769     nsCOMPtr<nsIDOMSerializer> serializer =
00770       do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
00771     NS_ENSURE_SUCCESS(rv, rv);
00772 
00773     nsAutoString docString;
00774     rv = serializer->SerializeToString(ddoc, docString);
00775     NS_ENSURE_SUCCESS(rv, rv);
00776 
00777     char* b64 =
00778       PL_Base64Encode(NS_ConvertUTF16toUTF8(docString).get(), 0, nsnull);
00779     if (!b64) {
00780       return NS_ERROR_FAILURE;
00781     }
00782 
00783     nsCAutoString b64String;
00784     b64String.AppendLiteral("data:application/vnd.mozilla.xul+xml;base64,");
00785     b64String.Append(b64);
00786     PR_Free(b64);
00787 
00788     CopyUTF8toUTF16(b64String, messageURL);
00789   }
00790 
00791   if (aLevel.EqualsLiteral("modal")) {
00792     options.AppendLiteral(",modal");
00793   } else if (aLevel.EqualsLiteral("modeless")) {
00794     options.AppendLiteral(",minimizable");
00795   }
00796 
00797   nsCOMPtr<nsISupportsString> arg(
00798     do_CreateInstance("@mozilla.org/supports-string;1", &rv));
00799   if (!arg)
00800     return rv;
00801 
00802   arg->SetData(messageURL);
00803 
00804   nsCOMPtr<nsISupportsArray> args(
00805     do_CreateInstance("@mozilla.org/supports-array;1", &rv));
00806   if (!args)
00807     return rv;
00808 
00809   args->AppendElement(arg);
00810 
00811   nsCOMPtr<nsIDOMWindow> messageWindow;
00812   // The 2nd argument is the window name, and if a window with the name exists,
00813   // it gets reused.  Using "_blank" makes sure we get a new window each time.
00814   internal->OpenDialog(NS_LITERAL_STRING(MESSAGE_WINDOW_URL),
00815                        NS_LITERAL_STRING("_blank"), options, args,
00816                        getter_AddRefs(messageWindow));
00817   return NS_OK;
00818 }
00819 
00820 void
00821 sEphemeralCallbackShow(nsITimer *aTimer, void *aListener)
00822 {
00823   nsXFormsMessageElement* self =
00824     NS_STATIC_CAST(nsXFormsMessageElement*, aListener);
00825   if (self)
00826     self->ShowEphemeral();
00827 }
00828 
00829 void
00830 sEphemeralCallbackHide(nsITimer *aTimer, void *aListener)
00831 {
00832   nsXFormsMessageElement* self =
00833     NS_STATIC_CAST(nsXFormsMessageElement*, aListener);
00834   if (self)
00835     self->HideEphemeral();
00836 }
00837 
00838 void
00839 sEphemeralCallbackResetPosition(nsITimer *aTimer, void *aListener)
00840 {
00841   nsXFormsMessageElement* self =
00842     NS_STATIC_CAST(nsXFormsMessageElement*, aListener);
00843   if (self)
00844     self->ResetEphemeralPosition();
00845 }
00846 
00847 void
00848 nsXFormsMessageElement::StartEphemeral()
00849 {
00850   HideEphemeral();
00851   if (!mElement)
00852     return;
00853   nsCOMPtr<nsIDOMDocument> domdoc;
00854   mElement->GetOwnerDocument(getter_AddRefs(domdoc));
00855   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domdoc));
00856   if (!doc)
00857     return;
00858   doc->SetProperty(nsXFormsAtoms::messageProperty, this);
00859   mEphemeralTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
00860   if (mEphemeralTimer)
00861     mEphemeralTimer->InitWithFuncCallback(sEphemeralCallbackShow, this, 
00862                                           SHOW_EPHEMERAL_TIMEOUT,
00863                                           nsITimer::TYPE_ONE_SHOT);
00864 }
00865 
00866 void
00867 nsXFormsMessageElement::ShowEphemeral()
00868 {
00869   if (mEphemeralTimer) {
00870     mEphemeralTimer->Cancel();
00871     mEphemeralTimer = nsnull;
00872   }
00873   if (!mElement)
00874     return;
00875 
00876   nsCOMPtr<nsIXFormsEphemeralMessageUI> ui(do_QueryInterface(mElement));
00877   if (ui) {
00878     ui->Show(mPosX, mPosY);
00879   }
00880 
00881   mEphemeralTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
00882   if (mEphemeralTimer)
00883     mEphemeralTimer->InitWithFuncCallback(sEphemeralCallbackHide, this,
00884                                           HIDE_EPHEMERAL_TIMEOUT,
00885                                           nsITimer::TYPE_ONE_SHOT);
00886 }
00887 
00888 void
00889 nsXFormsMessageElement::HideEphemeral()
00890 {
00891   if (mEphemeralTimer) {
00892     mEphemeralTimer->Cancel();
00893     mEphemeralTimer = nsnull;
00894   }
00895   if (!mElement)
00896     return;
00897 
00898   nsCOMPtr<nsIDOMDocument> domdoc;
00899   mElement->GetOwnerDocument(getter_AddRefs(domdoc));
00900   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domdoc));
00901   if (!doc)
00902     return;
00903   nsXFormsMessageElement *msg =
00904     NS_STATIC_CAST(nsXFormsMessageElement*,
00905                    doc->GetProperty(nsXFormsAtoms::messageProperty));
00906   if (msg && msg != this) {
00907     msg->HideEphemeral();
00908     return;
00909   }
00910   doc->UnsetProperty(nsXFormsAtoms::messageProperty);
00911 
00912   nsCOMPtr<nsIXFormsEphemeralMessageUI> ui(do_QueryInterface(mElement));
00913   if (ui) {
00914     ui->Hide();
00915   }
00916 
00917   mEphemeralTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
00918   if (mEphemeralTimer)
00919     mEphemeralTimer->InitWithFuncCallback(sEphemeralCallbackResetPosition,
00920                                           this, 
00921                                           EPHEMERAL_POSITION_RESET_TIMEOUT,
00922                                           nsITimer::TYPE_ONE_SHOT);
00923 }
00924 
00925 nsresult
00926 nsXFormsMessageElement::TestExternalFile()
00927 {
00928   // Let's see if checking for any external resources is even necessary.  Single
00929   // node binding trumps linking attributes in order of precendence.  If we
00930   // find single node binding in evidence, then return NS_OK to show that
00931   // this message element has access to the info that it needs.
00932   nsAutoString snb;
00933   mElement->GetAttribute(NS_LITERAL_STRING("bind"), snb);
00934   if (!snb.IsEmpty()) {
00935     return NS_OK;
00936   }
00937   mElement->GetAttribute(NS_LITERAL_STRING("ref"), snb);
00938   if (!snb.IsEmpty()) {
00939     return NS_OK;
00940   }
00941 
00942   // if no linking attribute, no need to go on
00943   nsAutoString src;
00944   mElement->GetAttribute(NS_LITERAL_STRING("src"), src);
00945   if (src.IsEmpty()) {
00946     return NS_OK;
00947   }
00948   // Remember the src attribute so we can set it in the context info
00949   // for the xforms-link-error event if the link fails.
00950   mSrc = src;
00951 
00952   nsCOMPtr<nsIDOMDocument> domDoc;
00953   mElement->GetOwnerDocument(getter_AddRefs(domDoc));
00954   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00955   NS_ENSURE_STATE(doc);
00956   nsCOMPtr<nsIURI> uri;
00957   NS_NewURI(getter_AddRefs(uri), src, doc->GetDocumentCharacterSet().get(),
00958             doc->GetDocumentURI());
00959   NS_ENSURE_STATE(uri);
00960 
00961   if (!nsXFormsUtils::CheckConnectionAllowed(mElement, uri)) {
00962     nsAutoString tagName;
00963     mElement->GetLocalName(tagName);
00964     const PRUnichar *strings[] = { tagName.get() };
00965     nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLinkLoadOrigin"),
00966                                strings, 1, mElement, mElement);
00967     // Keep the the dialog from popping up.  Won't be able to reach the
00968     // resource anyhow.
00969     mStopType = eStopType_Security;
00970     return NS_ERROR_FAILURE;
00971   }
00972 
00973   nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
00974   NS_WARN_IF_FALSE(loadGroup, "No load group!");
00975 
00976   // Using the same load group as the main document and creating
00977   // the channel with LOAD_NORMAL flag delays the dispatching of
00978   // the 'load' event until message data document has been loaded.
00979   nsresult rv = NS_NewChannel(getter_AddRefs(mChannel), uri, nsnull, loadGroup,
00980                               this, nsIRequest::LOAD_NORMAL);
00981   NS_ENSURE_TRUE(mChannel, rv);
00982   
00983   // See if it's an http channel.  We'll look at the http status code more
00984   // closely and only request the "HEAD" to keep the response small.
00985   // Especially since we are requesting this for every message in the document
00986   // and in most cases we pass off the URL to another browser service to
00987   // get and display the message so no good to try to look at the contents
00988   // anyway.
00989   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
00990   if (httpChannel) {
00991     if (!IsEphemeral()) {
00992       PRBool isReallyHTTP = PR_FALSE;
00993       uri->SchemeIs("http", &isReallyHTTP);
00994       if (!isReallyHTTP) {
00995         uri->SchemeIs("https", &isReallyHTTP);
00996       }
00997       if (isReallyHTTP) {
00998         httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
00999       }
01000     }
01001   }
01002 
01003   rv = mChannel->AsyncOpen(this, nsnull);
01004   if (NS_FAILED(rv)) {
01005     mChannel = nsnull;
01006   
01007     // URI doesn't exist; report error.
01008     // set up the error strings
01009     nsAutoString tagName;
01010     mElement->GetLocalName(tagName);
01011     const PRUnichar *strings[] = { src.get(), tagName.get() };
01012     nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLink1Error"),
01013                                strings, 2, mElement, mElement);
01014     mStopType = eStopType_LinkError;
01015     // Remember the src attribute so we can set it in the context info
01016     // for the xforms-link-error event.
01017     mSrc = src;
01018     return NS_ERROR_FAILURE;
01019   }
01020 
01021   // channel should be running along smoothly, increment the count
01022   AddRemoveExternalResource(PR_TRUE);
01023   return NS_OK;
01024 }
01025 
01026 NS_IMETHODIMP
01027 nsXFormsMessageElement::GetValue(nsAString& aValue)
01028 {
01029   // The order of precedence for determining the text of the message
01030   // is: single node binding, linking, inline text (8.3.5).  We cache
01031   // the value of the external message (via mSrcAttrText) for hints
01032   // and ephemeral messages.
01033   
01034   nsXFormsDelegateStub::GetValue(aValue);
01035   if (aValue.IsVoid()) {
01036     if (!mSrcAttrText.IsEmpty()) {
01037       // handle linking ('src') attribute
01038       aValue = NS_ConvertUTF8toUTF16(mSrcAttrText);
01039     }
01040     else {
01041       // Return inline value
01042       nsCOMPtr<nsIDOM3Node> node = do_QueryInterface(mElement);
01043       if (node) {
01044         node->GetTextContent(aValue);
01045       }
01046     }
01047   }
01048 
01049   return NS_OK;
01050 }
01051 
01052 // nsIInterfaceRequestor
01053 
01054 NS_IMETHODIMP
01055 nsXFormsMessageElement::GetInterface(const nsIID &aIID, void **aResult)
01056 {
01057   *aResult = nsnull;
01058   return QueryInterface(aIID, aResult);
01059 }
01060 
01061 // nsIChannelEventSink
01062 
01063 NS_IMETHODIMP
01064 nsXFormsMessageElement::OnChannelRedirect(nsIChannel *OldChannel,
01065                                           nsIChannel *aNewChannel,
01066                                           PRUint32    aFlags)
01067 {
01068   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
01069 
01070   nsCOMPtr<nsIURI> newURI;
01071   nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
01072   NS_ENSURE_SUCCESS(rv, rv);
01073   
01074   if (!nsXFormsUtils::CheckConnectionAllowed(mElement, newURI)) {
01075     nsAutoString tagName;
01076     mElement->GetLocalName(tagName);
01077     const PRUnichar *strings[] = { tagName.get() };
01078     nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLinkLoadOrigin"),
01079                                strings, 1, mElement, mElement);
01080     mStopType = eStopType_Security;
01081     return NS_ERROR_ABORT;
01082   }
01083 
01084   return NS_OK;
01085 }
01086 
01087 // nsIStreamListener
01088 
01089 NS_IMETHODIMP
01090 nsXFormsMessageElement::OnStartRequest(nsIRequest *aRequest,
01091                                        nsISupports *aContext)
01092 {
01093   // Make sure to null out mChannel before we return.  Keep in mind that
01094   // if this is the last message channel to be loaded for the xforms
01095   // document then when AddRemoveExternalResource is called, it may result
01096   // in xforms-ready firing. Should there be a message acting as a handler
01097   // for xforms-ready, it will start the logic to display itself
01098   // (HandleAction()).  So we can't call AddRemoveExternalResource to remove
01099   // this channel from the count until we've set the mStopType to be the
01100   // proper value.  Entering this function, mStopType will be eStopType_None,
01101   // so if we need mStopType to be any other value (like in an error
01102   // condition), please make sure it is set before AddRemoveExternalResource
01103   // is called.
01104   NS_ASSERTION(aRequest == mChannel, "unexpected request");
01105   NS_ASSERTION(mChannel, "no channel");
01106 
01107   if (!mElement) {
01108     AddRemoveExternalResource(PR_FALSE);
01109     mChannel = nsnull;
01110     return NS_BINDING_ABORTED;
01111   }
01112 
01113   nsresult status;
01114   nsresult rv = mChannel->GetStatus(&status);
01115   // DNS errors and other obvious problems will return failure status
01116   if (NS_FAILED(rv) || NS_FAILED(status)) {
01117     // NS_BINDING_ABORTED means that we have been cancelled by a later
01118     // AttributeSet() call (which will also reset mStopType), so don't
01119     // treat it like an error.
01120     if (status != NS_BINDING_ABORTED) {
01121       nsAutoString src, tagName;
01122       mElement->GetLocalName(tagName);
01123       mElement->GetAttribute(NS_LITERAL_STRING("src"), src);
01124       const PRUnichar *strings[] = { tagName.get(), src.get() };
01125       nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLink2Error"),
01126                                  strings, 2, mElement, mElement);
01127       mStopType = eStopType_LinkError;
01128       // Remember the src attribute so we can set it in the context info
01129       // for the xforms-link-error event.
01130       mSrc = src;
01131     }
01132 
01133     AddRemoveExternalResource(PR_FALSE);
01134     mChannel = nsnull;
01135 
01136     return NS_BINDING_ABORTED;
01137   }
01138 
01139   // If status is zero, it might still be an error if it's http:
01140   // http has data even when there's an error like a 404.
01141   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
01142   if (httpChannel) {
01143     // If responseStatus is 2xx, it is valid.
01144     // 3xx (various flavors of redirection) COULD be successful.  Can't really
01145     // follow those to conclusion so we'll assume they were successful.
01146     // If responseStatus is 4xx or 5xx, it is an error.
01147     PRUint32 responseStatus;
01148     rv = httpChannel->GetResponseStatus(&responseStatus);
01149     if (NS_FAILED(rv) || (responseStatus >= 400)) {
01150       nsAutoString src, tagName;
01151       mElement->GetLocalName(tagName);
01152       mElement->GetAttribute(NS_LITERAL_STRING("src"), src);
01153       const PRUnichar *strings[] = { tagName.get(), src.get() };
01154       nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLink2Error"),
01155                                  strings, 2, mElement, mElement);
01156       mStopType = eStopType_LinkError;
01157       // Remember the src attribute so we can set it in the context info
01158       // for the xforms-link-error event.
01159       mSrc = src;
01160     }
01161   }
01162 
01163   // If the type is Hint or Ephemeral we want to return ns_ok so that callbacks
01164   // continue and we can read the contents of the src attribute during
01165   // OnDataAvailable. For all others, we return ns_binding_aborted because we
01166   // don't care to actually read the data at this point. The external content
01167   // for these other elements will be retrieved by the services that actually
01168   // display the popups.
01169   if (IsEphemeral() && mStopType == eStopType_None)
01170     return NS_OK;
01171 
01172   AddRemoveExternalResource(PR_FALSE);
01173   mChannel = nsnull;
01174 
01175   return NS_BINDING_ABORTED;
01176 }
01177 
01178 NS_IMETHODIMP
01179 nsXFormsMessageElement::OnDataAvailable(nsIRequest *aRequest,
01180                                         nsISupports *aContext,
01181                                         nsIInputStream *aInputStream,
01182                                         PRUint32 aOffset,
01183                                         PRUint32 aCount)
01184 {
01185   if (!mElement) {
01186     AddRemoveExternalResource(PR_FALSE);
01187     mChannel = nsnull;
01188     return NS_BINDING_ABORTED;
01189   }
01190 
01191   if (!IsEphemeral())
01192     return NS_BINDING_ABORTED;
01193 
01194   nsresult rv;
01195   PRUint32 size, bytesRead;
01196   char buffer[256];
01197 
01198   while (aCount) {
01199     size = PR_MIN(aCount, sizeof(buffer));
01200     rv = aInputStream->Read(buffer, size, &bytesRead);
01201     NS_ENSURE_SUCCESS(rv, rv);
01202     mSrcAttrText.Append(buffer, bytesRead);
01203     aCount -= bytesRead;
01204   }
01205 
01206   return NS_OK;
01207 }
01208 
01209 NS_IMETHODIMP
01210 nsXFormsMessageElement::OnStopRequest(nsIRequest *aRequest,
01211                                       nsISupports *aContext,
01212                                       nsresult aStatusCode)
01213 {
01214   if (IsEphemeral()) {
01215     nsCOMPtr<nsIXFormsUIWidget> widget = do_QueryInterface(mElement);
01216     if (widget)
01217       widget->Refresh();
01218 
01219     AddRemoveExternalResource(PR_FALSE);
01220     mChannel = nsnull;
01221   }
01222 
01223   return NS_OK;
01224 }
01225 
01226 void
01227 nsXFormsMessageElement::AddRemoveExternalResource(PRBool aAdd)
01228 {
01229   // if this message doesn't have a channel established already or it has
01230   // already returned, then no sense bumping the counter.
01231   if (!mChannel) {
01232     return;
01233   }
01234 
01235   nsCOMPtr<nsIDOMDocument> domDoc;
01236   mElement->GetOwnerDocument(getter_AddRefs(domDoc));
01237   if (!domDoc) {
01238     return;
01239   }
01240 
01241   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
01242   PRUint32 loadingMessages = NS_PTR_TO_UINT32(
01243     doc->GetProperty(nsXFormsAtoms::externalMessagesProperty));
01244   if (aAdd) {
01245     loadingMessages++;
01246   } else {
01247     if (loadingMessages) {
01248       loadingMessages--;
01249     }
01250   }
01251   doc->SetProperty(nsXFormsAtoms::externalMessagesProperty,
01252                    NS_REINTERPRET_CAST(void *, loadingMessages), nsnull);
01253 
01254   if (!loadingMessages) {
01255     // no outstanding loads left, let the model in the document know in case
01256     // the models are waiting to send out the xforms-ready event
01257 
01258     nsCOMPtr<nsIModelElementPrivate> modelPriv =
01259       nsXFormsUtils::GetModel(mElement);
01260     if (modelPriv) {
01261       // if there are no more messages loading then it is probably the case
01262       // that my mChannel is going to get nulled out as soon as this function
01263       // returns.  If the model is waiting for this notification, then it may
01264       // kick off the message right away and we should probably ensure that
01265       // mChannel is gone before HandleAction is called.  So...if the channel
01266       // isn't pending, let's null it out right here.
01267       PRBool isPending = PR_TRUE;
01268       mChannel->IsPending(&isPending);
01269       if (!isPending) {
01270         mChannel = nsnull;
01271       }
01272       modelPriv->MessageLoadFinished();
01273     }
01274   }
01275 }
01276 
01277 PRBool nsXFormsMessageElement::IsEphemeral()
01278 {
01279   if (mType == eType_Hint)
01280     return PR_TRUE;
01281 
01282   nsAutoString level;
01283   mElement->GetAttribute(NS_LITERAL_STRING("level"), level);
01284   return level.Equals(NS_LITERAL_STRING("ephemeral"));
01285 }
01286 
01287 nsresult
01288 nsXFormsMessageElement::SetContextInfo(const char *aName, const nsAString &aValue)
01289 {
01290   nsCOMPtr<nsXFormsContextInfo> contextInfo = new nsXFormsContextInfo(mElement);
01291   NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
01292   contextInfo->SetStringValue(aName, aValue);
01293   mContextInfo.AppendObject(contextInfo);
01294 
01295   return NS_OK;
01296 }
01297 
01298 NS_IMETHODIMP
01299 nsXFormsMessageElement::GetCurrentEvent(nsIDOMEvent **aEvent)
01300 {
01301   NS_IF_ADDREF(*aEvent = mCurrentEvent);
01302   return NS_OK;
01303 }
01304 
01305 NS_HIDDEN_(nsresult)
01306 NS_NewXFormsMessageElement(nsIXTFElement **aResult)
01307 {
01308   *aResult = new nsXFormsMessageElement(nsXFormsMessageElement::eType_Normal);
01309   if (!*aResult)
01310     return NS_ERROR_OUT_OF_MEMORY;
01311 
01312   NS_ADDREF(*aResult);
01313   return NS_OK;
01314 }
01315 
01316 NS_HIDDEN_(nsresult)
01317 NS_NewXFormsHintElement(nsIXTFElement **aResult)
01318 {
01319   *aResult = new nsXFormsMessageElement(nsXFormsMessageElement::eType_Hint);
01320   if (!*aResult)
01321     return NS_ERROR_OUT_OF_MEMORY;
01322 
01323   NS_ADDREF(*aResult);
01324   return NS_OK;
01325 }
01326 
01327 NS_HIDDEN_(nsresult)
01328 NS_NewXFormsHelpElement(nsIXTFElement **aResult)
01329 {
01330   *aResult = new nsXFormsMessageElement(nsXFormsMessageElement::eType_Help);
01331   if (!*aResult)
01332     return NS_ERROR_OUT_OF_MEMORY;
01333 
01334   NS_ADDREF(*aResult);
01335   return NS_OK;
01336 }
01337 
01338 NS_HIDDEN_(nsresult)
01339 NS_NewXFormsAlertElement(nsIXTFElement **aResult)
01340 {
01341   *aResult = new nsXFormsMessageElement(nsXFormsMessageElement::eType_Alert);
01342   if (!*aResult)
01343     return NS_ERROR_OUT_OF_MEMORY;
01344 
01345   NS_ADDREF(*aResult);
01346   return NS_OK;
01347 }