Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsInstanceElement.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  *  Merle Sterling <msterlin@us.ibm.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsXFormsInstanceElement.h"
00042 #include "nsIDOMElement.h"
00043 #include "nsIDOMEventTarget.h"
00044 #include "nsIDOM3Node.h"
00045 #include "nsMemory.h"
00046 #include "nsXFormsAtoms.h"
00047 #include "nsString.h"
00048 #include "nsIDOMEventReceiver.h"
00049 #include "nsIDOMDOMImplementation.h"
00050 #include "nsIXTFGenericElementWrapper.h"
00051 #include "nsXFormsUtils.h"
00052 #include "nsNetUtil.h"
00053 
00054 static const char* kLoadAsData = "loadAsData";
00055 
00056 NS_IMPL_ISUPPORTS_INHERITED6(nsXFormsInstanceElement,
00057                              nsXFormsStubElement,
00058                              nsIXFormsNSInstanceElement,
00059                              nsIInstanceElementPrivate,
00060                              nsIStreamListener,
00061                              nsIRequestObserver,
00062                              nsIInterfaceRequestor,
00063                              nsIChannelEventSink)
00064 
00065 nsXFormsInstanceElement::nsXFormsInstanceElement()
00066   : mElement(nsnull)
00067   , mInitialized(PR_FALSE)
00068   , mLazy(PR_FALSE)
00069 {
00070 }
00071 
00072 NS_IMETHODIMP
00073 nsXFormsInstanceElement::OnDestroyed()
00074 {
00075   if (mChannel) {
00076     // better be a good citizen and tell the browser that we don't need this
00077     // resource anymore.
00078     mChannel->Cancel(NS_BINDING_ABORTED);
00079     mChannel = nsnull;
00080   }
00081   mListener = nsnull;
00082   SetInstanceDocument(nsnull);
00083   mElement = nsnull;
00084   return NS_OK;
00085 }
00086 
00087 NS_IMETHODIMP
00088 nsXFormsInstanceElement::AttributeSet(nsIAtom *aName,
00089                                       const nsAString &aNewValue)
00090 {
00091   if (!mInitialized || mLazy)
00092     return NS_OK;
00093 
00094   // @src has precedence over inline content which has precedence
00095   // over @resource.
00096   if (aName == nsXFormsAtoms::src) {
00097     LoadExternalInstance(aNewValue);
00098   } else if (aName == nsXFormsAtoms::resource) {
00099     // If @resource is set or changed, it only matters if there is
00100     // no @src or inline content.
00101     nsAutoString src;
00102     mElement->GetAttribute(NS_LITERAL_STRING("src"), src);
00103     if (src.IsEmpty()) {
00104       // Check for inline content.
00105       nsCOMPtr<nsIDOMNode> child;
00106       GetFirstChildElement(getter_AddRefs(child));
00107       if (!child) {
00108         LoadExternalInstance(aNewValue);
00109       }
00110     }
00111   }
00112 
00113   return NS_OK;
00114 }
00115 
00116 NS_IMETHODIMP
00117 nsXFormsInstanceElement::AttributeRemoved(nsIAtom *aName)
00118 {
00119   if (!mInitialized || mLazy)
00120     return NS_OK;
00121 
00122   // @src has precdence over inline content which has precedence
00123   // over @resource.
00124   if (aName == nsXFormsAtoms::src) {
00125     PRBool restart = PR_FALSE;
00126     if (mChannel) {
00127       // looks like we are trying to remove the src attribute while we are
00128       // already trying to load the external instance document.  We'll stop
00129       // the current load effort.
00130       restart = PR_TRUE;
00131       mChannel->Cancel(NS_BINDING_ABORTED);
00132       mChannel = nsnull;
00133       mListener = nsnull;
00134     }
00135 
00136     // We no longer have an external (@src) instance to use. Next in precedence
00137     // would be inline content so we'll use that if it exists. If there is no
00138     // inline content, then we'll look for @resource (the instance may have had
00139     // both @src and @resource and no inline content).
00140     nsresult rv;
00141     nsCOMPtr<nsIDOMNode> child;
00142     GetFirstChildElement(getter_AddRefs(child));
00143     if (child) {
00144       // Reset our instance document to whatever inline content we have.
00145       rv = CloneInlineInstance(child);
00146     } else {
00147       nsAutoString resource;
00148       mElement->GetAttribute(NS_LITERAL_STRING("resource"), resource);
00149       if (!resource.IsEmpty()) {
00150         LoadExternalInstance(resource);
00151       }
00152     }
00153 
00154     // if we had already started to load an external instance document, then
00155     // as part of that we would have told the model to wait for that external
00156     // document to load before it finishes the model construction.  Since we
00157     // aren't loading from an external document any longer, tell the model that
00158     // there is no need to wait for us anymore.
00159     if (restart) {
00160       nsCOMPtr<nsIModelElementPrivate> model = GetModel();
00161       if (model) {
00162         model->InstanceLoadFinished(PR_TRUE, EmptyString());
00163       }
00164     }
00165     return rv;
00166   }
00167 
00168   return NS_OK;
00169 }
00170 
00171 NS_IMETHODIMP
00172 nsXFormsInstanceElement::OnCreated(nsIXTFGenericElementWrapper *aWrapper)
00173 {
00174   aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_ATTRIBUTE_SET |
00175                                 nsIXTFElement::NOTIFY_ATTRIBUTE_REMOVED |
00176                                 nsIXTFElement::NOTIFY_WILL_CHANGE_PARENT |
00177                                 nsIXTFElement::NOTIFY_PARENT_CHANGED);
00178 
00179   nsCOMPtr<nsIDOMElement> node;
00180   aWrapper->GetElementNode(getter_AddRefs(node));
00181 
00182   // It's ok to keep a weak pointer to mElement.  mElement will have an owning
00183   // reference to this object, so as long as we null out mElement in
00184   // OnDestroyed, it will always be valid.
00185 
00186   mElement = node;
00187   NS_ASSERTION(mElement, "Wrapper is not an nsIDOMElement, we'll crash soon");
00188 
00189   return NS_OK;
00190 }
00191 
00192 // nsIInterfaceRequestor
00193 
00194 NS_IMETHODIMP
00195 nsXFormsInstanceElement::GetInterface(const nsIID & aIID, void **aResult)
00196 {
00197   *aResult = nsnull;
00198   return QueryInterface(aIID, aResult);
00199 }
00200 
00201 // nsIChannelEventSink
00202 
00203 NS_IMETHODIMP
00204 nsXFormsInstanceElement::OnChannelRedirect(nsIChannel *OldChannel,
00205                                            nsIChannel *aNewChannel,
00206                                            PRUint32    aFlags)
00207 {
00208   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
00209   NS_PRECONDITION(!mLazy, "Loading an instance document for a lazy instance?");
00210 
00211   nsCOMPtr<nsIURI> newURI;
00212   nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
00213   NS_ENSURE_SUCCESS(rv, rv);
00214 
00215   if (!nsXFormsUtils::CheckConnectionAllowed(mElement, newURI)) {
00216     const PRUnichar *strings[] = { NS_LITERAL_STRING("instance").get() };
00217     nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLinkLoadOrigin"),
00218                                strings, 1, mElement, mElement);
00219     return NS_ERROR_ABORT;
00220   }
00221 
00222   return NS_OK;
00223 }
00224 
00225 // nsIStreamListener
00226 
00227 // It is possible that mListener could be null here.  If the document hit a
00228 // parsing error after we've already started to load the external source, then
00229 // Mozilla will destroy all of the elements and the document in order to load
00230 // the parser error page.  This will cause mListener to become null but since
00231 // the channel will hold a nsCOMPtr to the nsXFormsInstanceElement preventing
00232 // it from being freed up, the channel will still be able to call the
00233 // nsIStreamListener functions that we implement here.  And calling
00234 // mChannel->Cancel() is no guarantee that these other notifications won't come
00235 // through if the timing is wrong.  So we need to check for mElement below
00236 // before we handle any of the stream notifications.
00237 
00238 NS_IMETHODIMP
00239 nsXFormsInstanceElement::OnStartRequest(nsIRequest *request, nsISupports *ctx)
00240 {
00241   NS_PRECONDITION(!mLazy, "Loading an instance document for a lazy instance?");
00242   if (!mElement || !mListener) {
00243     return NS_OK;
00244   }
00245   return mListener->OnStartRequest(request, ctx);
00246 }
00247 
00248 NS_IMETHODIMP
00249 nsXFormsInstanceElement::OnDataAvailable(nsIRequest *aRequest,
00250                                          nsISupports *ctxt,
00251                                          nsIInputStream *inStr,
00252                                          PRUint32 sourceOffset,
00253                                          PRUint32 count)
00254 {
00255   NS_PRECONDITION(!mLazy, "Loading an instance document for a lazy instance?");
00256   if (!mElement || !mListener) {
00257     return NS_OK;
00258   }
00259   return mListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
00260 }
00261 
00262 NS_IMETHODIMP
00263 nsXFormsInstanceElement::OnStopRequest(nsIRequest *request, nsISupports *ctx,
00264                                        nsresult status)
00265 {
00266   NS_PRECONDITION(!mLazy, "Loading an instance document for a lazy instance?");
00267   if (status == NS_BINDING_ABORTED) {
00268     // looks like our element has already been destroyed.  No use continuing on.
00269     return NS_OK;
00270   }
00271 
00272   mChannel = nsnull;
00273   NS_ASSERTION(mListener, "No stream listener for document!");
00274   mListener->OnStopRequest(request, ctx, status);
00275 
00276   PRBool succeeded = NS_SUCCEEDED(status);
00277   if (!succeeded) {
00278     SetInstanceDocument(nsnull);
00279   }
00280 
00281   if (mDocument) {
00282     nsCOMPtr<nsIDOMElement> docElem;
00283     mDocument->GetDocumentElement(getter_AddRefs(docElem));
00284     if (docElem) {
00285       nsAutoString tagName, namespaceURI;
00286       docElem->GetTagName(tagName);
00287       docElem->GetNamespaceURI(namespaceURI);
00288   
00289       if (tagName.EqualsLiteral("parsererror") &&
00290           namespaceURI.EqualsLiteral("http://www.mozilla.org/newlayout/xml/parsererror.xml")) {
00291         NS_WARNING("resulting instance document could not be parsed");
00292         succeeded = PR_FALSE;
00293         SetInstanceDocument(nsnull);
00294       }
00295     }
00296 
00297     // Replace the principal for the loaded document
00298     nsCOMPtr<nsIDocument> iDoc(do_QueryInterface(mDocument));
00299     nsresult rv = ReplacePrincipal(iDoc);
00300     if (NS_FAILED(rv)) {
00301       SetInstanceDocument(nsnull);
00302       return rv;
00303     }
00304   }
00305 
00306   nsCOMPtr<nsIModelElementPrivate> model = GetModel();
00307   if (model) {
00308     nsCAutoString uri;
00309     request->GetName(uri);
00310     model->InstanceLoadFinished(succeeded, NS_ConvertASCIItoUTF16(uri));
00311   }
00312 
00313   mListener = nsnull;
00314   return NS_OK;
00315 }
00316 
00317 nsresult
00318 nsXFormsInstanceElement::ReplacePrincipal(nsIDocument *aDocument)
00319 {
00320   if (!aDocument || !mElement)
00321     return NS_ERROR_FAILURE;
00322 
00323   // Set Principal
00324   nsCOMPtr<nsIDOMDocument> domDoc;
00325   mElement->GetOwnerDocument(getter_AddRefs(domDoc));
00326   nsCOMPtr<nsIDocument> fromDoc(do_QueryInterface(domDoc));
00327   NS_ENSURE_STATE(fromDoc);
00328   aDocument->SetPrincipal(fromDoc->GetPrincipal());
00329 
00330   return NS_OK;
00331 }
00332 
00333 // nsIXFormsNSInstanceElement
00334 
00335 NS_IMETHODIMP
00336 nsXFormsInstanceElement::GetInstanceDocument(nsIDOMDocument **aDocument)
00337 {
00338   NS_IF_ADDREF(*aDocument = mDocument);
00339   return NS_OK;
00340 }
00341 
00342 // nsIInstanceElementPrivate
00343 
00344 NS_IMETHODIMP
00345 nsXFormsInstanceElement::SetInstanceDocument(nsIDOMDocument *aDocument)
00346 {
00347   nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
00348   if (doc) {
00349     doc->UnsetProperty(nsXFormsAtoms::instanceDocumentOwner);
00350   }
00351 
00352   mDocument = aDocument;
00353 
00354   doc = do_QueryInterface(mDocument);
00355   if (doc) {
00356     // Set property to prevent an instance document loading an external instance
00357     // document
00358     nsresult rv = doc->SetProperty(nsXFormsAtoms::isInstanceDocument, doc);
00359     NS_ENSURE_SUCCESS(rv, rv);
00360 
00361     nsCOMPtr<nsISupports> owner(do_QueryInterface(mElement));
00362     NS_ENSURE_STATE(owner);
00363     rv = doc->SetProperty(nsXFormsAtoms::instanceDocumentOwner, owner);
00364     NS_ENSURE_SUCCESS(rv, rv);
00365 
00366     // Replace the principal of the instance document so it is the same as for
00367     // the owning form. Why is this not a security breach? Because we handle
00368     // our own whitelist of domains that we trust (see
00369     // nsXFormsUtils::CheckSameOrigin()), and if we have gotten this far
00370     // (ie. loaded the document) the user has trusted obviously trusted the
00371     // source. See also https://bugzilla.mozilla.org/show_bug.cgi?id=338451
00372     rv = ReplacePrincipal(doc);
00373     NS_ENSURE_SUCCESS(rv, rv);
00374   }
00375 
00376   return NS_OK;
00377 }
00378 
00379 
00380 NS_IMETHODIMP
00381 nsXFormsInstanceElement::BackupOriginalDocument()
00382 {
00383   nsresult rv = NS_OK;
00384 
00385   // This is called when xforms-ready is received by the model.  By now we know 
00386   // that the instance document, whether external or inline, is loaded into 
00387   // mDocument.  Get the root node, clone it, and insert it into our copy of 
00388   // the document.  This is the magic behind getting xforms-reset to work.
00389   nsCOMPtr<nsIDocument> origDoc(do_QueryInterface(mOriginalDocument));
00390   if(mDocument && origDoc) {
00391     // xf:instance elements in original document must not try to load anything.
00392     rv = origDoc->SetProperty(nsXFormsAtoms::isInstanceDocument, origDoc);
00393     NS_ENSURE_SUCCESS(rv, rv);
00394 
00395     nsCOMPtr<nsIDOMNode> newNode;
00396     nsCOMPtr<nsIDOMElement> instanceRoot;
00397     rv = mDocument->GetDocumentElement(getter_AddRefs(instanceRoot));
00398     NS_ENSURE_SUCCESS(rv, rv);
00399     NS_ENSURE_TRUE(instanceRoot, NS_ERROR_FAILURE);
00400 
00401     nsCOMPtr<nsIDOMNode> nodeReturn;
00402     rv = mOriginalDocument->ImportNode(instanceRoot, PR_TRUE,
00403                                        getter_AddRefs(newNode));
00404     if(NS_SUCCEEDED(rv)) {
00405       rv = mOriginalDocument->AppendChild(newNode, getter_AddRefs(nodeReturn));
00406       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), 
00407                        "failed to set up original instance document");
00408     }
00409   }
00410   return rv;
00411 }
00412 
00413 NS_IMETHODIMP
00414 nsXFormsInstanceElement::RestoreOriginalDocument()
00415 {
00416   // This is called when xforms-reset is received by the model.  We assume
00417   // that the backup of the instance document has been populated and is
00418   // loaded into mOriginalDocument.
00419  
00420  if (!mOriginalDocument) {
00421     return NS_ERROR_FAILURE;
00422   }
00423   
00424   nsCOMPtr<nsIDOMNode> newDocNode;
00425   nsresult rv = mOriginalDocument->CloneNode(PR_TRUE,
00426                                              getter_AddRefs(newDocNode));
00427   NS_ENSURE_SUCCESS(rv, rv);
00428 
00429   nsCOMPtr<nsIDOMDocument> newDoc(do_QueryInterface(newDocNode));
00430   NS_ENSURE_STATE(newDoc);
00431   
00432   rv = SetInstanceDocument(newDoc);
00433   NS_ENSURE_SUCCESS(rv, rv);
00434   
00435   return NS_OK;
00436 }
00437 
00438 NS_IMETHODIMP
00439 nsXFormsInstanceElement::GetElement(nsIDOMElement **aElement)
00440 {
00441   NS_IF_ADDREF(*aElement = mElement);
00442   return NS_OK;  
00443 }
00444 
00445 NS_IMETHODIMP
00446 nsXFormsInstanceElement::WillChangeParent(nsIDOMElement *aNewParent)
00447 {
00448   if (!aNewParent) {
00449     nsCOMPtr<nsIModelElementPrivate> model = GetModel();
00450     if (model) {
00451       model->RemoveInstanceElement(this);
00452     }
00453   }
00454 
00455   return NS_OK;
00456 }
00457 
00458 NS_IMETHODIMP
00459 nsXFormsInstanceElement::ParentChanged(nsIDOMElement *aNewParent)
00460 {
00461   nsCOMPtr<nsIModelElementPrivate> model = GetModel();
00462   if (!model) return NS_OK;
00463 
00464   if (mInitialized || !mElement) {
00465     return NS_OK;
00466   }
00467 
00468   mInitialized = PR_TRUE;
00469   model->AddInstanceElement(this);
00470 
00471   // If model isn't loaded entirely (It means xforms-ready event hasn't been
00472   // fired) then the instance will be initialized by model and will be backed up
00473   // when xforms-ready event is fired.
00474   PRBool isready;
00475   model->GetIsReady(&isready);
00476   if (!isready)
00477     return NS_OK;
00478 
00479   // If the model is loaded and ready then the instance is inserted dynamically.
00480   // The instance should create instance document and back it up.
00481 
00482   // Probably dynamic instances should be handled too when model isn't loaded
00483   // entirely (for more information see a comment 29 of bug 320081
00484   // https://bugzilla.mozilla.org/show_bug.cgi?id=320081#c29).
00485 
00486   nsAutoString src;
00487   mElement->GetAttribute(NS_LITERAL_STRING("src"), src);
00488 
00489   if (!src.IsEmpty()) {
00490     // XXX: external dynamic instances isn't handled (see a bug 325684
00491     // https://bugzilla.mozilla.org/show_bug.cgi?id=325684)
00492     return NS_OK;
00493   }
00494 
00495   // If we don't have a linked external instance, use our inline data.
00496   nsCOMPtr<nsIDOMNode> child;
00497   GetFirstChildElement(getter_AddRefs(child));
00498   nsresult rv = CloneInlineInstance(child);
00499   if (NS_FAILED(rv))
00500     return rv;
00501   return BackupOriginalDocument();
00502 }
00503 
00504 NS_IMETHODIMP
00505 nsXFormsInstanceElement::Initialize()
00506 {
00507   mElement->HasAttributeNS(NS_LITERAL_STRING(NS_NAMESPACE_MOZ_XFORMS_LAZY),
00508                            NS_LITERAL_STRING("lazy"), &mLazy);
00509 
00510   if (mLazy) { // Lazy instance
00511     return CreateInstanceDocument(NS_LITERAL_STRING("instanceData"));
00512   }
00513 
00514   // Normal instance
00515   // If the src attribute is given, then it takes precedence over inline
00516   // content and the resource attribute, and the XML data for the instance
00517   // is obtained from the link. If the src attribute is omitted, then the
00518   // data for the instance is obtained from inline content if it is given
00519   // or the resource attribute otherwise. If both the resource attribute
00520   // and inline content are provided, the inline content takes precedence.
00521   nsresult rv = NS_OK;
00522   nsAutoString src, resource;
00523   mElement->GetAttribute(NS_LITERAL_STRING("src"), src);
00524 
00525   if (!src.IsEmpty()) {
00526     LoadExternalInstance(src);
00527   } else {
00528     // If we have a first child element then we have inline content.
00529     nsCOMPtr<nsIDOMNode> child;
00530     GetFirstChildElement(getter_AddRefs(child));
00531     if (child) {
00532       rv = CloneInlineInstance(child);
00533     } else {
00534       // No @src or inline content, better have @resource
00535       mElement->GetAttribute(NS_LITERAL_STRING("resource"), resource);
00536       LoadExternalInstance(resource);
00537     }
00538   }
00539 
00540   return rv;
00541 }
00542 
00543 // private methods
00544 
00545 nsresult
00546 nsXFormsInstanceElement::CloneInlineInstance(nsIDOMNode *aChild)
00547 {
00548   // Clear out our existing instance data
00549   nsresult rv = CreateInstanceDocument(EmptyString());
00550   if (NS_FAILED(rv))
00551     return rv; // don't warn, we might just not be in the document yet
00552 
00553 
00554   // aChild must be the first child element under xf:instance or it is not
00555   // a valid XML document.
00556   if (!aChild) {
00557     nsXFormsUtils::ReportError(NS_LITERAL_STRING("inlineInstanceNoChildError"),
00558                                mElement);
00559 
00560     nsCOMPtr<nsIModelElementPrivate> model(GetModel());
00561     nsCOMPtr<nsIDOMNode> modelNode(do_QueryInterface(model));
00562     // Context Info: 'resource-uri'
00563     // ID of the failed instance preceded by the # fragment identifier.
00564     nsAutoString idStr;
00565     mElement->GetAttribute(NS_LITERAL_STRING("id"), idStr);
00566     if (!idStr.IsEmpty()) {
00567       nsAutoString resourceURI;
00568       resourceURI.AssignLiteral("#");
00569       resourceURI.Append(idStr);
00570       nsCOMPtr<nsXFormsContextInfo> contextInfo =
00571         new nsXFormsContextInfo(mElement);
00572       NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00573       contextInfo->SetStringValue("resource-uri", resourceURI);
00574       mContextInfo.AppendObject(contextInfo);
00575     }
00576     nsXFormsUtils::DispatchEvent(modelNode, eEvent_LinkException, nsnull,
00577                                  mElement, &mContextInfo);
00578     nsXFormsUtils::HandleFatalError(mElement,
00579                                     NS_LITERAL_STRING("XFormsLinkException"));
00580     return NS_ERROR_FAILURE;
00581   }
00582 
00583   // Check for siblings to first child element node. This is an error, since
00584   // the inline content is then not a well-formed XML document.
00585   nsCOMPtr<nsIDOMNode> sibling, temp;
00586   aChild->GetNextSibling(getter_AddRefs(sibling));
00587   while (sibling) {
00588     PRUint16 nodeType;
00589     sibling->GetNodeType(&nodeType);
00590 
00591     if (nodeType == nsIDOMNode::ELEMENT_NODE) {
00592       nsXFormsUtils::ReportError(NS_LITERAL_STRING("inlineInstanceMultipleElementsError"),
00593                                  mElement);
00594 
00595       nsCOMPtr<nsIModelElementPrivate> model(GetModel());
00596       nsCOMPtr<nsIDOMNode> modelNode(do_QueryInterface(model));
00597       // Context Info: 'resource-uri'
00598       // ID of the failed instance preceded by the # fragment identifier.
00599       nsAutoString idStr;
00600       mElement->GetAttribute(NS_LITERAL_STRING("id"), idStr);
00601       if (!idStr.IsEmpty()) {
00602         nsAutoString resourceURI;
00603         resourceURI.AssignLiteral("#");
00604         resourceURI.Append(idStr);
00605         nsCOMPtr<nsXFormsContextInfo> contextInfo =
00606           new nsXFormsContextInfo(mElement);
00607         NS_ENSURE_TRUE(contextInfo, NS_ERROR_OUT_OF_MEMORY);
00608         contextInfo->SetStringValue("resource-uri", resourceURI);
00609         mContextInfo.AppendObject(contextInfo);
00610       }
00611       nsXFormsUtils::DispatchEvent(modelNode, eEvent_LinkException, nsnull,
00612                                    mElement, &mContextInfo);
00613       nsXFormsUtils::HandleFatalError(mElement,
00614                                       NS_LITERAL_STRING("XFormsLinkException"));
00615       return NS_ERROR_FAILURE;
00616     }
00617 
00618     temp.swap(sibling);
00619     temp->GetNextSibling(getter_AddRefs(sibling));
00620   }
00621 
00622   // Clone and insert content into new document
00623   nsCOMPtr<nsIDOMNode> newNode;
00624   rv = mDocument->ImportNode(aChild, PR_TRUE, getter_AddRefs(newNode));
00625   NS_ENSURE_SUCCESS(rv, rv);
00626 
00627   nsCOMPtr<nsIDOMNode> nodeReturn;
00628   rv = mDocument->AppendChild(newNode, getter_AddRefs(nodeReturn));
00629   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to append root instance node");
00630 
00631   return rv;
00632 }
00633 
00634 void
00635 nsXFormsInstanceElement::LoadExternalInstance(const nsAString &aSrc)
00636 {
00637   nsresult rv = NS_ERROR_FAILURE;
00638 
00639   PRBool restart = PR_FALSE;
00640   if (mChannel) {
00641     // probably hit this condition because someone changed the value of our
00642     // src or resource attribute while we are already trying to load the
00643     // previously specified document.  We'll stop the current load effort and
00644     // kick off the new attempt.
00645     restart = PR_TRUE;
00646     mChannel->Cancel(NS_BINDING_ABORTED);
00647     mChannel = nsnull;
00648     mListener = nsnull;
00649   }
00650 
00651   // Check whether we are an instance document ourselves
00652   nsCOMPtr<nsIDOMDocument> domDoc;
00653   mElement->GetOwnerDocument(getter_AddRefs(domDoc));
00654   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00655   if (doc) {
00656     if (doc->GetProperty(nsXFormsAtoms::isInstanceDocument)) {
00659       const nsPromiseFlatString& flat = PromiseFlatString(aSrc);
00660       const PRUnichar *strings[] = { flat.get() };
00661       nsXFormsUtils::ReportError(NS_LITERAL_STRING("instanceInstanceLoad"),
00662                                  strings, 1, mElement, mElement);
00663     } else {
00664       // Clear out our existing instance data
00665       if (NS_SUCCEEDED(CreateInstanceDocument(EmptyString()))) {
00666         nsCOMPtr<nsIDocument> newDoc = do_QueryInterface(mDocument);
00667 
00668         nsCOMPtr<nsIURI> uri;
00669         NS_NewURI(getter_AddRefs(uri), aSrc,
00670                   doc->GetDocumentCharacterSet().get(), doc->GetDocumentURI());
00671         if (uri) {
00672           if (nsXFormsUtils::CheckConnectionAllowed(mElement, uri)) {
00673             nsCOMPtr<nsILoadGroup> loadGroup;
00674             loadGroup = doc->GetDocumentLoadGroup();
00675             NS_WARN_IF_FALSE(loadGroup, "No load group!");
00676 
00677             // Using the same load group as the main document and creating
00678             // the channel with LOAD_NORMAL flag delays the dispatching of
00679             // the 'load' event until all instance data documents have been
00680             // loaded.
00681             NS_NewChannel(getter_AddRefs(mChannel), uri, nsnull, loadGroup,
00682                           nsnull, nsIRequest::LOAD_NORMAL);
00683 
00684             if (mChannel) {
00685               rv = newDoc->StartDocumentLoad(kLoadAsData, mChannel, loadGroup,
00686                                              nsnull, getter_AddRefs(mListener),
00687                                              PR_TRUE);
00688               if (NS_SUCCEEDED(rv)) {
00689                 mChannel->SetNotificationCallbacks(this);
00690                 rv = mChannel->AsyncOpen(this, nsnull);
00691               }
00692             }
00693           } else {
00694             const PRUnichar *strings[] = {NS_LITERAL_STRING("instance").get()};
00695             nsXFormsUtils::ReportError(NS_LITERAL_STRING("externalLinkLoadOrigin"),
00696                                        strings, 1, mElement, mElement);
00697           }
00698         }
00699       }
00700     }
00701   }
00702 
00703   nsCOMPtr<nsIModelElementPrivate> model = GetModel();
00704   if (model) {
00705     // if this isn't the first time that this instance element has tried
00706     // to load an external document, then we don't need to tell the model
00707     // to wait again.  It would screw up the counter it uses.  But if this
00708     // isn't a restart, then by golly, the model better wait for us!
00709     if (!restart) {
00710       model->InstanceLoadStarted();
00711     }
00712     if (NS_FAILED(rv)) {
00713       // Context Info: 'resource-uri'
00714       // The resource URI of the link that failed.
00715       model->InstanceLoadFinished(PR_FALSE, aSrc);
00716     }
00717   }
00718 }
00719 
00720 nsresult
00721 nsXFormsInstanceElement::CreateInstanceDocument(const nsAString &aQualifiedName)
00722 {
00723   nsCOMPtr<nsIDOMDocument> doc;
00724   nsresult rv = mElement->GetOwnerDocument(getter_AddRefs(doc));
00725   NS_ENSURE_SUCCESS(rv, rv);
00726 
00727   if (!doc) // could be we just aren't inserted yet, so don't warn
00728     return NS_ERROR_FAILURE;
00729 
00730   nsCOMPtr<nsIDOMDOMImplementation> domImpl;
00731   rv = doc->GetImplementation(getter_AddRefs(domImpl));
00732   NS_ENSURE_SUCCESS(rv, rv);
00733 
00734   nsCOMPtr<nsIDOMDocument> newDoc;
00735   rv = domImpl->CreateDocument(EmptyString(), aQualifiedName, nsnull,
00736                                getter_AddRefs(newDoc));
00737   NS_ENSURE_SUCCESS(rv, rv);
00738 
00739   rv = SetInstanceDocument(newDoc);
00740   NS_ENSURE_SUCCESS(rv, rv);
00741 
00742   // I don't know if not being able to create a backup document is worth
00743   // failing this function.  Since it probably won't be used often, we'll
00744   // let it slide.  But it probably does mean that things are going south
00745   // with the browser.
00746   domImpl->CreateDocument(EmptyString(), aQualifiedName, nsnull,
00747                           getter_AddRefs(mOriginalDocument));
00748   return rv;
00749 }
00750 
00751 already_AddRefed<nsIModelElementPrivate>
00752 nsXFormsInstanceElement::GetModel()
00753 {
00754   if (!mElement)
00755   {
00756     NS_WARNING("The XTF wrapper element has been destroyed");
00757     return nsnull;
00758   }
00759 
00760   nsCOMPtr<nsIDOMNode> parentNode;
00761   mElement->GetParentNode(getter_AddRefs(parentNode));
00762 
00763   nsIModelElementPrivate *model = nsnull;
00764   if (parentNode)
00765     CallQueryInterface(parentNode, &model);
00766   return model;
00767 }
00768 
00769 nsresult
00770 nsXFormsInstanceElement::GetFirstChildElement(nsIDOMNode **aChild)
00771 {
00772   // look for our first child element (skip over text nodes, etc.)
00773   nsCOMPtr<nsIDOMNode> child, temp;
00774   mElement->GetFirstChild(getter_AddRefs(child));
00775   while (child) {
00776     PRUint16 nodeType;
00777     child->GetNodeType(&nodeType);
00778 
00779     if (nodeType == nsIDOMNode::ELEMENT_NODE)
00780       break;
00781 
00782     temp.swap(child);
00783     temp->GetNextSibling(getter_AddRefs(child));
00784   }
00785 
00786   NS_IF_ADDREF(*aChild = child);
00787 
00788   return NS_OK;
00789 }
00790 
00791 nsresult
00792 NS_NewXFormsInstanceElement(nsIXTFElement **aResult)
00793 {
00794   *aResult = new nsXFormsInstanceElement();
00795   if (!*aResult)
00796     return NS_ERROR_OUT_OF_MEMORY;
00797 
00798   NS_ADDREF(*aResult);
00799   return NS_OK;
00800 }