Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsLabelElement.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  * IBM Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Brian Ryner <bryner@brianryner.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 
00044 #include "nsXFormsUtils.h"
00045 #include "nsXFormsControlStub.h"
00046 #include "nsXFormsDelegateStub.h"
00047 #include "nsXFormsAtoms.h"
00048 #include "nsCOMPtr.h"
00049 #include "nsIDOMElement.h"
00050 #include "nsIDOMXPathResult.h"
00051 #include "nsIDOM3Node.h"
00052 #include "nsIDOMDocument.h"
00053 #include "nsIDOMText.h"
00054 #include "nsString.h"
00055 #include "nsIXFormsUIWidget.h"
00056 #include "nsIDocument.h"
00057 #include "nsNetUtil.h"
00058 #include "nsIXFormsItemElement.h"
00059 
00060 class nsXFormsLabelElement : public nsXFormsDelegateStub,
00061                              public nsIStreamListener,
00062                              public nsIInterfaceRequestor
00063 {
00064 public:
00065   NS_DECL_ISUPPORTS_INHERITED
00066   NS_DECL_NSIREQUESTOBSERVER
00067   NS_DECL_NSISTREAMLISTENER
00068   NS_DECL_NSIINTERFACEREQUESTOR
00069 
00070   // nsIXFormsDelegate
00071   NS_IMETHOD GetValue(nsAString& aValue);
00072   NS_IMETHOD WidgetAttached();
00073 
00074   // nsIXFormsControl
00075   NS_IMETHOD IsEventTarget(PRBool *aOK);
00076   NS_IMETHOD Refresh();
00077 
00078   NS_IMETHOD OnCreated(nsIXTFBindableElementWrapper *aWrapper);
00079   NS_IMETHOD OnDestroyed();
00080 
00081   // nsIXTFElement overrides
00082   NS_IMETHOD ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex);
00083   NS_IMETHOD ChildAppended(nsIDOMNode *aChild);
00084   NS_IMETHOD ChildRemoved(PRUint32 aIndex);
00085   NS_IMETHOD AttributeSet(nsIAtom *aName, const nsAString &aSrc);
00086   NS_IMETHOD AttributeRemoved(nsIAtom *aName);
00087 
00088   nsXFormsLabelElement() : mWidgetLoaded(PR_FALSE) {};
00089 
00090 #ifdef DEBUG_smaug
00091   virtual const char* Name() { return "label"; }
00092 #endif
00093 private:
00094   NS_HIDDEN_(void) LoadExternalLabel(const nsAString& aValue);
00095 
00101   nsresult SetContextInfo(const char *aName, const nsAString &aValue);
00102 
00103   nsCString            mSrcAttrText;
00104   nsCOMPtr<nsIChannel> mChannel;
00105   PRBool               mWidgetLoaded;
00106   // Context Info for events.
00107   nsCOMArray<nsIXFormsContextInfo> mContextInfo;
00108 };
00109 
00110 NS_IMPL_ISUPPORTS_INHERITED3(nsXFormsLabelElement,
00111                              nsXFormsDelegateStub,
00112                              nsIRequestObserver,
00113                              nsIStreamListener,
00114                              nsIInterfaceRequestor)
00115 
00116 NS_IMETHODIMP
00117 nsXFormsLabelElement::OnCreated(nsIXTFBindableElementWrapper *aWrapper)
00118 {
00119   nsresult rv = nsXFormsDelegateStub::OnCreated(aWrapper);
00120   NS_ENSURE_SUCCESS(rv, rv);
00121 
00122   aWrapper->SetNotificationMask(kStandardNotificationMask |
00123                                 nsIXTFElement::NOTIFY_CHILD_INSERTED |
00124                                 nsIXTFElement::NOTIFY_CHILD_APPENDED |
00125                                 nsIXTFElement::NOTIFY_CHILD_REMOVED);
00126   return NS_OK;
00127 }
00128 
00129 NS_IMETHODIMP
00130 nsXFormsLabelElement::OnDestroyed()
00131 {
00132   if (mChannel) {
00133     // better be a good citizen and tell the browser that we don't need this
00134     // resource anymore.
00135     mChannel->Cancel(NS_BINDING_ABORTED);
00136     mChannel = nsnull;
00137   }
00138   return nsXFormsDelegateStub::OnDestroyed();
00139 }
00140 
00141 NS_IMETHODIMP
00142 nsXFormsLabelElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex)
00143 {
00144   Refresh();
00145   return NS_OK;
00146 }
00147 
00148 NS_IMETHODIMP
00149 nsXFormsLabelElement::ChildAppended(nsIDOMNode *aChild)
00150 {
00151   Refresh();
00152   return NS_OK;
00153 }
00154 
00155 NS_IMETHODIMP
00156 nsXFormsLabelElement::ChildRemoved(PRUint32 aIndex)
00157 {
00158   Refresh();
00159   return NS_OK;
00160 }
00161 
00162 NS_IMETHODIMP
00163 nsXFormsLabelElement::AttributeSet(nsIAtom *aName, const nsAString &aValue)
00164 {
00165   if (aName == nsXFormsAtoms::src) {
00166     // If we are currently trying to load an external label, cancel the request.
00167     if (mChannel) {
00168       mChannel->Cancel(NS_BINDING_ABORTED);
00169     }
00170 
00171     LoadExternalLabel(aValue);
00172 
00173     // No need to call Refresh() here, since it is called once the link
00174     // target has been read in, during OnStopRequest()
00175 
00176     return NS_OK;
00177   }
00178 
00179   return nsXFormsDelegateStub::AttributeSet(aName, aValue);
00180 }
00181 
00182 NS_IMETHODIMP
00183 nsXFormsLabelElement::AttributeRemoved(nsIAtom *aName)
00184 {
00185   if (aName == nsXFormsAtoms::src) {
00186     // If we are currently trying to load an external label, cancel the request.
00187     if (mChannel) {
00188       mChannel->Cancel(NS_BINDING_ABORTED);
00189     }
00190 
00191     mSrcAttrText.Truncate();
00192     Refresh();
00193     return NS_OK;
00194   }
00195 
00196   return nsXFormsDelegateStub::AttributeRemoved(aName);
00197 }
00198 
00199 NS_IMETHODIMP
00200 nsXFormsLabelElement::GetValue(nsAString& aValue)
00201 {
00202   // The order of precedence for determining the label is:
00203   //   single node binding, linking, inline text (8.3.3)
00204 
00205   nsXFormsDelegateStub::GetValue(aValue);
00206   if (aValue.IsVoid() && !mSrcAttrText.IsEmpty()) {
00207     // handle linking ('src') attribute
00208     aValue = NS_ConvertUTF8toUTF16(mSrcAttrText);
00209   }
00210   if (aValue.IsVoid()) {
00211     NS_ENSURE_STATE(mElement);
00212 
00213     nsCOMPtr<nsIDOM3Node> inner(do_QueryInterface(mElement));
00214     if (inner) {
00215       inner->GetTextContent(aValue);
00216     }
00217   }
00218 
00219   return NS_OK;
00220 }
00221 
00222 NS_IMETHODIMP
00223 nsXFormsLabelElement::WidgetAttached()
00224 {
00225   mWidgetLoaded = PR_TRUE;
00226 
00227   // We shouldn't report to model that label is ready to work
00228   // if label is loading external resource. We will report
00229   // about it later when external resource will be loaded.
00230   if (!mChannel)
00231     nsXFormsDelegateStub::WidgetAttached();
00232 
00233   return NS_OK;
00234 }
00235 
00236 void
00237 nsXFormsLabelElement::LoadExternalLabel(const nsAString& aSrc)
00238 {
00239   nsresult rv = NS_ERROR_FAILURE;
00240 
00241   nsCOMPtr<nsIDOMDocument> domDoc;
00242   mElement->GetOwnerDocument(getter_AddRefs(domDoc));
00243   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00244   if (doc) {
00245     nsCOMPtr<nsIURI> uri;
00246     NS_NewURI(getter_AddRefs(uri), aSrc, doc->GetDocumentCharacterSet().get(),
00247               doc->GetDocumentURI());
00248     if (uri) {
00249       if (nsXFormsUtils::CheckConnectionAllowed(mElement, uri)) {
00250         nsCOMPtr<nsILoadGroup> loadGroup;
00251         loadGroup = doc->GetDocumentLoadGroup();
00252         NS_WARN_IF_FALSE(loadGroup, "No load group!");
00253 
00254         // Using the same load group as the main document and creating
00255         // the channel with LOAD_NORMAL flag delays the dispatching of
00256         // the 'load' event until label data document has been loaded.
00257         NS_NewChannel(getter_AddRefs(mChannel), uri, nsnull, loadGroup,
00258                       this, nsIRequest::LOAD_NORMAL);
00259 
00260         if (mChannel) {
00261           rv = mChannel->AsyncOpen(this, nsnull);
00262           if (NS_FAILED(rv)) {
00263             // URI doesn't exist; report error.
00264             mChannel = nsnull;
00265 
00266             const nsPromiseFlatString& flat = PromiseFlatString(aSrc);
00267             const PRUnichar *strings[] = { flat.get(),
00268                                            NS_LITERAL_STRING("label").get() };
00269             nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLink1Error"),
00270                                        strings, 2, mElement, mElement);
00271 
00272             nsCOMPtr<nsIModelElementPrivate> modelPriv =
00273                                               nsXFormsUtils::GetModel(mElement);
00274             nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
00275 
00276             // Context Info: 'resource-uri'
00277             // The URI associated with the failed link.
00278             nsAutoString resourceURI(aSrc);
00279             SetContextInfo("resource-uri", resourceURI);
00280             nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull,
00281                                          mElement, &mContextInfo);
00282           }
00283         }
00284       } else {
00285         const PRUnichar *strings[] = { NS_LITERAL_STRING("label").get() };
00286         nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLinkLoadOrigin"),
00287                                    strings, 1, mElement, mElement);
00288         nsCOMPtr<nsIModelElementPrivate> modelPriv =
00289           nsXFormsUtils::GetModel(mElement);
00290         nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
00291 
00292         // Context Info: 'resource-uri'
00293         // The URI associated with the failed link.
00294         nsAutoString resourceURI(aSrc);
00295         SetContextInfo("resource-uri", resourceURI);
00296         nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, mElement,
00297                                      &mContextInfo);
00298       }
00299     }
00300   }
00301 }
00302 
00303 // nsIXFormsControl
00304 
00305 NS_IMETHODIMP
00306 nsXFormsLabelElement::Refresh()
00307 {
00308   nsresult rv = nsXFormsDelegateStub::Refresh();
00309   if (NS_FAILED(rv) || rv == NS_OK_XFORMS_NOREFRESH)
00310     return rv;
00311 
00312   nsCOMPtr<nsIDOMNode> parent;
00313   mElement->GetParentNode(getter_AddRefs(parent));
00314 
00315   // If <label> is inside <select1> its parent is <item>
00316   // or <contextcontainer> (which parent is <item>).
00317   nsCOMPtr<nsIXFormsItemElement> item(do_QueryInterface(parent));
00318   if (item) {
00319     item->LabelRefreshed();
00320   } else if (parent) {
00321     nsCOMPtr<nsIDOMNode> grandparent;
00322     parent->GetParentNode(getter_AddRefs(grandparent));
00323     item = do_QueryInterface(grandparent);
00324     if (item) {
00325       item->LabelRefreshed();
00326     }
00327   }
00328 
00329   return NS_OK;
00330 }
00331 
00332 NS_IMETHODIMP
00333 nsXFormsLabelElement::IsEventTarget(PRBool *aOK)
00334 {
00335   *aOK = PR_FALSE;
00336   return NS_OK;
00337 }
00338 
00339 // nsIInterfaceRequestor
00340 
00341 NS_IMETHODIMP
00342 nsXFormsLabelElement::GetInterface(const nsIID &aIID, void **aResult)
00343 {
00344   *aResult = nsnull;
00345   return QueryInterface(aIID, aResult);
00346 }
00347 
00348 // nsIStreamListener
00349 
00350 // It is possible that the label element could well on its way to invalid by
00351 // the time that the below handlers are called.  If the document hit a
00352 // parsing error after we've already started to load the external source, then
00353 // Mozilla will destroy all of the elements and the document in order to load
00354 // the parser error page.  This will cause mElement to become null but since
00355 // the channel will hold a nsCOMPtr to the nsXFormsLabelElement preventing
00356 // it from being freed up, the channel will still be able to call the
00357 // nsIStreamListener functions that we implement here.  And calling
00358 // mChannel->Cancel() is no guarantee that these other notifications won't come
00359 // through if the timing is wrong.  So we need to check for mElement below
00360 // before we handle any of the stream notifications.
00361 
00362 NS_IMETHODIMP
00363 nsXFormsLabelElement::OnStartRequest(nsIRequest *aRequest,
00364                                      nsISupports *aContext)
00365 {
00366   if (!mElement) {
00367     return NS_OK;
00368   }
00369 
00370   // Only handle data from text files for now.  Cancel any other requests.
00371   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
00372   if (channel) {
00373     nsCAutoString type;
00374     channel->GetContentType(type);
00375     if (!type.EqualsLiteral("text/plain"))
00376       return NS_ERROR_ILLEGAL_VALUE;
00377   }
00378 
00379   mSrcAttrText.Truncate();
00380   return NS_OK;
00381 }
00382 
00383 NS_IMETHODIMP
00384 nsXFormsLabelElement::OnDataAvailable(nsIRequest *aRequest,
00385                                       nsISupports *aContext,
00386                                       nsIInputStream *aInputStream,
00387                                       PRUint32 aOffset,
00388                                       PRUint32 aCount)
00389 {
00390   if (!mElement) {
00391     return NS_OK;
00392   }
00393 
00394   nsresult rv;
00395   PRUint32 size, bytesRead;
00396   char buffer[256];
00397 
00398   while (aCount) {
00399     size = PR_MIN(aCount, sizeof(buffer));
00400     rv = aInputStream->Read(buffer, size, &bytesRead);
00401     if (NS_FAILED(rv))
00402       return rv;
00403     mSrcAttrText.Append(buffer, bytesRead);
00404     aCount -= bytesRead;
00405   }
00406 
00407   return NS_OK;
00408 }
00409 
00410 NS_IMETHODIMP
00411 nsXFormsLabelElement::OnStopRequest(nsIRequest *aRequest,
00412                                     nsISupports *aContext,
00413                                     nsresult aStatusCode)
00414 {
00415   // Done with load request, so null out channel member
00416   mChannel = nsnull;
00417   if (!mElement) {
00418     return NS_OK;
00419   }
00420 
00421   if (NS_FAILED(aStatusCode)) {
00422     // If we received NS_BINDING_ABORTED, then we were cancelled by a later
00423     // AttributeSet() or AttributeRemoved call.  Don't do anything and return.
00424     if (aStatusCode == NS_BINDING_ABORTED)
00425       return NS_OK;
00426 
00427     nsAutoString src;
00428     mElement->GetAttribute(NS_LITERAL_STRING("src"), src);
00429     const PRUnichar *strings[] = { NS_LITERAL_STRING("label").get(), 
00430                                    src.get() };
00431     nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLink2Error"),
00432                                strings, 2, mElement, mElement);
00433 
00434     nsCOMPtr<nsIModelElementPrivate> modelPriv =
00435       nsXFormsUtils::GetModel(mElement);
00436     nsCOMPtr<nsIDOMNode> model = do_QueryInterface(modelPriv);
00437 
00438     // Context Info: 'resource-uri'
00439     // The URI associated with the failed link.
00440     SetContextInfo("resource-uri", src);
00441     nsXFormsUtils::DispatchEvent(model, eEvent_LinkError, nsnull, mElement,
00442                                  &mContextInfo);
00443 
00444     mSrcAttrText.Truncate();
00445   }
00446 
00447   if (mWidgetLoaded)
00448     nsXFormsDelegateStub::WidgetAttached();
00449 
00450   return NS_OK;
00451 }
00452 
00453 nsresult
00454 nsXFormsLabelElement::SetContextInfo(const char *aName, const nsAString &aValue)
00455 {
00456   nsCOMPtr<nsXFormsContextInfo> contextInfo = new nsXFormsContextInfo(mElement);
00457   NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00458   contextInfo->SetStringValue(aName, aValue);
00459   mContextInfo.AppendObject(contextInfo);
00460 
00461   return NS_OK;
00462 }
00463 
00464 NS_HIDDEN_(nsresult)
00465 NS_NewXFormsLabelElement(nsIXTFElement **aResult)
00466 {
00467   *aResult = new nsXFormsLabelElement();
00468   if (!*aResult)
00469     return NS_ERROR_OUT_OF_MEMORY;
00470 
00471   NS_ADDREF(*aResult);
00472   return NS_OK;
00473 }