Back to index

lightning-sunbird  0.9+nobinonly
nsXULElement.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Chris Waterson <waterson@netscape.com>
00025  *   Pierre Phaneuf <pp@ludusdesign.com>
00026  *   Peter Annema <disttsc@bart.nl>
00027  *   Brendan Eich <brendan@mozilla.org>
00028  *   Mike Shaver <shaver@mozilla.org>
00029  *
00030  * Alternatively, the contents of this file may be used under the terms of
00031  * either of the GNU General Public License Version 2 or later (the "GPL"),
00032  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00033  * in which case the provisions of the GPL or the LGPL are applicable instead
00034  * of those above. If you wish to allow use of your version of this file only
00035  * under the terms of either the GPL or the LGPL, and not to allow others to
00036  * use your version of this file under the terms of the MPL, indicate your
00037  * decision by deleting the provisions above and replace them with the notice
00038  * and other provisions required by the GPL or the LGPL. If you do not delete
00039  * the provisions above, a recipient may use your version of this file under
00040  * the terms of any one of the MPL, the GPL or the LGPL.
00041  *
00042  * ***** END LICENSE BLOCK *****
00043  *
00044  * This Original Code has been modified by IBM Corporation.
00045  * Modifications made by IBM described herein are
00046  * Copyright (c) International Business Machines
00047  * Corporation, 2000
00048  *
00049  * Modifications to Mozilla code or documentation
00050  * identified per MPL Section 3.3
00051  *
00052  * Date         Modified by     Description of modification
00053  * 03/27/2000   IBM Corp.       Added PR_CALLBACK for Optlink
00054  *                               use in OS2
00055  */
00056 
00057 /*
00058 
00059   Implementation for a XUL content element.
00060 
00061   TO DO
00062 
00063   1. Possibly clean up the attribute walking code (e.g., in
00064      SetDocument, GetAttrCount, etc.) by extracting into an iterator.
00065 
00066  */
00067 
00068 #include "jsapi.h"      // for JS_AddNamedRoot and JS_RemoveRootRT
00069 #include "jsxdrapi.h"
00070 #include "nsCOMPtr.h"
00071 #include "nsDOMCID.h"
00072 #include "nsDOMError.h"
00073 #include "nsDOMString.h"
00074 #include "nsIDOMEvent.h"
00075 #include "nsIPrivateDOMEvent.h"
00076 #include "nsHashtable.h"
00077 #include "nsIAtom.h"
00078 #include "nsIDOMAttr.h"
00079 #include "nsIDOMDocument.h"
00080 #include "nsIDOMElement.h"
00081 #include "nsIDOMMouseListener.h"
00082 #include "nsIDOMMouseMotionListener.h"
00083 #include "nsIDOMLoadListener.h"
00084 #include "nsIDOMFocusListener.h"
00085 #include "nsIDOMPaintListener.h"
00086 #include "nsIDOMKeyListener.h"
00087 #include "nsIDOMFormListener.h"
00088 #include "nsIDOMXULListener.h"
00089 #include "nsIDOMScrollListener.h"
00090 #include "nsIDOMContextMenuListener.h"
00091 #include "nsIDOMDragListener.h"
00092 #include "nsIDOMEventListener.h"
00093 #include "nsIDOMEventReceiver.h"
00094 #include "nsIDOMNodeList.h"
00095 #include "nsIDOMXULCommandDispatcher.h"
00096 #include "nsIDOMXULElement.h"
00097 #include "nsIDOMXULSelectCntrlItemEl.h"
00098 #include "nsIDocument.h"
00099 #include "nsIEventListenerManager.h"
00100 #include "nsIEventStateManager.h"
00101 #include "nsIFastLoadService.h"
00102 #include "nsHTMLStyleSheet.h"
00103 #include "nsINameSpaceManager.h"
00104 #include "nsIObjectInputStream.h"
00105 #include "nsIObjectOutputStream.h"
00106 #include "nsIPresShell.h"
00107 #include "nsIPrincipal.h"
00108 #include "nsIRDFCompositeDataSource.h"
00109 #include "nsIRDFNode.h"
00110 #include "nsIRDFService.h"
00111 #include "nsIScriptContext.h"
00112 #include "nsIScriptGlobalObject.h"
00113 #include "nsIScriptGlobalObjectOwner.h"
00114 #include "nsIServiceManager.h"
00115 #include "nsICSSStyleRule.h"
00116 #include "nsIStyleSheet.h"
00117 #include "nsIStyledContent.h"
00118 #include "nsIURL.h"
00119 #include "nsIViewManager.h"
00120 #include "nsIWidget.h"
00121 #include "nsIXULDocument.h"
00122 #include "nsIXULPopupListener.h"
00123 #include "nsIXULPrototypeDocument.h"
00124 #include "nsIXULTemplateBuilder.h"
00125 #include "nsIXBLService.h"
00126 #include "nsLayoutCID.h"
00127 #include "nsContentCID.h"
00128 #include "nsRDFCID.h"
00129 #include "nsStyleConsts.h"
00130 #include "nsXPIDLString.h"
00131 #include "nsXULControllers.h"
00132 #include "nsIBoxObject.h"
00133 #include "nsPIBoxObject.h"
00134 #include "nsXULDocument.h"
00135 #include "nsRuleWalker.h"
00136 #include "nsIDOMViewCSS.h"
00137 #include "nsIDOMCSSStyleDeclaration.h"
00138 #include "nsCSSDeclaration.h"
00139 #include "nsIListBoxObject.h"
00140 #include "nsContentUtils.h"
00141 #include "nsContentList.h"
00142 #include "nsMutationEvent.h"
00143 #include "nsIDOMMutationEvent.h"
00144 #include "nsPIDOMWindow.h"
00145 #include "nsDOMAttributeMap.h"
00146 #include "nsDOMCSSDeclaration.h"
00147 #include "nsGenericHTMLElement.h"
00148 #include "nsHTMLAtoms.h"
00149 #include "nsLayoutAtoms.h"
00150 #include "nsXULContentUtils.h"
00151 
00152 #include "prlog.h"
00153 #include "rdf.h"
00154 
00155 #include "nsIControllers.h"
00156 
00157 // The XUL interfaces implemented by the RDF content node.
00158 #include "nsIDOMXULElement.h"
00159 
00160 // The XUL doc interface
00161 #include "nsIDOMXULDocument.h"
00162 
00163 #include "nsReadableUtils.h"
00164 #include "nsITimelineService.h"
00165 #include "nsIFrame.h"
00166 #include "nsNodeInfoManager.h"
00167 #include "nsXBLBinding.h"
00168 
00172 #define XUL_ELEMENT_CHILDREN_MUST_BE_REBUILT \
00173   (nsXULElement::eChildrenMustBeRebuilt << XUL_ELEMENT_LAZY_STATE_OFFSET)
00174 
00175 #define XUL_ELEMENT_TEMPLATE_CONTENTS_BUILT \
00176   (nsXULElement::eTemplateContentsBuilt << XUL_ELEMENT_LAZY_STATE_OFFSET)
00177 
00178 #define XUL_ELEMENT_CONTAINER_CONTENTS_BUILT \
00179   (nsXULElement::eContainerContentsBuilt << XUL_ELEMENT_LAZY_STATE_OFFSET)
00180 
00181 class nsIDocShell;
00182 
00183 // Global object maintenance
00184 nsICSSParser* nsXULPrototypeElement::sCSSParser = nsnull;
00185 nsIXULPrototypeCache* nsXULPrototypeScript::sXULPrototypeCache = nsnull;
00186 nsIXBLService * nsXULElement::gXBLService = nsnull;
00187 nsICSSOMFactory* nsXULElement::gCSSOMFactory = nsnull;
00188 
00189 //----------------------------------------------------------------------
00190 
00191 static NS_DEFINE_CID(kXULPopupListenerCID,        NS_XULPOPUPLISTENER_CID);
00192 static NS_DEFINE_CID(kCSSOMFactoryCID,            NS_CSSOMFACTORY_CID);
00193 
00194 //----------------------------------------------------------------------
00195 
00196 #if 0 /* || defined(DEBUG_shaver) || defined(DEBUG_waterson) */
00197 #define DEBUG_ATTRIBUTE_STATS
00198 #endif
00199 
00200 #ifdef DEBUG_ATTRIBUTE_STATS
00201 #include <execinfo.h>
00202 
00203 static struct {
00204     PRUint32 UnsetAttr;
00205     PRUint32 Create;
00206     PRUint32 Total;
00207 } gFaults;
00208 #endif
00209 
00210 //----------------------------------------------------------------------
00211 
00212 
00213 // XXX This function is called for every attribute on every element for
00214 // XXX which we SetDocument, among other places.  A linear search might
00215 // XXX not be what we want.
00216 static PRBool
00217 IsEventHandler(nsIAtom* aName)
00218 {
00219     const char* name;
00220     aName->GetUTF8String(&name);
00221 
00222     if (name[0] != 'o' || name[1] != 'n') {
00223         return PR_FALSE;
00224     }
00225     
00226     return aName == nsLayoutAtoms::onclick            ||
00227            aName == nsLayoutAtoms::ondblclick         ||
00228            aName == nsLayoutAtoms::onmousedown        ||
00229            aName == nsLayoutAtoms::onmouseup          ||
00230            aName == nsLayoutAtoms::onmouseover        ||
00231            aName == nsLayoutAtoms::onmouseout         ||
00232            aName == nsLayoutAtoms::onmousemove        ||
00233 
00234            aName == nsLayoutAtoms::onkeydown          ||
00235            aName == nsLayoutAtoms::onkeyup            ||
00236            aName == nsLayoutAtoms::onkeypress         ||
00237 
00238            aName == nsLayoutAtoms::oncompositionstart ||
00239            aName == nsLayoutAtoms::oncompositionend   ||
00240 
00241            aName == nsLayoutAtoms::onload             ||
00242            aName == nsLayoutAtoms::onunload           ||
00243            aName == nsLayoutAtoms::onabort            ||
00244            aName == nsLayoutAtoms::onerror            ||
00245 
00246            aName == nsLayoutAtoms::onpopupshowing     ||
00247            aName == nsLayoutAtoms::onpopupshown       ||
00248            aName == nsLayoutAtoms::onpopuphiding      ||
00249            aName == nsLayoutAtoms::onpopuphidden      ||
00250            aName == nsLayoutAtoms::onclose            ||
00251            aName == nsLayoutAtoms::oncommand          ||
00252            aName == nsLayoutAtoms::onbroadcast        ||
00253            aName == nsLayoutAtoms::oncommandupdate    ||
00254 
00255            aName == nsLayoutAtoms::onoverflow         ||
00256            aName == nsLayoutAtoms::onunderflow        ||
00257            aName == nsLayoutAtoms::onoverflowchanged  ||
00258 
00259            aName == nsLayoutAtoms::onfocus            ||
00260            aName == nsLayoutAtoms::onblur             ||
00261 
00262            aName == nsLayoutAtoms::onsubmit           ||
00263            aName == nsLayoutAtoms::onreset            ||
00264            aName == nsLayoutAtoms::onchange           ||
00265            aName == nsLayoutAtoms::onselect           ||
00266            aName == nsLayoutAtoms::oninput            ||
00267 
00268            aName == nsLayoutAtoms::onpaint            ||
00269 
00270            aName == nsLayoutAtoms::ondragenter        ||
00271            aName == nsLayoutAtoms::ondragover         ||
00272            aName == nsLayoutAtoms::ondragexit         ||
00273            aName == nsLayoutAtoms::ondragdrop         ||
00274            aName == nsLayoutAtoms::ondraggesture      ||
00275 
00276            aName == nsLayoutAtoms::oncontextmenu;
00277 }
00278 
00279 //----------------------------------------------------------------------
00280 
00281 #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
00282 PRUint32             nsXULPrototypeAttribute::gNumElements;
00283 PRUint32             nsXULPrototypeAttribute::gNumAttributes;
00284 PRUint32             nsXULPrototypeAttribute::gNumEventHandlers;
00285 PRUint32             nsXULPrototypeAttribute::gNumCacheTests;
00286 PRUint32             nsXULPrototypeAttribute::gNumCacheHits;
00287 PRUint32             nsXULPrototypeAttribute::gNumCacheSets;
00288 PRUint32             nsXULPrototypeAttribute::gNumCacheFills;
00289 #endif
00290 
00291 //----------------------------------------------------------------------
00292 // nsXULElement
00293 //
00294 
00295 nsXULElement::nsXULElement(nsINodeInfo* aNodeInfo)
00296     : nsGenericElement(aNodeInfo),
00297       mPrototype(nsnull),
00298       mBindingParent(nsnull)
00299 {
00300     XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements);
00301 }
00302 
00303 nsXULElement::~nsXULElement()
00304 {
00305     //XXX UnbindFromTree is not called always before dtor.
00306     //XXX Related to templates or overlays?
00307     //XXXbz probably related to the cloning thing!
00308     if (IsInDoc()) {
00309       UnbindFromTree();
00310     }
00311 
00312     nsDOMSlots* slots = GetExistingDOMSlots();
00313     if (slots) {
00314       NS_IF_RELEASE(slots->mControllers); // Forces release
00315     }
00316 
00317     if (mPrototype)
00318         mPrototype->Release();
00319 }
00320 
00321 
00322 nsresult
00323 nsXULElement::Create(nsXULPrototypeElement* aPrototype,
00324                      nsIDocument* aDocument,
00325                      PRBool aIsScriptable,
00326                      nsIContent** aResult)
00327 {
00328     // Create an nsXULElement from a prototype
00329     NS_PRECONDITION(aPrototype != nsnull, "null ptr");
00330     if (! aPrototype)
00331         return NS_ERROR_NULL_POINTER;
00332 
00333     NS_PRECONDITION(aResult != nsnull, "null ptr");
00334     if (! aResult)
00335         return NS_ERROR_NULL_POINTER;
00336 
00337     nsCOMPtr<nsINodeInfo> nodeInfo;
00338     nsresult rv;
00339     if (aDocument) {
00340         nsINodeInfo* ni = aPrototype->mNodeInfo;
00341         rv = aDocument->NodeInfoManager()->GetNodeInfo(ni->NameAtom(),
00342                                                        ni->GetPrefixAtom(),
00343                                                        ni->NamespaceID(),
00344                                                        getter_AddRefs(nodeInfo));
00345         NS_ENSURE_SUCCESS(rv, rv);
00346     }
00347     else {
00348       nodeInfo = aPrototype->mNodeInfo;
00349     }
00350 
00351     nsRefPtr<nsXULElement> element = new nsXULElement(nodeInfo);
00352 
00353     if (! element)
00354         return NS_ERROR_OUT_OF_MEMORY;
00355 
00356     element->mPrototype = aPrototype;
00357 
00358     aPrototype->AddRef();
00359 
00360     if (aIsScriptable) {
00361         // Check each attribute on the prototype to see if we need to do
00362         // any additional processing and hookup that would otherwise be
00363         // done 'automagically' by SetAttr().
00364         for (PRUint32 i = 0; i < aPrototype->mNumAttributes; ++i)
00365             element->AddListenerFor(aPrototype->mAttributes[i].mName, PR_TRUE);
00366     }
00367 
00368     NS_ADDREF(*aResult = element.get());
00369     return NS_OK;
00370 }
00371 
00372 nsresult
00373 NS_NewXULElement(nsIContent** aResult, nsINodeInfo *aNodeInfo)
00374 {
00375     NS_PRECONDITION(aNodeInfo, "need nodeinfo for non-proto Create");
00376 
00377     *aResult = nsnull;
00378 
00379     // Create an nsXULElement with the specified namespace and tag.
00380     nsXULElement* element = new nsXULElement(aNodeInfo);
00381     NS_ENSURE_TRUE(element, NS_ERROR_OUT_OF_MEMORY);
00382 
00383     // Using kungFuDeathGrip so an early return will clean up properly.
00384     nsCOMPtr<nsIContent> kungFuDeathGrip = element;
00385 
00386     kungFuDeathGrip.swap(*aResult);
00387 
00388 #ifdef DEBUG_ATTRIBUTE_STATS
00389     {
00390         gFaults.Create++; gFaults.Total++;
00391         nsAutoString tagstr;
00392         element->GetNodeInfo()->GetQualifiedName(tagstr);
00393         char *tagcstr = ToNewCString(tagstr);
00394         fprintf(stderr, "XUL: Heavyweight create of <%s>: %d/%d\n",
00395                 tagcstr, gFaults.Create, gFaults.Total);
00396         nsMemory::Free(tagcstr);
00397         void *back[5];
00398         backtrace(back, sizeof(back) / sizeof(back[0]));
00399         backtrace_symbols_fd(back, sizeof(back) / sizeof(back[0]), 2);
00400     }
00401 #endif
00402 
00403     return NS_OK;
00404 }
00405 
00406 //----------------------------------------------------------------------
00407 // nsISupports interface
00408 
00409 NS_IMPL_ADDREF_INHERITED(nsXULElement, nsGenericElement)
00410 NS_IMPL_RELEASE_INHERITED(nsXULElement, nsGenericElement)
00411 
00412 NS_IMETHODIMP
00413 nsXULElement::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00414 {
00415     NS_ENSURE_ARG_POINTER(aInstancePtr);
00416     *aInstancePtr = nsnull;
00417 
00418 
00419     nsresult rv = nsGenericElement::QueryInterface(aIID, aInstancePtr);
00420     if (NS_SUCCEEDED(rv))
00421         return rv;
00422 
00423     nsISupports *inst = nsnull;
00424 
00425     if (aIID.Equals(NS_GET_IID(nsIDOMNode))) {
00426         inst = NS_STATIC_CAST(nsIDOMNode *, this);
00427     } else if (aIID.Equals(NS_GET_IID(nsIDOMElement))) {
00428         inst = NS_STATIC_CAST(nsIDOMElement *, this);
00429     } else if (aIID.Equals(NS_GET_IID(nsIDOMXULElement))) {
00430         inst = NS_STATIC_CAST(nsIDOMXULElement *, this);
00431     } else if (aIID.Equals(NS_GET_IID(nsIXMLContent))) {
00432         inst = NS_STATIC_CAST(nsIXMLContent *, this);
00433     } else if (aIID.Equals(NS_GET_IID(nsIScriptEventHandlerOwner))) {
00434         inst = NS_STATIC_CAST(nsIScriptEventHandlerOwner *, this);
00435     } else if (aIID.Equals(NS_GET_IID(nsIChromeEventHandler))) {
00436         inst = NS_STATIC_CAST(nsIChromeEventHandler *, this);
00437     } else if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
00438         inst = nsContentUtils::GetClassInfoInstance(eDOMClassInfo_XULElement_id);
00439         NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);
00440     } else {
00441         return PostQueryInterface(aIID, aInstancePtr);
00442     }
00443 
00444     NS_ADDREF(inst);
00445  
00446     *aInstancePtr = inst;
00447     return NS_OK;
00448 }
00449 
00450 //----------------------------------------------------------------------
00451 // nsIDOMNode interface
00452 
00453 NS_IMETHODIMP
00454 nsXULElement::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
00455 {
00456     nsresult rv;
00457 
00458     nsCOMPtr<nsIContent> result;
00459 
00460     // XXX setting document on some nodes not in a document so XBL will bind
00461     // and chrome won't break. Make XBL bind to document-less nodes!
00462     // XXXbz Once this is fixed, fix up the asserts in all implementations of
00463     // BindToTree to assert what they would like to assert, and fix the
00464     // ChangeDocumentFor() call in nsXULElement::BindToTree as well.  Also,
00465     // remove the UnbindFromTree call in ~nsXULElement, and add back in the
00466     // precondition in nsXULElement::UnbindFromTree.
00467     // Note: Make sure to do this witchery _after_ we've done any deep
00468     // cloning, so kids of the new node aren't confused about whether they're
00469     // in a document.
00470     
00471     PRBool fakeBeingInDocument = PR_TRUE;
00472     
00473     // If we have a prototype, so will our clone.
00474     if (mPrototype) {
00475         rv = nsXULElement::Create(mPrototype, GetOwnerDoc(), PR_TRUE,
00476                                   getter_AddRefs(result));
00477         NS_ENSURE_SUCCESS(rv, rv);
00478 
00479         fakeBeingInDocument = IsInDoc();
00480     } else {
00481         rv = NS_NewXULElement(getter_AddRefs(result), mNodeInfo);
00482         NS_ENSURE_SUCCESS(rv, rv);
00483     }
00484 
00485     // Copy attributes
00486     PRInt32 count = mAttrsAndChildren.AttrCount();
00487     for (PRInt32 i = 0; i < count; ++i) {
00488         const nsAttrName* name = mAttrsAndChildren.GetSafeAttrNameAt(i);
00489         nsAutoString valStr;
00490         mAttrsAndChildren.AttrAt(i)->ToString(valStr);
00491         rv = result->SetAttr(name->NamespaceID(), name->LocalName(),
00492                              name->GetPrefix(), valStr, PR_FALSE);
00493         NS_ENSURE_SUCCESS(rv, rv);
00494     }
00495 
00496     // XXX TODO: set up RDF generic builder n' stuff if there is a
00497     // 'datasources' attribute? This is really kind of tricky,
00498     // because then we'd need to -selectively- copy children that
00499     // -weren't- generated from RDF. Ugh. Forget it.
00500 
00501     // Note that we're _not_ copying mControllers.
00502 
00503     if (aDeep) {
00504         // Copy cloned children!
00505         PRInt32 i, count = mAttrsAndChildren.ChildCount();
00506         for (i = 0; i < count; ++i) {
00507             nsIContent* child = mAttrsAndChildren.ChildAt(i);
00508 
00509             NS_ASSERTION(child != nsnull, "null ptr");
00510             if (! child)
00511                 return NS_ERROR_UNEXPECTED;
00512 
00513             nsCOMPtr<nsIDOMNode> domchild = do_QueryInterface(child);
00514             NS_ASSERTION(domchild != nsnull, "child is not a DOM node");
00515             if (! domchild)
00516                 return NS_ERROR_UNEXPECTED;
00517 
00518             nsCOMPtr<nsIDOMNode> newdomchild;
00519             rv = domchild->CloneNode(PR_TRUE, getter_AddRefs(newdomchild));
00520             if (NS_FAILED(rv)) return rv;
00521 
00522             nsCOMPtr<nsIContent> newchild = do_QueryInterface(newdomchild);
00523             NS_ASSERTION(newchild != nsnull, "newdomchild is not an nsIContent");
00524             if (! newchild)
00525                 return NS_ERROR_UNEXPECTED;
00526 
00527             rv = result->AppendChildTo(newchild, PR_FALSE);
00528             if (NS_FAILED(rv)) return rv;
00529         }
00530     }
00531 
00532     if (fakeBeingInDocument) {
00533         // Don't use BindToTree here so we don't confuse the descendant
00534         // non-XUL nodes.
00535         NS_STATIC_CAST(nsXULElement*,
00536                        NS_STATIC_CAST(nsIContent*, result.get()))->
00537             mParentPtrBits |= PARENT_BIT_INDOCUMENT;
00538     }
00539     
00540     return CallQueryInterface(result, aReturn);
00541 }
00542 
00543 //----------------------------------------------------------------------
00544 
00545 NS_IMETHODIMP
00546 nsXULElement::GetElementsByAttribute(const nsAString& aAttribute,
00547                                      const nsAString& aValue,
00548                                      nsIDOMNodeList** aReturn)
00549 {
00550     nsCOMPtr<nsIAtom> attrAtom(do_GetAtom(aAttribute));
00551     NS_ENSURE_TRUE(attrAtom, NS_ERROR_OUT_OF_MEMORY);
00552 
00553     nsContentList *list = 
00554         new nsContentList(GetDocument(),
00555                           nsXULDocument::MatchAttribute,
00556                           aValue,
00557                           this,
00558                           PR_TRUE,
00559                           attrAtom,
00560                           kNameSpaceID_Unknown);
00561     NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
00562 
00563     NS_ADDREF(*aReturn = list);
00564     return NS_OK;
00565 }
00566 
00567 
00568 //----------------------------------------------------------------------
00569 // nsIXMLContent interface
00570 
00571 NS_IMETHODIMP
00572 nsXULElement::MaybeTriggerAutoLink(nsIDocShell *aShell)
00573 {
00574   return NS_OK;
00575 }
00576 
00577 nsresult
00578 nsXULElement::AddScriptEventListener(nsIAtom* aName, const nsAString& aValue)
00579 {
00580     // XXX sXBL/XBL2 issue! Owner or current document?
00581     // Note that If it's current, then we need to hook up listeners on the root
00582     // when we BindToTree...
00583     nsIDocument* doc = GetOwnerDoc();
00584     if (!doc)
00585         return NS_OK; // XXX
00586 
00587     nsresult rv;
00588 
00589     nsISupports *target = NS_STATIC_CAST(nsIContent *, this);
00590     PRBool defer = PR_TRUE;
00591 
00592     nsCOMPtr<nsIEventListenerManager> manager;
00593 
00594     nsIContent *root = doc->GetRootContent();
00595     nsCOMPtr<nsIContent> content(do_QueryInterface(NS_STATIC_CAST(nsIStyledContent*, this)));
00596 
00597     nsIScriptGlobalObject *global = doc->GetScriptGlobalObject();
00598     nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(global));
00599 
00600     if (window && window->IsInnerWindow() && (!root || root == content) &&
00601         !mNodeInfo->Equals(nsXULAtoms::overlay)) {
00602         nsCOMPtr<nsIDOMEventReceiver> receiver = do_QueryInterface(global);
00603         if (! receiver)
00604             return NS_ERROR_UNEXPECTED;
00605 
00606         rv = receiver->GetListenerManager(getter_AddRefs(manager));
00607 
00608         target = global;
00609         defer = PR_FALSE;
00610     }
00611     else {
00612         rv = GetListenerManager(getter_AddRefs(manager));
00613     }
00614 
00615     if (NS_FAILED(rv)) return rv;
00616 
00617     return manager->AddScriptEventListener(target, aName, aValue, defer,
00618                                            !nsContentUtils::IsChromeDoc(doc));
00619 }
00620 
00621 nsresult
00622 nsXULElement::GetListenerManager(nsIEventListenerManager** aResult)
00623 {
00624     if (!mListenerManager) {
00625         nsresult rv =
00626             NS_NewEventListenerManager(getter_AddRefs(mListenerManager));
00627         if (NS_FAILED(rv))
00628             return rv;
00629 
00630         mListenerManager->SetListenerTarget(NS_STATIC_CAST(nsIContent*, this));
00631     }
00632 
00633     *aResult = mListenerManager;
00634     NS_ADDREF(*aResult);
00635     return NS_OK;
00636 }
00637 
00638 PRBool
00639 nsXULElement::IsFocusable(PRInt32 *aTabIndex)
00640 {
00641   // Use incoming tabindex as default value
00642   PRInt32 tabIndex = aTabIndex? *aTabIndex : -1;
00643   PRBool disabled = tabIndex < 0;
00644   nsCOMPtr<nsIDOMXULControlElement> xulControl = 
00645     do_QueryInterface(NS_STATIC_CAST(nsIContent*, this));
00646   if (xulControl) {
00647     xulControl->GetDisabled(&disabled);
00648     if (disabled) {
00649       tabIndex = -1;  // Can't tab to disabled elements
00650     }
00651     else if (HasAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex)) {
00652       // If attribute not set, will use default value passed in
00653       xulControl->GetTabIndex(&tabIndex);
00654     }
00655     if (tabIndex != -1 && sTabFocusModelAppliesToXUL &&
00656         !(sTabFocusModel & eTabFocus_formElementsMask)) {
00657       // By default, the tab focus model doesn't apply to xul element on any system but OS X.
00658       // on OS X we're following it for UI elements (XUL) as sTabFocusModel is based on
00659       // "Full Keyboard Access" system setting (see mac/nsILookAndFeel).
00660       // both textboxes and list elements (i.e. trees and list) should always be focusable
00661       // (textboxes are handled as html:input)
00662       if (!mNodeInfo->Equals(nsXULAtoms::tree) && !mNodeInfo->Equals(nsXULAtoms::listbox))
00663         tabIndex = -1; 
00664     }
00665   }
00666 
00667   if (aTabIndex) {
00668     *aTabIndex = tabIndex;
00669   }
00670 
00671   return tabIndex >= 0 || (!disabled && HasAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex));
00672 }
00673 
00674 
00675 //----------------------------------------------------------------------
00676 // nsIScriptEventHandlerOwner interface
00677 
00678 nsresult
00679 nsXULElement::GetCompiledEventHandler(nsIAtom *aName, void** aHandler)
00680 {
00681     XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheTests);
00682     *aHandler = nsnull;
00683 
00684     nsXULPrototypeAttribute *attr =
00685         FindPrototypeAttribute(kNameSpaceID_None, aName);
00686     if (attr) {
00687         XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheHits);
00688         *aHandler = attr->mEventHandler;
00689     }
00690 
00691     return NS_OK;
00692 }
00693 
00694 nsresult
00695 nsXULElement::CompileEventHandler(nsIScriptContext* aContext,
00696                                   void* aTarget,
00697                                   nsIAtom *aName,
00698                                   const nsAString& aBody,
00699                                   const char* aURL,
00700                                   PRUint32 aLineNo,
00701                                   void** aHandler)
00702 {
00703     nsresult rv;
00704     JSObject* scopeObject;
00705 
00706     XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheSets);
00707 
00708     nsIScriptContext *context;
00709     if (mPrototype) {
00710         // It'll be shared among the instances of the prototype.
00711         // Use null for the scope object when precompiling shared
00712         // prototype scripts.
00713         scopeObject = nsnull;
00714 
00715         // Use the prototype document's special context.  Because
00716         // scopeObject is null, the JS engine has no other source of
00717         // <the-new-shared-event-handler>.__proto__ than to look in
00718         // cx->globalObject for Function.prototype.  That prototype
00719         // keeps the global object alive, so if we use this document's
00720         // global object, we'll be putting something in the prototype
00721         // that protects this document's global object from GC.
00722         // XXX sXBL/XBL2 issue! Owner or current document?
00723         nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(GetOwnerDoc());
00724         NS_ENSURE_TRUE(xuldoc, NS_ERROR_UNEXPECTED);
00725 
00726         nsCOMPtr<nsIXULPrototypeDocument> protodoc;
00727         rv = xuldoc->GetMasterPrototype(getter_AddRefs(protodoc));
00728         NS_ENSURE_SUCCESS(rv, rv);
00729         NS_ENSURE_TRUE(protodoc, NS_ERROR_UNEXPECTED);
00730 
00731         nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner =
00732             do_QueryInterface(protodoc);
00733         nsIScriptGlobalObject* global = globalOwner->GetScriptGlobalObject();
00734         NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
00735 
00736         context = global->GetContext();
00737     }
00738     else {
00739         // We don't have a prototype; do a one-off compile.
00740         NS_ASSERTION(aTarget != nsnull, "no prototype and no target?!");
00741         scopeObject = NS_REINTERPRET_CAST(JSObject*, aTarget);
00742         context = aContext;
00743     }
00744 
00745     // Compile the event handler
00746     const char *eventName = nsContentUtils::GetEventArgName(kNameSpaceID_XUL);
00747     rv = context->CompileEventHandler(scopeObject, aName, eventName, aBody,
00748                                       aURL, aLineNo, !scopeObject,
00749                                       aHandler);
00750     if (NS_FAILED(rv)) return rv;
00751 
00752     if (! scopeObject) {
00753         // If it's a shared handler, we need to bind the shared
00754         // function object to the real target.
00755 
00756         // XXX: Shouldn't this use context and not aContext?
00757         rv = aContext->BindCompiledEventHandler(aTarget, aName, *aHandler);
00758         if (NS_FAILED(rv)) return rv;
00759     }
00760 
00761     nsXULPrototypeAttribute *attr =
00762         FindPrototypeAttribute(kNameSpaceID_None, aName);
00763     if (attr) {
00764         XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheFills);
00765         attr->mEventHandler = *aHandler;
00766 
00767         if (attr->mEventHandler) {
00768             JSContext *cx = (JSContext*) context->GetNativeContext();
00769             if (!cx)
00770                 return NS_ERROR_UNEXPECTED;
00771 
00772             rv = nsContentUtils::AddJSGCRoot(&attr->mEventHandler,
00773                                              "nsXULPrototypeAttribute::mEventHandler");
00774             if (NS_FAILED(rv)) {
00775                 attr->mEventHandler = nsnull;
00776                 return rv;
00777             }
00778         }
00779     }
00780 
00781     return NS_OK;
00782 }
00783 
00784 
00785 void
00786 nsXULElement::AddListenerFor(const nsAttrName& aName,
00787                              PRBool aCompileEventHandlers)
00788 {
00789     // If appropriate, add a popup listener and/or compile the event
00790     // handler. Called when we change the element's document, create a
00791     // new element, change an attribute's value, etc.
00792     // Eventlistenener-attributes are always in the null namespace
00793     if (aName.IsAtom()) {
00794         nsIAtom *attr = aName.Atom();
00795         MaybeAddPopupListener(attr);
00796         if (aCompileEventHandlers && IsEventHandler(attr)) {
00797             nsAutoString value;
00798             GetAttr(kNameSpaceID_None, attr, value);
00799             AddScriptEventListener(attr, value);
00800         }
00801     }
00802 }
00803 
00804 void
00805 nsXULElement::MaybeAddPopupListener(nsIAtom* aLocalName)
00806 {
00807     // If appropriate, add a popup listener. Called when we change the
00808     // element's document, create a new element, change an attribute's
00809     // value, etc.
00810     if (aLocalName == nsXULAtoms::menu ||
00811         aLocalName == nsXULAtoms::contextmenu ||
00812         // XXXdwh popup and context are deprecated
00813         aLocalName == nsXULAtoms::popup ||
00814         aLocalName == nsXULAtoms::context) {
00815         AddPopupListener(aLocalName);
00816     }
00817 }
00818 
00819 //----------------------------------------------------------------------
00820 //
00821 // nsIContent interface
00822 //
00823 
00824 nsresult
00825 nsXULElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
00826                          nsIContent* aBindingParent,
00827                          PRBool aCompileEventHandlers)
00828 {
00829     NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!");
00830     // XXXbz XUL elements are confused about their current doc when they're
00831     // cloned, so we don't assert if aParent is a XUL element and aDocument is
00832     // null, even if aParent->GetCurrentDoc() is non-null
00833     //  NS_PRECONDITION(!aParent || aDocument == aParent->GetCurrentDoc(),
00834     //                  "aDocument must be current doc of aParent");
00835     NS_PRECONDITION(!aParent ||
00836                     (aParent->IsContentOfType(eXUL) && aDocument == nsnull) ||
00837                     aDocument == aParent->GetCurrentDoc(),
00838                     "aDocument must be current doc of aParent");
00839     // XXXbz we'd like to assert that GetCurrentDoc() is null, but the cloning
00840     // mess makes that impossible.  We can't even assert that aDocument ==
00841     // GetCurrentDoc() when GetCurrentDoc() is non-null, since we may be
00842     // getting inserted into a different document.  :(
00843     //    NS_PRECONDITION(!GetCurrentDoc(),
00844     //                    "Already have a document.  Unbind first!");
00845 
00846     // Note that as we recurse into the kids, they'll have a non-null
00847     // parent.  So only assert if our parent is _changing_ while we
00848     // have a parent.
00849     NS_PRECONDITION(!GetParent() || aParent == GetParent(),
00850                     "Already have a parent.  Unbind first!");
00851     NS_PRECONDITION(!GetBindingParent() ||
00852                     aBindingParent == GetBindingParent() ||
00853                     (!aBindingParent && aParent &&
00854                      aParent->GetBindingParent() == GetBindingParent()),
00855                     "Already have a binding parent.  Unbind first!");
00856 
00857     if (!aBindingParent && aParent) {
00858         aBindingParent = aParent->GetBindingParent();
00859     }
00860 
00861     // First set the binding parent
00862     mBindingParent = aBindingParent;
00863     
00864     // Now set the parent; make sure to preserve the bits we have
00865     // stashed there Note that checking whether aParent == GetParent()
00866     // is probably not worth it here.
00867     PtrBits new_bits = NS_REINTERPRET_CAST(PtrBits, aParent);
00868     new_bits |= mParentPtrBits & nsIContent::kParentBitMask;
00869     mParentPtrBits = new_bits;
00870 
00871     nsIDocument *oldOwnerDocument = GetOwnerDoc();
00872     nsIDocument *newOwnerDocument;
00873     nsNodeInfoManager* nodeInfoManager;
00874 
00875     // XXXbz sXBL/XBL2 issue!
00876 
00877     // Finally, set the document
00878     if (aDocument) {
00879         // Notify XBL- & nsIAnonymousContentCreator-generated
00880         // anonymous content that the document is changing.  XXXbz
00881         // ordering issues here?  Probably not, since
00882         // ChangeDocumentFor is just pretty broken anyway....  Need to
00883         // get it working.
00884 
00885         // XXXbz XBL doesn't handle this (asserts), and we don't really want
00886         // to be doing this during parsing anyway... sort this out.    
00887         //    aDocument->BindingManager()->ChangeDocumentFor(this, nsnull,
00888         //                                                   aDocument);
00889 
00890         // Being added to a document.
00891         mParentPtrBits |= PARENT_BIT_INDOCUMENT;
00892 
00893         newOwnerDocument = aDocument;
00894         nodeInfoManager = newOwnerDocument->NodeInfoManager();
00895     } else {
00896         newOwnerDocument = aParent->GetOwnerDoc();
00897         nodeInfoManager = aParent->GetNodeInfo()->NodeInfoManager();
00898     }
00899 
00900     // Handle a change in our owner document.
00901 
00902     if (oldOwnerDocument && oldOwnerDocument != newOwnerDocument) {
00903         // Remove all properties.
00904         nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(oldOwnerDocument);
00905         if (nsDoc) {
00906             nsDoc->SetBoxObjectFor(this, nsnull);
00907         }
00908         oldOwnerDocument->PropertyTable()->DeleteAllPropertiesFor(this);
00909     }
00910 
00911     nsresult rv;
00912     if (mNodeInfo->NodeInfoManager() != nodeInfoManager) {
00913         nsCOMPtr<nsINodeInfo> newNodeInfo;
00914         rv = nodeInfoManager->GetNodeInfo(mNodeInfo->NameAtom(),
00915                                           mNodeInfo->GetPrefixAtom(),
00916                                           mNodeInfo->NamespaceID(),
00917                                           getter_AddRefs(newNodeInfo));
00918         NS_ENSURE_SUCCESS(rv, rv);
00919         NS_ASSERTION(newNodeInfo, "GetNodeInfo lies");
00920         mNodeInfo.swap(newNodeInfo);
00921     }
00922 
00923     if (newOwnerDocument) {
00924         // we need to (re-)initialize several attributes that are dependant on
00925         // the document. Do that now.
00926         // XXXbz why do we have attributes depending on the current document?
00927         // Shouldn't they depend on the owner document?  Or is this code just
00928         // misplaced, basically?
00929         
00930         PRInt32 count = mAttrsAndChildren.AttrCount();
00931         PRBool haveLocalAttributes = (count > 0);
00932         PRInt32 i;
00933         for (i = 0; i < count; i++) {
00934             AddListenerFor(*mAttrsAndChildren.GetSafeAttrNameAt(i),
00935                            aCompileEventHandlers);
00936         }
00937 
00938         if (mPrototype) {
00939             PRInt32 count = mPrototype->mNumAttributes;
00940             for (i = 0; i < count; i++) {
00941                 nsXULPrototypeAttribute *protoattr =
00942                     &mPrototype->mAttributes[i];
00943 
00944                 // Don't clobber a locally modified attribute.
00945                 if (haveLocalAttributes &&
00946                     mAttrsAndChildren.GetAttr(protoattr->mName.LocalName(), 
00947                                               protoattr->mName.NamespaceID())) {
00948                     continue;
00949                 }
00950 
00951                 AddListenerFor(protoattr->mName, aCompileEventHandlers);
00952             }
00953         }
00954     }
00955 
00956     // Now recurse into our kids
00957     PRUint32 i;
00958     for (i = 0; i < GetChildCount(); ++i) {
00959         // The child can remove itself from the parent in BindToTree.
00960         nsCOMPtr<nsIContent> child = mAttrsAndChildren.ChildAt(i);
00961         nsresult rv = child->BindToTree(aDocument, this, aBindingParent,
00962                                         aCompileEventHandlers);
00963         NS_ENSURE_SUCCESS(rv, rv);
00964     }
00965 
00966     // XXXbz script execution during binding can trigger some of these
00967     // postcondition asserts....  But we do want that, since things will
00968     // generally be quite broken when that happens.
00969     // XXXbz we'd like to assert that we have the right GetCurrentDoc(), but
00970     // we may be being bound to a null document while we already have a
00971     // current doc, due to the cloneNode hack...  So can't assert that yet.
00972     //    NS_POSTCONDITION(aDocument == GetCurrentDoc(),
00973     //                     "Bound to wrong document");
00974     NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
00975     NS_POSTCONDITION(aBindingParent == GetBindingParent(),
00976                      "Bound to wrong binding parent");
00977 
00978     return NS_OK;
00979 }
00980 
00981 void
00982 nsXULElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
00983 {
00984     // XXXbz we'd like to assert that called didn't screw up aDeep, but I'm not
00985     // sure we can....
00986     //    NS_PRECONDITION(aDeep || (!GetCurrentDoc() && !GetBindingParent()),
00987     //                    "Shallow unbind won't clear document and binding "
00988     //                    "parent on kids!");
00989     // Make sure to unbind this node before doing the kids
00990     nsIDocument *document = GetCurrentDoc();
00991     if (document) {
00992         // Notify XBL- & nsIAnonymousContentCreator-generated
00993         // anonymous content that the document is changing.
00994         document->BindingManager()->ChangeDocumentFor(this, document, nsnull);
00995 
00996         nsCOMPtr<nsIDOMNSDocument> nsDoc(do_QueryInterface(document));
00997         nsDoc->SetBoxObjectFor(this, nsnull);
00998     }
00999 
01000     // mControllers can own objects that are implemented
01001     // in JavaScript (such as some implementations of
01002     // nsIControllers.  These objects prevent their global
01003     // object's script object from being garbage collected,
01004     // which means JS continues to hold an owning reference
01005     // to the nsGlobalWindow, which owns the document,
01006     // which owns this content.  That's a cycle, so we break
01007     // it here.  (It might be better to break this by releasing
01008     // mDocument in nsGlobalWindow::SetDocShell, but I'm not
01009     // sure whether that would fix all possible cycles through
01010     // mControllers.)
01011     nsDOMSlots* slots = GetExistingDOMSlots();
01012     if (slots) {
01013         NS_IF_RELEASE(slots->mControllers);
01014     }
01015 
01016     // XXXbz why are we nuking our listener manager?  We can get events while
01017     // not in a document!
01018     if (mListenerManager) {
01019         mListenerManager->Disconnect();
01020         mListenerManager = nsnull;
01021     }
01022 
01023     // Unset things in the reverse order from how we set them in BindToTree
01024     mParentPtrBits &= ~PARENT_BIT_INDOCUMENT;
01025   
01026     if (aNullParent) {
01027         // Just mask it out
01028         mParentPtrBits &= nsIContent::kParentBitMask;
01029     }
01030   
01031     mBindingParent = nsnull;
01032 
01033     if (aDeep) {
01034         // Do the kids.  Note that we don't want to GetChildCount(), because
01035         // that will force content generation... if we never had to generate
01036         // the content, we shouldn't force it now!
01037         PRUint32 i, n = PeekChildCount();
01038 
01039         for (i = 0; i < n; ++i) {
01040             // Note that we pass PR_FALSE for aNullParent here, since we don't
01041             // want the kids to forget us.  We _do_ want them to forget their
01042             // binding parent, though, since this only walks non-anonymous
01043             // kids.
01044             mAttrsAndChildren.ChildAt(i)->UnbindFromTree(PR_TRUE, PR_FALSE);
01045         }
01046     }
01047 }
01048 
01049 PRBool
01050 nsXULElement::IsNativeAnonymous() const
01051 {
01052     // XXX Workaround for bug 280541, wallpaper for bug 326644
01053     return Tag() == nsXULAtoms::popupgroup &&
01054            nsGenericElement::IsNativeAnonymous();
01055 }
01056 
01057 PRUint32
01058 nsXULElement::GetChildCount() const
01059 {
01060     if (NS_FAILED(EnsureContentsGenerated())) {
01061         return 0;
01062     }
01063 
01064     return PeekChildCount();
01065 }
01066 
01067 nsIContent *
01068 nsXULElement::GetChildAt(PRUint32 aIndex) const
01069 {
01070     if (NS_FAILED(EnsureContentsGenerated())) {
01071         return nsnull;
01072     }
01073 
01074     return mAttrsAndChildren.GetSafeChildAt(aIndex);
01075 }
01076 
01077 PRInt32
01078 nsXULElement::IndexOf(nsIContent* aPossibleChild) const
01079 {
01080     if (NS_FAILED(EnsureContentsGenerated())) {
01081         return -1;
01082     }
01083 
01084     return mAttrsAndChildren.IndexOfChild(aPossibleChild);
01085 }
01086 
01087 nsresult
01088 nsXULElement::InsertChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify)
01089 {
01090     nsresult rv = EnsureContentsGenerated();
01091     NS_ENSURE_SUCCESS(rv, rv);
01092 
01093     NS_PRECONDITION(nsnull != aKid, "null ptr");
01094 
01095     // Make sure that we're not trying to insert the same child
01096     // twice. If we do, the DOM APIs (e.g., GetNextSibling()), will
01097     // freak out.
01098     NS_ASSERTION(mAttrsAndChildren.IndexOfChild(aKid) < 0,
01099                  "element is already a child");
01100 
01101     PRBool isAppend = aIndex == mAttrsAndChildren.ChildCount();
01102     
01103     nsIDocument* doc = GetCurrentDoc();
01104     mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
01105 
01106     rv = mAttrsAndChildren.InsertChildAt(aKid, aIndex);
01107     NS_ENSURE_SUCCESS(rv, rv);
01108 
01109     rv = aKid->BindToTree(doc, this, nsnull, PR_TRUE);
01110     if (NS_FAILED(rv)) {
01111         mAttrsAndChildren.RemoveChildAt(aIndex);
01112         aKid->UnbindFromTree();
01113         return rv;
01114     }
01115 
01116     // XXXbz this screws up ranges, no?  Need to figure out why this is
01117     // commented and uncomment....
01118     //nsRange::OwnerChildInserted(this, aIndex);
01119 
01120     // The kid may have removed us from the document, so recheck that we're
01121     // still in the document before proceeding.  Also, the kid may have just
01122     // removed itself, in which case we don't really want to fire
01123     // ContentAppended or a mutation event.
01124     // XXXbz What if the kid just moved us in the document?  Scripts suck.  We
01125     // really need to stop running them while we're in the middle of modifying
01126     // the DOM....
01127     if (doc && doc == GetCurrentDoc() && aKid->GetParent() == this) {
01128         if (aNotify) {
01129             if (isAppend) {
01130                 doc->ContentAppended(this, aIndex);
01131             } else {
01132                 doc->ContentInserted(this, aKid, aIndex);
01133             }
01134         }
01135 
01136         if (HasMutationListeners(this,
01137                                  NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
01138             nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED, aKid);
01139             mutation.mRelatedNode =
01140                 do_QueryInterface(NS_STATIC_CAST(nsIStyledContent*, this));
01141 
01142             nsEventStatus status = nsEventStatus_eIgnore;
01143             aKid->HandleDOMEvent(nsnull, &mutation, nsnull, NS_EVENT_FLAG_INIT,
01144                                  &status);
01145         }
01146 
01147     }
01148 
01149     return NS_OK;
01150 }
01151 
01152 nsresult
01153 nsXULElement::AppendChildTo(nsIContent* aKid, PRBool aNotify)
01154 {
01155     nsresult rv = EnsureContentsGenerated();
01156     NS_ENSURE_SUCCESS(rv, rv);
01157 
01158     NS_PRECONDITION((nsnull != aKid) && (aKid != NS_STATIC_CAST(nsIStyledContent*, this)), "null ptr");
01159 
01160     nsIDocument* doc = GetCurrentDoc();
01161     mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
01162 
01163     rv = mAttrsAndChildren.AppendChild(aKid);
01164     NS_ENSURE_SUCCESS(rv, rv);
01165 
01166     rv = aKid->BindToTree(doc, this, nsnull, PR_TRUE);
01167     if (NS_FAILED(rv)) {
01168         mAttrsAndChildren.RemoveChildAt(GetChildCount() - 1);
01169         aKid->UnbindFromTree();
01170         return rv;
01171     }
01172     // ranges don't need adjustment since new child is at end of list
01173 
01174     // The kid may have removed us from the document, so recheck that we're
01175     // still in the document before proceeding.  Also, the kid may have just
01176     // removed itself, in which case we don't really want to fire
01177     // ContentAppended or a mutation event.
01178     // XXXbz What if the kid just moved us in the document?  Scripts suck.  We
01179     // really need to stop running them while we're in the middle of modifying
01180     // the DOM....
01181     if (doc && doc == GetCurrentDoc() && aKid->GetParent() == this) {
01182         if (aNotify) {
01183             doc->ContentAppended(this, mAttrsAndChildren.ChildCount() - 1);
01184         }
01185 
01186         if (HasMutationListeners(this,
01187                                  NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
01188             nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED, aKid);
01189             mutation.mRelatedNode =
01190                 do_QueryInterface(NS_STATIC_CAST(nsIStyledContent*, this));
01191 
01192             nsEventStatus status = nsEventStatus_eIgnore;
01193             aKid->HandleDOMEvent(nsnull, &mutation, nsnull, NS_EVENT_FLAG_INIT, &status);
01194         }
01195     }
01196 
01197     return NS_OK;
01198 }
01199 
01200 nsresult
01201 nsXULElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify)
01202 {
01203     nsresult rv = EnsureContentsGenerated();
01204     NS_ENSURE_SUCCESS(rv, rv);
01205 
01206     nsMutationGuard::DidMutate();
01207 
01208     nsCOMPtr<nsIContent> oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex);
01209     if (!oldKid) {
01210       return NS_OK;
01211     }
01212 
01213     // On the removal of a <treeitem>, <treechildren>, or <treecell> element,
01214     // the possibility exists that some of the items in the removed subtree
01215     // are selected (and therefore need to be deselected). We need to account for this.
01216     nsCOMPtr<nsIDOMXULMultiSelectControlElement> controlElement;
01217     nsCOMPtr<nsIListBoxObject> listBox;
01218     PRBool fireSelectionHandler = PR_FALSE;
01219 
01220     // -1 = do nothing, -2 = null out current item
01221     // anything else = index to re-set as current
01222     PRInt32 newCurrentIndex = -1;
01223 
01224     nsINodeInfo *ni = oldKid->GetNodeInfo();
01225     if (ni && ni->Equals(nsXULAtoms::listitem, kNameSpaceID_XUL)) {
01226       // This is the nasty case. We have (potentially) a slew of selected items
01227       // and cells going away.
01228       // First, retrieve the tree.
01229       // Check first whether this element IS the tree
01230       controlElement = do_QueryInterface((nsIDOMXULElement*)this);
01231 
01232       // If it's not, look at our parent
01233       if (!controlElement)
01234         rv = GetParentTree(getter_AddRefs(controlElement));
01235 
01236       nsCOMPtr<nsIDOMElement> oldKidElem = do_QueryInterface(oldKid);
01237       if (controlElement && oldKidElem) {
01238         // Iterate over all of the items and find out if they are contained inside
01239         // the removed subtree.
01240         PRInt32 length;
01241         controlElement->GetSelectedCount(&length);
01242         for (PRInt32 i = 0; i < length; i++) {
01243           nsCOMPtr<nsIDOMXULSelectControlItemElement> node;
01244           controlElement->GetSelectedItem(i, getter_AddRefs(node));
01245           // we need to QI here to do an XPCOM-correct pointercompare
01246           nsCOMPtr<nsIDOMElement> selElem = do_QueryInterface(node);
01247           if (selElem == oldKidElem &&
01248               NS_SUCCEEDED(controlElement->RemoveItemFromSelection(node))) {
01249             length--;
01250             i--;
01251             fireSelectionHandler = PR_TRUE;
01252           }
01253         }
01254 
01255         nsCOMPtr<nsIDOMXULSelectControlItemElement> curItem;
01256         controlElement->GetCurrentItem(getter_AddRefs(curItem));
01257         nsCOMPtr<nsIContent> curNode = do_QueryInterface(curItem);
01258         if (curNode && nsContentUtils::ContentIsDescendantOf(curNode, oldKid)) {
01259             // Current item going away
01260             nsCOMPtr<nsIBoxObject> box;
01261             controlElement->GetBoxObject(getter_AddRefs(box));
01262             listBox = do_QueryInterface(box);
01263             if (listBox && oldKidElem) {
01264               listBox->GetIndexOfItem(oldKidElem, &newCurrentIndex);
01265             }
01266 
01267             // If any of this fails, we'll just set the current item to null
01268             if (newCurrentIndex == -1)
01269               newCurrentIndex = -2;
01270         }
01271       }
01272     }
01273 
01274     rv = nsGenericElement::RemoveChildAt(aIndex, aNotify);
01275 
01276     if (newCurrentIndex == -2)
01277         controlElement->SetCurrentItem(nsnull);
01278     else if (newCurrentIndex > -1) {
01279         // Make sure the index is still valid
01280         PRInt32 treeRows;
01281         listBox->GetRowCount(&treeRows);
01282         if (treeRows > 0) {
01283             newCurrentIndex = PR_MIN((treeRows - 1), newCurrentIndex);
01284             nsCOMPtr<nsIDOMElement> newCurrentItem;
01285             listBox->GetItemAtIndex(newCurrentIndex, getter_AddRefs(newCurrentItem));
01286             nsCOMPtr<nsIDOMXULSelectControlItemElement> xulCurItem = do_QueryInterface(newCurrentItem);
01287             if (xulCurItem)
01288                 controlElement->SetCurrentItem(xulCurItem);
01289         } else {
01290             controlElement->SetCurrentItem(nsnull);
01291         }
01292     }
01293 
01294     nsIDocument* doc;
01295     if (fireSelectionHandler && (doc = GetCurrentDoc())) {
01296       nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(doc));
01297       nsCOMPtr<nsIDOMEvent> event;
01298       docEvent->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
01299       nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
01300 
01301       if (privateEvent) {
01302         event->InitEvent(NS_LITERAL_STRING("select"), PR_FALSE, PR_TRUE);
01303         privateEvent->SetTrusted(PR_TRUE);
01304 
01305         nsCOMPtr<nsIDOMEventTarget> target =
01306             do_QueryInterface(NS_STATIC_CAST(nsIContent *, this));
01307         NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
01308         PRBool defaultActionEnabled;
01309         target->DispatchEvent(event, &defaultActionEnabled);
01310       }
01311     }
01312 
01313     return rv;
01314 }
01315 
01316 void
01317 nsXULElement::UnregisterAccessKey(const nsAString& aOldValue)
01318 {
01319     // If someone changes the accesskey, unregister the old one
01320     //
01321     nsIDocument* doc = GetCurrentDoc();
01322     if (doc && !aOldValue.IsEmpty()) {
01323         nsIPresShell *shell = doc->GetShellAt(0);
01324 
01325         if (shell) {
01326             nsIContent *content = this;
01327 
01328             // find out what type of content node this is
01329             if (mNodeInfo->Equals(nsXULAtoms::label)) {
01330                 // For anonymous labels the unregistering must
01331                 // occur on the binding parent control.
01332                 content = GetBindingParent();
01333             }
01334 
01335             if (content) {
01336                 shell->GetPresContext()->EventStateManager()->
01337                     UnregisterAccessKey(content, aOldValue.First());
01338             }
01339         }
01340     }
01341 }
01342 
01343 // XXX attribute code swiped from nsGenericContainerElement
01344 // this class could probably just use nsGenericContainerElement
01345 // needed to maintain attribute namespace ID as well as ordering
01346 // NOTE: Changes to this function may need to be made in
01347 // |SetInlineStyleRule| as well.
01348 nsresult
01349 nsXULElement::SetAttr(PRInt32 aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix,
01350                       const nsAString& aValue, PRBool aNotify)
01351 {
01352     nsAutoString oldValue;
01353     PRBool hasListeners = PR_FALSE;
01354     PRBool modification = PR_FALSE;
01355 
01356     if (IsInDoc()) {
01357         PRBool isAccessKey = aName == nsXULAtoms::accesskey &&
01358                              aNamespaceID == kNameSpaceID_None;
01359         hasListeners = nsGenericElement::HasMutationListeners(this,
01360             NS_EVENT_BITS_MUTATION_ATTRMODIFIED);
01361 
01362         // If we have no listeners and aNotify is false, we are almost
01363         // certainly coming from the content sink and will almost certainly
01364         // have no previous value.  Even if we do, setting the value is cheap
01365         // when we have no listeners and don't plan to notify.  The check for
01366         // aNotify here is an optimization, the check for haveListeners is a
01367         // correctness issue.
01368         // The check for isAccessKey is so that we get the old value and can
01369         // unregister the old key.
01370         if (hasListeners || aNotify || isAccessKey) {
01371             // Don't do any update if old == new.
01372             const nsAttrValue* attrVal =
01373                 mAttrsAndChildren.GetAttr(aName, aNamespaceID);
01374             if (attrVal) {
01375                 modification = PR_TRUE;
01376                 attrVal->ToString(oldValue);
01377                 if (aValue.Equals(oldValue)) {
01378                     return NS_OK;
01379                 }
01380             }
01381 
01382             // If the accesskey attribute changes, unregister it here. It will
01383             // be registered for the new value in the relevant frames. Also see
01384             // nsAreaFrame, nsBoxFrame and nsTextBoxFrame's AttributeChanged
01385             // If we want to merge with nsGenericElement then we could maybe
01386             // do this in WillChangeAttr instead. That is only called when
01387             // aNotify is true, but that might be enough.
01388             if (isAccessKey) {
01389                 UnregisterAccessKey(oldValue);
01390             }
01391         }
01392     }
01393 
01394     // XXX UnsetAttr handles more attributes then we do. See bug 233642.
01395 
01396     // Parse into a nsAttrValue
01397 
01398     // WARNING!!
01399     // This code is largely duplicated in nsXULPrototypeElement::SetAttrAt.
01400     // Any changes should be made to both functions.
01401     nsAttrValue attrValue;
01402     if (aNamespaceID == kNameSpaceID_None) {
01403         if (aName == nsXULAtoms::style) {
01404             nsGenericHTMLElement::ParseStyleAttribute(this, PR_TRUE, aValue,
01405                                                       attrValue);
01406         }
01407         else if (aName == nsXULAtoms::id &&
01408                  !aValue.IsEmpty()) {
01409             // Store id as atom.
01410             // id="" means that the element has no id. Not that it has
01411             // emptystring as id.
01412             attrValue.ParseAtom(aValue);
01413         }
01414         else if (aName == nsXULAtoms::clazz) {
01415             attrValue.ParseAtomArray(aValue);
01416         }
01417         else {
01418             attrValue.ParseStringOrAtom(aValue);
01419         }
01420 
01421         // Add popup and event listeners. We can't call AddListenerFor since
01422         // the attribute isn't set yet.
01423         MaybeAddPopupListener(aName);
01424         if (IsEventHandler(aName)) {
01425             AddScriptEventListener(aName, aValue);
01426         }
01427 
01428         // Hide chrome if needed
01429         if (aName == nsXULAtoms::hidechrome &&
01430             mNodeInfo->Equals(nsXULAtoms::window)) {
01431             HideWindowChrome(NS_LITERAL_STRING("true").Equals(aValue));
01432         }
01433 
01434         // XXX need to check if they're changing an event handler: if so, then we need
01435         // to unhook the old one.
01436     }
01437     else {
01438         attrValue.ParseStringOrAtom(aValue);
01439     }
01440 
01441     return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue,
01442                             attrValue, modification, hasListeners, aNotify);
01443 }
01444 
01445 nsresult
01446 nsXULElement::SetAttrAndNotify(PRInt32 aNamespaceID,
01447                                nsIAtom* aAttribute,
01448                                nsIAtom* aPrefix,
01449                                const nsAString& aOldValue,
01450                                nsAttrValue& aParsedValue,
01451                                PRBool aModification,
01452                                PRBool aFireMutation,
01453                                PRBool aNotify)
01454 {
01455     nsresult rv;
01456     PRUint8 modType = aModification ?
01457         NS_STATIC_CAST(PRUint8, nsIDOMMutationEvent::MODIFICATION) :
01458         NS_STATIC_CAST(PRUint8, nsIDOMMutationEvent::ADDITION);
01459 
01460     nsIDocument* doc = GetCurrentDoc();
01461     mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
01462     if (aNotify && doc) {
01463         doc->AttributeWillChange(this, aNamespaceID, aAttribute);
01464     }
01465 
01466     if (aNamespaceID == kNameSpaceID_None) {
01467         rv = mAttrsAndChildren.SetAndTakeAttr(aAttribute, aParsedValue);
01468         NS_ENSURE_SUCCESS(rv, rv);
01469     }
01470     else {
01471         nsCOMPtr<nsINodeInfo> ni;
01472         rv = mNodeInfo->NodeInfoManager()->GetNodeInfo(aAttribute, aPrefix,
01473                                                        aNamespaceID,
01474                                                        getter_AddRefs(ni));
01475         NS_ENSURE_SUCCESS(rv, rv);
01476 
01477         rv = mAttrsAndChildren.SetAndTakeAttr(ni, aParsedValue);
01478         NS_ENSURE_SUCCESS(rv, rv);
01479     }
01480 
01481     if (doc) {
01482         nsRefPtr<nsXBLBinding> binding =
01483             doc->BindingManager()->GetBinding(this);
01484         if (binding) {
01485             binding->AttributeChanged(aAttribute, aNamespaceID, PR_FALSE, aNotify);
01486         }
01487 
01488         if (aNotify) {
01489             doc->AttributeChanged(this, aNamespaceID, aAttribute, modType);
01490         }
01491 
01492         if (aFireMutation) {
01493             nsCOMPtr<nsIDOMEventTarget> node =
01494                 do_QueryInterface(NS_STATIC_CAST(nsIContent *, this));
01495             nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED, node);
01496 
01497             nsAutoString attrName;
01498             aAttribute->ToString(attrName);
01499             nsCOMPtr<nsIDOMAttr> attrNode;
01500             GetAttributeNode(attrName, getter_AddRefs(attrNode));
01501             mutation.mRelatedNode = attrNode;
01502 
01503             mutation.mAttrName = aAttribute;
01504             nsAutoString newValue;
01505             // We don't really need to call GetAttr here, but lets do it
01506             // anyway to ease future codeshare with nsGenericHTMLElement
01507             // which has to call GetAttr here due to enums.
01508             GetAttr(aNamespaceID, aAttribute, newValue);
01509             if (!newValue.IsEmpty()) {
01510                 mutation.mNewAttrValue = do_GetAtom(newValue);
01511             }
01512             if (!aOldValue.IsEmpty()) {
01513                 mutation.mPrevAttrValue = do_GetAtom(aOldValue);
01514             }
01515             mutation.mAttrChange = modType;
01516             nsEventStatus status = nsEventStatus_eIgnore;
01517             HandleDOMEvent(nsnull, &mutation, nsnull,
01518                            NS_EVENT_FLAG_INIT, &status);
01519         }
01520     }
01521 
01522     return NS_OK;
01523 }
01524 
01525 const nsAttrName*
01526 nsXULElement::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const
01527 {
01528     NS_ConvertUTF16toUTF8 name(aStr);
01529     const nsAttrName* attrName =
01530         mAttrsAndChildren.GetExistingAttrNameFromQName(name);
01531     if (attrName) {
01532         return attrName;
01533     }
01534 
01535     if (mPrototype) {
01536         PRUint32 i;
01537         for (i = 0; i < mPrototype->mNumAttributes; ++i) {
01538             attrName = &mPrototype->mAttributes[i].mName;
01539             if (attrName->QualifiedNameEquals(name)) {
01540                 return attrName;
01541             }
01542         }
01543     }
01544 
01545     return nsnull;
01546 }
01547 
01548 nsresult
01549 nsXULElement::GetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
01550                       nsAString& aResult) const
01551 {
01552     NS_ASSERTION(nsnull != aName, "must have attribute name");
01553     NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown,
01554                  "must have a real namespace ID!");
01555 
01556     const nsAttrValue* val = FindLocalOrProtoAttr(aNameSpaceID, aName);
01557 
01558     if (!val) {
01559         // Since we are returning a success code we'd better do
01560         // something about the out parameters (someone may have
01561         // given us a non-empty string).
01562         aResult.Truncate();
01563         return NS_CONTENT_ATTR_NOT_THERE;
01564     }
01565 
01566     val->ToString(aResult);
01567 
01568     return aResult.IsEmpty() ? NS_CONTENT_ATTR_NO_VALUE :
01569                                NS_CONTENT_ATTR_HAS_VALUE;
01570 }
01571 
01572 PRBool
01573 nsXULElement::HasAttr(PRInt32 aNameSpaceID, nsIAtom* aName) const
01574 {
01575     NS_ASSERTION(nsnull != aName, "must have attribute name");
01576     NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown,
01577                  "must have a real namespace ID!");
01578 
01579     return mAttrsAndChildren.GetAttr(aName, aNameSpaceID) ||
01580            FindPrototypeAttribute(aNameSpaceID, aName);
01581 }
01582 
01583 nsresult
01584 nsXULElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotify)
01585 {
01586     NS_ASSERTION(nsnull != aName, "must have attribute name");
01587     nsresult rv;
01588 
01589     // Because It's Hard to maintain a magic ``unset'' value in
01590     // the local attributes, we'll fault all the attributes,
01591     // unhook ourselves from the prototype, and then remove the
01592     // local copy of the attribute that we want to unset. In
01593     // other words, we'll become ``heavyweight''.
01594     //
01595     // We can avoid this if the attribute isn't in the prototype,
01596     // then we just need to remove it locally
01597 
01598     nsXULPrototypeAttribute *protoattr =
01599         FindPrototypeAttribute(aNameSpaceID, aName);
01600     if (protoattr) {
01601         // We've got an attribute on the prototype, so we need to
01602         // fully fault and remove the local copy.
01603         rv = MakeHeavyweight();
01604         NS_ENSURE_SUCCESS(rv, rv);
01605 
01606 #ifdef DEBUG_ATTRIBUTE_STATS
01607         gFaults.UnsetAttr++; gFaults.Total++;
01608         fprintf(stderr, "XUL: Faulting for UnsetAttr: %d/%d\n",
01609                 gFaults.UnsetAttr, gFaults.Total);
01610 #endif
01611     }
01612 
01613     PRInt32 index = mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID);
01614     if (index < 0) {
01615         NS_ASSERTION(!protoattr, "we used to have a protoattr, we should now "
01616                                  "have a normal one");
01617 
01618         return NS_OK;
01619     }
01620 
01621     nsAutoString oldValue;
01622     GetAttr(aNameSpaceID, aName, oldValue);
01623 
01624     nsIDocument* doc = GetCurrentDoc();
01625     mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
01626     if (aNotify && doc) {
01627         doc->AttributeWillChange(this, aNameSpaceID, aName);
01628     }
01629 
01630     PRBool hasMutationListeners =
01631         HasMutationListeners(this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED);
01632     nsCOMPtr<nsIDOMAttr> attrNode;
01633     if (hasMutationListeners) {
01634         nsAutoString attrName;
01635         aName->ToString(attrName);
01636         GetAttributeNode(attrName, getter_AddRefs(attrNode));
01637     }
01638 
01639     nsDOMSlots *slots = GetExistingDOMSlots();
01640     if (slots && slots->mAttributeMap) {
01641       slots->mAttributeMap->DropAttribute(aNameSpaceID, aName);
01642     }
01643 
01644     nsAttrValue ignored;
01645     rv = mAttrsAndChildren.RemoveAttrAt(index, ignored);
01646     NS_ENSURE_SUCCESS(rv, rv);
01647 
01648     // XXX if the RemoveAttrAt() call fails, we might end up having removed
01649     // the attribute from the attribute map even though the attribute is still
01650     // on the element
01651     // https://bugzilla.mozilla.org/show_bug.cgi?id=296205
01652 
01653     // Deal with modification of magical attributes that side-effect
01654     // other things.
01655     // XXX Know how to remove POPUP event listeners when an attribute is unset?
01656 
01657     if (aNameSpaceID == kNameSpaceID_None) {
01658         if (aName == nsXULAtoms::hidechrome &&
01659             mNodeInfo->Equals(nsXULAtoms::window)) {
01660             HideWindowChrome(PR_FALSE);
01661         }
01662 
01663         // If the accesskey attribute is removed, unregister it here
01664         // Also see nsAreaFrame, nsBoxFrame and nsTextBoxFrame's AttributeChanged
01665         if (aName == nsXULAtoms::accesskey || aName == nsXULAtoms::control) {
01666             UnregisterAccessKey(oldValue);
01667         }
01668 
01669         // Check to see if the OBSERVES attribute is being unset.  If so, we
01670         // need to remove our broadcaster goop completely.
01671         if (doc && (aName == nsXULAtoms::observes ||
01672                           aName == nsXULAtoms::command)) {
01673             nsCOMPtr<nsIDOMXULDocument> xuldoc = do_QueryInterface(doc);
01674             if (xuldoc) {
01675                 // Do a getElementById to retrieve the broadcaster
01676                 nsCOMPtr<nsIDOMElement> broadcaster;
01677                 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
01678                 domDoc->GetElementById(oldValue, getter_AddRefs(broadcaster));
01679                 if (broadcaster) {
01680                     xuldoc->RemoveBroadcastListenerFor(broadcaster, this,
01681                                                        NS_LITERAL_STRING("*"));
01682                 }
01683             }
01684         }
01685     }
01686 
01687     if (doc) {
01688         if (hasMutationListeners) {
01689             nsCOMPtr<nsIDOMEventTarget> node(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this)));
01690             nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED, node);
01691 
01692             mutation.mRelatedNode = attrNode;
01693             mutation.mAttrName = aName;
01694 
01695             if (!oldValue.IsEmpty())
01696               mutation.mPrevAttrValue = do_GetAtom(oldValue);
01697             mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
01698 
01699             nsEventStatus status = nsEventStatus_eIgnore;
01700             this->HandleDOMEvent(nsnull, &mutation, nsnull,
01701                                  NS_EVENT_FLAG_INIT, &status);
01702         }
01703 
01704         nsRefPtr<nsXBLBinding> binding =
01705             doc->BindingManager()->GetBinding(this);
01706         if (binding)
01707             binding->AttributeChanged(aName, aNameSpaceID, PR_TRUE, aNotify);
01708 
01709         if (aNotify) {
01710             doc->AttributeChanged(this, aNameSpaceID, aName,
01711                                   nsIDOMMutationEvent::REMOVAL);
01712         }
01713     }
01714 
01715     return NS_OK;
01716 }
01717 
01718 nsresult
01719 nsXULElement::GetAttrNameAt(PRUint32 aIndex, PRInt32* aNameSpaceID,
01720                             nsIAtom** aName, nsIAtom** aPrefix) const
01721 {
01722 #ifdef DEBUG_ATTRIBUTE_STATS
01723     int proto = mPrototype ? mPrototype->mNumAttributes : 0;
01724     fprintf(stderr, "GANA: %p[%d] of %d/%d:", (void *)this, aIndex,
01725             mAttrsAndChildren.AttrCount(), proto);
01726 #endif
01727 
01728     PRUint32 localAttrCount = mAttrsAndChildren.AttrCount();
01729     if (aIndex < localAttrCount) {
01730         const nsAttrName* name = mAttrsAndChildren.GetSafeAttrNameAt(aIndex);
01731 
01732         *aNameSpaceID = name->NamespaceID();
01733         NS_ADDREF(*aName = name->LocalName());
01734         NS_IF_ADDREF(*aPrefix = name->GetPrefix());
01735 #ifdef DEBUG_ATTRIBUTE_STATS
01736         fprintf(stderr, " local!\n");
01737 #endif
01738         return NS_OK;
01739     }
01740 
01741     aIndex -= localAttrCount;
01742 
01743     if (mPrototype && aIndex < mPrototype->mNumAttributes) {
01744         // XXX This code looks very wrong. See bug 232639.
01745 
01746         PRBool skip;
01747         nsXULPrototypeAttribute* attr;
01748         do {
01749             attr = &mPrototype->mAttributes[aIndex];
01750             skip = localAttrCount &&
01751                    mAttrsAndChildren.GetAttr(attr->mName.LocalName(),
01752                                              attr->mName.NamespaceID());
01753 #ifdef DEBUG_ATTRIBUTE_STATS
01754             if (skip)
01755                 fprintf(stderr, " [skip %d/%d]", aIndex, aIndex + localAttrCount);
01756 #endif
01757         } while (skip && aIndex++ < mPrototype->mNumAttributes);
01758 
01759         if (aIndex <= mPrototype->mNumAttributes) {
01760 #ifdef DEBUG_ATTRIBUTE_STATS
01761             fprintf(stderr, " proto[%d]!\n", aIndex);
01762 #endif
01763             *aNameSpaceID = attr->mName.NamespaceID();
01764             NS_ADDREF(*aName = attr->mName.LocalName());
01765             NS_IF_ADDREF(*aPrefix = attr->mName.GetPrefix());
01766 
01767             return NS_OK;
01768         }
01769         // else, we are out of attrs to return, fall-through
01770     }
01771 
01772 #ifdef DEBUG_ATTRIBUTE_STATS
01773     fprintf(stderr, " not found\n");
01774 #endif
01775 
01776     *aNameSpaceID = kNameSpaceID_None;
01777     *aName = nsnull;
01778     *aPrefix = nsnull;
01779 
01780     return NS_ERROR_ILLEGAL_VALUE;
01781 }
01782 
01783 PRUint32
01784 nsXULElement::GetAttrCount() const
01785 {
01786     PRBool haveLocalAttributes;
01787 
01788     PRUint32 count = mAttrsAndChildren.AttrCount();
01789     haveLocalAttributes = count > 0;
01790 
01791 #ifdef DEBUG_ATTRIBUTE_STATS
01792     int dups = 0;
01793 #endif
01794 
01795     if (mPrototype) {
01796         for (PRUint32 i = 0; i < mPrototype->mNumAttributes; i++) {
01797             nsAttrName* attrName = &mPrototype->mAttributes[i].mName;
01798             
01799             if (!haveLocalAttributes ||
01800                 !mAttrsAndChildren.GetAttr(attrName->LocalName(),
01801                                            attrName->NamespaceID())) {
01802                 ++count;
01803 #ifdef DEBUG_ATTRIBUTE_STATS
01804             } else {
01805                 dups++;
01806 #endif
01807             }
01808         }
01809     }
01810 
01811 #ifdef DEBUG_ATTRIBUTE_STATS
01812     {
01813         int local = mAttrsAndChildren.AttrCount();
01814         int proto = mPrototype ? mPrototype->mNumAttributes : 0;
01815         nsAutoString tagstr;
01816         mNodeInfo->GetName(tagstr);
01817         char *tagcstr = ToNewCString(tagstr);
01818 
01819         fprintf(stderr, "GAC: %p has %d+%d-%d=%d <%s%s>\n", (void *)this,
01820                 local, proto, dups, count, mPrototype ? "" : "*", tagcstr);
01821         nsMemory::Free(tagcstr);
01822     }
01823 #endif
01824 
01825     return count;
01826 }
01827 
01828 
01829 #ifdef DEBUG
01830 static void
01831 rdf_Indent(FILE* out, PRInt32 aIndent)
01832 {
01833     for (PRInt32 i = aIndent; --i >= 0; ) fputs("  ", out);
01834 }
01835 
01836 void
01837 nsXULElement::List(FILE* out, PRInt32 aIndent) const
01838 {
01839     NS_PRECONDITION(IsInDoc(), "bad content");
01840 
01841     PRUint32 i;
01842 
01843     rdf_Indent(out, aIndent);
01844     fputs("<XUL", out);
01845     if (HasDOMSlots()) fputs("*", out);
01846     fputs(" ", out);
01847 
01848     nsAutoString as;
01849     mNodeInfo->GetQualifiedName(as);
01850     fputs(NS_LossyConvertUCS2toASCII(as).get(), out);
01851 
01852     fprintf(out, "@%p", (void *)this);
01853 
01854     PRUint32 nattrs = GetAttrCount();
01855 
01856     for (i = 0; i < nattrs; ++i) {
01857         nsCOMPtr<nsIAtom> attr;
01858         nsCOMPtr<nsIAtom> prefix;
01859         PRInt32 nameSpaceID;
01860         GetAttrNameAt(i, &nameSpaceID, getter_AddRefs(attr),
01861                       getter_AddRefs(prefix));
01862 
01863         nsAutoString v;
01864         GetAttr(nameSpaceID, attr, v);
01865 
01866         fputs(" ", out);
01867 
01868         nsAutoString s;
01869 
01870         if (prefix) {
01871             prefix->ToString(s);
01872 
01873             fputs(NS_LossyConvertUCS2toASCII(s).get(), out);
01874             fputs(":", out);
01875         }
01876 
01877         attr->ToString(s);
01878 
01879         fputs(NS_LossyConvertUCS2toASCII(s).get(), out);
01880         fputs("=", out);
01881         fputs(NS_LossyConvertUCS2toASCII(v).get(), out);
01882     }
01883 
01884     PRUint32 nchildren = GetChildCount();
01885 
01886     if (nchildren) {
01887         fputs("\n", out);
01888 
01889         for (i = 0; i < nchildren; ++i) {
01890             GetChildAt(i)->List(out, aIndent + 1);
01891         }
01892 
01893         rdf_Indent(out, aIndent);
01894     }
01895     fputs(">\n", out);
01896 
01897     // XXX sXBL/XBL2 issue! Owner or current document?
01898     nsIDocument* doc = GetCurrentDoc();
01899     if (doc) {
01900         nsIBindingManager *bindingManager = doc->BindingManager();
01901         nsCOMPtr<nsIDOMNodeList> anonymousChildren;
01902         bindingManager->GetAnonymousNodesFor(NS_STATIC_CAST(nsIContent*, NS_CONST_CAST(nsXULElement*, this)),
01903                                              getter_AddRefs(anonymousChildren));
01904 
01905         if (anonymousChildren) {
01906             PRUint32 length;
01907             anonymousChildren->GetLength(&length);
01908             if (length) {
01909                 rdf_Indent(out, aIndent);
01910                 fputs("anonymous-children<\n", out);
01911 
01912                 for (PRUint32 i2 = 0; i2 < length; ++i2) {
01913                     nsCOMPtr<nsIDOMNode> node;
01914                     anonymousChildren->Item(i2, getter_AddRefs(node));
01915                     nsCOMPtr<nsIContent> child = do_QueryInterface(node);
01916                     child->List(out, aIndent + 1);
01917                 }
01918 
01919                 rdf_Indent(out, aIndent);
01920                 fputs(">\n", out);
01921             }
01922         }
01923 
01924         PRBool hasContentList;
01925         bindingManager->HasContentListFor(NS_STATIC_CAST(nsIContent*, NS_CONST_CAST(nsXULElement*, this)),
01926                                           &hasContentList);
01927 
01928         if (hasContentList) {
01929             nsCOMPtr<nsIDOMNodeList> contentList;
01930             bindingManager->GetContentListFor(NS_STATIC_CAST(nsIContent*, NS_CONST_CAST(nsXULElement*, this)),
01931                                               getter_AddRefs(contentList));
01932 
01933             NS_ASSERTION(contentList != nsnull, "oops, binding manager lied");
01934 
01935             PRUint32 length;
01936             contentList->GetLength(&length);
01937             if (length) {
01938                 rdf_Indent(out, aIndent);
01939                 fputs("content-list<\n", out);
01940 
01941                 for (PRUint32 i2 = 0; i2 < length; ++i2) {
01942                     nsCOMPtr<nsIDOMNode> node;
01943                     contentList->Item(i2, getter_AddRefs(node));
01944                     nsCOMPtr<nsIContent> child = do_QueryInterface(node);
01945                     child->List(out, aIndent + 1);
01946                 }
01947 
01948                 rdf_Indent(out, aIndent);
01949                 fputs(">\n", out);
01950             }
01951         }
01952     }
01953 }
01954 #endif
01955 
01956 nsresult
01957 nsXULElement::HandleDOMEvent(nsPresContext* aPresContext, nsEvent* aEvent,
01958                              nsIDOMEvent** aDOMEvent, PRUint32 aFlags,
01959                              nsEventStatus* aEventStatus)
01960 {
01961     // Make sure to tell the event that dispatch has started.
01962     NS_MARK_EVENT_DISPATCH_STARTED(aEvent);
01963 
01964     nsresult ret = NS_OK;
01965 
01966     PRBool retarget = PR_FALSE;
01967     PRBool externalDOMEvent = PR_FALSE;
01968     nsCOMPtr<nsIDOMEventTarget> oldTarget;
01969 
01970     nsIDOMEvent* domEvent = nsnull;
01971     if (NS_EVENT_FLAG_INIT & aFlags) {
01972         nsIAtom* tag = Tag();
01973         if (aEvent->message == NS_XUL_COMMAND && tag != nsXULAtoms::command) {
01974             // See if we have a command elt.  If so, we execute on the command instead
01975             // of on our content element.
01976             nsAutoString command;
01977             GetAttr(kNameSpaceID_None, nsXULAtoms::command, command);
01978             if (!command.IsEmpty()) {
01979                 // XXX sXBL/XBL2 issue! Owner or current document?
01980                 nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(GetCurrentDoc()));
01981                 NS_ENSURE_STATE(domDoc);
01982                 nsCOMPtr<nsIDOMElement> commandElt;
01983                 domDoc->GetElementById(command, getter_AddRefs(commandElt));
01984                 nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt));
01985                 if (commandContent) {
01986                     // Create a new command event to the element pointed to
01987                     // by the command attribute.  The new event's sourceEvent
01988                     // will be the original event that we're handling.
01989                     aEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH;
01990 
01991                     nsXULCommandEvent event(NS_IS_TRUSTED_EVENT(aEvent),
01992                                             NS_XUL_COMMAND, nsnull);
01993                     if (aEvent->eventStructType == NS_XUL_COMMAND_EVENT) {
01994                         nsXULCommandEvent *orig =
01995                             NS_STATIC_CAST(nsXULCommandEvent*, aEvent);
01996 
01997                         event.isShift = orig->isShift;
01998                         event.isControl = orig->isControl;
01999                         event.isAlt = orig->isAlt;
02000                         event.isMeta = orig->isMeta;
02001                     } else {
02002                         NS_WARNING("Incorrect eventStructType for command event");
02003                     }
02004 
02005                     // Make sure we have a DOMEvent.
02006                     if (aDOMEvent) {
02007                         if (*aDOMEvent) {
02008                             externalDOMEvent = PR_TRUE;
02009                         }
02010                     } else {
02011                         aDOMEvent = &domEvent;
02012                     }
02013 
02014                     if (!*aDOMEvent) {
02015                         nsCOMPtr<nsIEventListenerManager> lm;
02016                         ret = GetListenerManager(getter_AddRefs(lm));
02017                         NS_ENSURE_SUCCESS(ret, ret);
02018 
02019                         ret = lm->CreateEvent(aPresContext, aEvent,
02020                                               EmptyString(), aDOMEvent);
02021                         NS_ENSURE_SUCCESS(ret, ret);
02022 
02023                         // We need to explicitly set the target here, because
02024                         // the DOM implementation will try to compute the
02025                         // target from the frame. If we don't have a frame
02026                         // (e.g., we're a key), then that breaks.
02027                         nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
02028                             do_QueryInterface(*aDOMEvent);
02029                         NS_ENSURE_TRUE(privateEvent, NS_ERROR_FAILURE);
02030 
02031                         nsCOMPtr<nsIDOMEventTarget> target =
02032                             do_QueryInterface(NS_STATIC_CAST(nsIContent*,
02033                                                              this));
02034                         privateEvent->SetTarget(target);
02035                     }
02036                     event.sourceEvent = *aDOMEvent;
02037 
02038                     ret = commandContent->HandleDOMEvent(aPresContext,
02039                                                          &event, nsnull,
02040                                                          NS_EVENT_FLAG_INIT,
02041                                                          aEventStatus);
02042 
02043                     // We're leaving the DOM event loop so if we created a
02044                     // DOM event, release here.  If externalDOMEvent is
02045                     // set, the event was passed in, and we don't own it.
02046                     if (*aDOMEvent && !externalDOMEvent) {
02047                         nsrefcnt rc;
02048                         NS_RELEASE2(*aDOMEvent, rc);
02049                         // Note: we expect one outstanding reference to
02050                         // *aDOMEvent, because we set it in event.sourceEvent.
02051                         if (rc > 1) {
02052                             // Okay, so someone in the DOM loop (a listener,
02053                             // JS object) still has a ref to the DOM Event,
02054                             // but the internal data hasn't been malloc'd.
02055                             // Force a copy of the data here so the DOM Event
02056                             // is still valid.
02057                             nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
02058                                 do_QueryInterface(*aDOMEvent);
02059                             if (privateEvent) {
02060                                 privateEvent->DuplicatePrivateData();
02061                             }
02062                         }
02063                     }
02064                     return ret;
02065                 }
02066                 else {
02067                     NS_WARNING("A XUL element is attached to a command that doesn't exist!\n");
02068                     return NS_ERROR_FAILURE;
02069                 }
02070             }
02071         }
02072         if (aDOMEvent) {
02073             if (*aDOMEvent)
02074                 externalDOMEvent = PR_TRUE;
02075         }
02076         else
02077             aDOMEvent = &domEvent;
02078 
02079         aEvent->flags |= aFlags;
02080         aFlags &= ~(NS_EVENT_FLAG_CANT_BUBBLE | NS_EVENT_FLAG_CANT_CANCEL);
02081         aFlags |= NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_CAPTURE;
02082 
02083         if (!externalDOMEvent) {
02084             // In order for the event to have a proper target for events that don't go through
02085             // the presshell (onselect, oncommand, oncreate, ondestroy) we need to set our target
02086             // ourselves. Also, key sets and menus don't have frames and therefore need their
02087             // targets explicitly specified.
02088             //
02089             // We need this for drag&drop as well since the mouse may have moved into a different
02090             // frame between the initial mouseDown and the generation of the drag gesture.
02091             // Obviously, the target should be the content/frame where the mouse was depressed,
02092             // not one computed by the current mouse location.
02093             if (aEvent->message == NS_XUL_COMMAND || aEvent->message == NS_XUL_POPUP_SHOWING ||
02094                 aEvent->message == NS_XUL_POPUP_SHOWN || aEvent->message == NS_XUL_POPUP_HIDING ||
02095                 aEvent->message == NS_XUL_POPUP_HIDDEN || aEvent->message == NS_FORM_SELECTED ||
02096                 aEvent->message == NS_XUL_BROADCAST || aEvent->message == NS_XUL_COMMAND_UPDATE ||
02097                 aEvent->message == NS_XUL_CLICK || aEvent->message == NS_DRAGDROP_GESTURE ||
02098                 tag == nsXULAtoms::menu || tag == nsXULAtoms::menuitem ||
02099                 tag == nsXULAtoms::menulist || tag == nsXULAtoms::menubar ||
02100                 tag == nsXULAtoms::menupopup || tag == nsXULAtoms::key ||
02101                 tag == nsXULAtoms::keyset) {
02102 
02103                 nsCOMPtr<nsIEventListenerManager> listenerManager;
02104                 if (NS_FAILED(ret = GetListenerManager(getter_AddRefs(listenerManager)))) {
02105                     NS_ERROR("Unable to instantiate a listener manager on this event.");
02106                     return ret;
02107                 }
02108                 nsAutoString empty;
02109                 if (NS_FAILED(ret = listenerManager->CreateEvent(aPresContext, aEvent, empty, aDOMEvent))) {
02110                     NS_ERROR("This event will fail without the ability to create the event early.");
02111                     return ret;
02112                 }
02113 
02114                 // We need to explicitly set the target here, because the
02115                 // DOM implementation will try to compute the target from
02116                 // the frame. If we don't have a frame (e.g., we're a
02117                 // menu), then that breaks.
02118                 nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(domEvent);
02119                 if (privateEvent) {
02120                     nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this)));
02121                     privateEvent->SetTarget(target);
02122                 }
02123                 else
02124                     return NS_ERROR_FAILURE;
02125 
02126                 // if we are a XUL click, we have the private event set.
02127                 // now switch to a left mouse click for the duration of the event
02128                 if (aEvent->message == NS_XUL_CLICK)
02129                     aEvent->message = NS_MOUSE_LEFT_CLICK;
02130             }
02131         }
02132     }
02133     else if (aEvent->message == NS_IMAGE_LOAD)
02134         return NS_OK; // Don't let these events bubble or be captured.  Just allow them
02135                     // on the target image.
02136 
02137     // Find out whether we're anonymous.
02138     // XXX Workaround bug 280541 without regressing bug 251197
02139     if (nsGenericElement::IsNativeAnonymous()) {
02140         retarget = PR_TRUE;
02141     } else {
02142         nsIContent* parent = GetParent();
02143         if (parent) {
02144             if (*aDOMEvent) {
02145                 (*aDOMEvent)->GetTarget(getter_AddRefs(oldTarget));
02146                 nsCOMPtr<nsIContent> content(do_QueryInterface(oldTarget));
02147                 if (content && content->GetBindingParent() == parent)
02148                     retarget = PR_TRUE;
02149             } else if (GetBindingParent() == parent) {
02150                 retarget = PR_TRUE;
02151             }
02152         }
02153     }
02154 
02155     // determine the parent:
02156     nsCOMPtr<nsIContent> parent;
02157     // XXX sXBL/XBL2 issue! Owner or current document?
02158     nsIDocument* doc = GetCurrentDoc();
02159     if (doc) {
02160         // check for an anonymous parent
02161         doc->BindingManager()->GetInsertionParent(this,
02162                                                   getter_AddRefs(parent));
02163     }
02164 
02165     if (!parent) {
02166         // if we didn't find an anonymous parent, use the explicit one,
02167         // whether it's null or not...
02168         parent = GetParent();
02169     }
02170 
02171     if (retarget || (parent != GetParent())) {
02172         if (!*aDOMEvent) {
02173             // We haven't made a DOMEvent yet.  Force making one now.
02174             nsCOMPtr<nsIEventListenerManager> listenerManager;
02175             if (NS_FAILED(ret = GetListenerManager(getter_AddRefs(listenerManager)))) {
02176                 return ret;
02177             }
02178             nsAutoString empty;
02179             if (NS_FAILED(ret = listenerManager->CreateEvent(aPresContext, aEvent, empty, aDOMEvent)))
02180                 return ret;
02181 
02182             if (!*aDOMEvent) {
02183                 return NS_ERROR_FAILURE;
02184             }
02185         }
02186 
02187         nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
02188             do_QueryInterface(*aDOMEvent);
02189         if (!privateEvent) {
02190             return NS_ERROR_FAILURE;
02191         }
02192 
02193         (*aDOMEvent)->GetTarget(getter_AddRefs(oldTarget));
02194 
02195         PRBool hasOriginal;
02196         privateEvent->HasOriginalTarget(&hasOriginal);
02197 
02198         if (!hasOriginal)
02199             privateEvent->SetOriginalTarget(oldTarget);
02200 
02201         if (retarget) {
02202             nsCOMPtr<nsIDOMEventTarget> target =
02203                 do_QueryInterface(GetParent());
02204             privateEvent->SetTarget(target);
02205       }
02206     }
02207 
02208     //Capturing stage evaluation
02209     if (NS_EVENT_FLAG_CAPTURE & aFlags &&
02210         !(aEvent->eventStructType == NS_MUTATION_EVENT &&
02211           IsAnonymousForEvents())) {
02212         //Initiate capturing phase.  Special case first call to document
02213         if (parent) {
02214             parent->HandleDOMEvent(aPresContext, aEvent, aDOMEvent, aFlags & NS_EVENT_CAPTURE_MASK, aEventStatus);
02215         }
02216         else if (doc) {
02217             ret = doc->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
02218                                       aFlags & NS_EVENT_CAPTURE_MASK,
02219                                       aEventStatus);
02220         }
02221     }
02222 
02223 
02224     if (retarget) {
02225         // The event originated beneath us, and we performed a retargeting.
02226         // We need to restore the original target of the event.
02227         nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
02228         if (privateEvent)
02229             privateEvent->SetTarget(oldTarget);
02230     }
02231 
02232     //Local handling stage
02233     if (mListenerManager && !(aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH)) {
02234         aEvent->flags |= aFlags;
02235         nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this)));
02236         mListenerManager->HandleEvent(aPresContext, aEvent, aDOMEvent, target, aFlags, aEventStatus);
02237         aEvent->flags &= ~aFlags;
02238     }
02239 
02240     if (retarget) {
02241         // The event originated beneath us, and we need to perform a
02242         // retargeting.
02243         nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
02244         if (privateEvent) {
02245             nsCOMPtr<nsIDOMEventTarget> parentTarget =
02246                 do_QueryInterface(GetParent());
02247             privateEvent->SetTarget(parentTarget);
02248         }
02249     }
02250 
02251     //Bubbling stage
02252     if (NS_EVENT_FLAG_BUBBLE & aFlags &&
02253         !(aEvent->eventStructType == NS_MUTATION_EVENT &&
02254           IsAnonymousForEvents())) {
02255         if (parent != nsnull) {
02256             // We have a parent. Let them field the event.
02257             ret = parent->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
02258                                          aFlags & NS_EVENT_BUBBLE_MASK, aEventStatus);
02259       }
02260         else if (IsInDoc()) {
02261         // We must be the document root. The event should bubble to the
02262         // document.
02263         ret = GetCurrentDoc()->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
02264                                             aFlags & NS_EVENT_BUBBLE_MASK, aEventStatus);
02265         }
02266     }
02267 
02268     if (retarget) {
02269         // The event originated beneath us, and we performed a retargeting.
02270         // We need to restore the original target of the event.
02271         nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(*aDOMEvent);
02272         if (privateEvent)
02273             privateEvent->SetTarget(oldTarget);
02274     }
02275 
02276     if (NS_EVENT_FLAG_INIT & aFlags) {
02277         // We're leaving the DOM event loop so if we created a DOM event,
02278         // release here.  If externalDOMEvent is set the event was passed in
02279         // and we don't own it
02280         if (*aDOMEvent && !externalDOMEvent) {
02281             nsrefcnt rc;
02282             NS_RELEASE2(*aDOMEvent, rc);
02283             if (0 != rc) {
02284                 // Okay, so someone in the DOM loop (a listener, JS object)
02285                 // still has a ref to the DOM Event but the internal data
02286                 // hasn't been malloc'd.  Force a copy of the data here so the
02287                 // DOM Event is still valid.
02288                 nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
02289                     do_QueryInterface(*aDOMEvent);
02290                 if (privateEvent) {
02291                     privateEvent->DuplicatePrivateData();
02292                 }
02293             }
02294             aDOMEvent = nsnull;
02295         }
02296 
02297         // Now that we're done with this event, remove the flag that says
02298         // we're in the process of dispatching this event.
02299         NS_MARK_EVENT_DISPATCH_DONE(aEvent);
02300     }
02301 
02302     return ret;
02303 }
02304 
02305 
02306 PRUint32
02307 nsXULElement::ContentID() const
02308 {
02309     return 0;
02310 }
02311 
02312 void
02313 nsXULElement::SetContentID(PRUint32 aID)
02314 {
02315 }
02316 
02317 nsresult
02318 nsXULElement::RangeAdd(nsIDOMRange* aRange)
02319 {
02320     // rdf content does not yet support DOM ranges
02321     return NS_OK;
02322 }
02323 
02324 
02325 void
02326 nsXULElement::RangeRemove(nsIDOMRange* aRange)
02327 {
02328     // rdf content does not yet support DOM ranges
02329 }
02330 
02331 
02332 const nsVoidArray *
02333 nsXULElement::GetRangeList() const
02334 {
02335     // XUL content does not yet support DOM ranges
02336     return nsnull;
02337 }
02338 
02339 // XXX This _should_ be an implementation method, _not_ publicly exposed :-(
02340 NS_IMETHODIMP
02341 nsXULElement::GetResource(nsIRDFResource** aResource)
02342 {
02343     nsAutoString id;
02344     nsresult rv = GetAttr(kNameSpaceID_None, nsXULAtoms::ref, id);
02345 
02346     if (rv != NS_CONTENT_ATTR_HAS_VALUE) {
02347         rv = GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
02348     }
02349 
02350     if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
02351         rv = nsXULContentUtils::RDFService()->
02352             GetUnicodeResource(id, aResource);
02353         NS_ENSURE_SUCCESS(rv, rv);
02354     }
02355     else {
02356         *aResource = nsnull;
02357     }
02358 
02359     return NS_OK;
02360 }
02361 
02362 
02363 NS_IMETHODIMP
02364 nsXULElement::GetDatabase(nsIRDFCompositeDataSource** aDatabase)
02365 {
02366     nsCOMPtr<nsIXULTemplateBuilder> builder;
02367     GetBuilder(getter_AddRefs(builder));
02368 
02369     if (builder)
02370         builder->GetDatabase(aDatabase);
02371     else
02372         *aDatabase = nsnull;
02373 
02374     return NS_OK;
02375 }
02376 
02377 
02378 NS_IMETHODIMP
02379 nsXULElement::GetBuilder(nsIXULTemplateBuilder** aBuilder)
02380 {
02381     *aBuilder = nsnull;
02382 
02383     // XXX sXBL/XBL2 issue! Owner or current document?
02384     nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(GetCurrentDoc());
02385     if (xuldoc)
02386         xuldoc->GetTemplateBuilderFor(this, aBuilder);
02387 
02388     return NS_OK;
02389 }
02390 
02391 
02392 //----------------------------------------------------------------------
02393 // Implementation methods
02394 
02395 nsresult
02396 nsXULElement::EnsureContentsGenerated(void) const
02397 {
02398     if (GetFlags() & XUL_ELEMENT_CHILDREN_MUST_BE_REBUILT) {
02399         // Ensure that the element is actually _in_ the document tree;
02400         // otherwise, somebody is trying to generate children for a node
02401         // that's not currently in the content model.
02402         NS_PRECONDITION(IsInDoc(), "element not in tree");
02403         if (!IsInDoc())
02404             return NS_ERROR_NOT_INITIALIZED;
02405 
02406         // XXX hack because we can't use "mutable"
02407         nsXULElement* unconstThis = NS_CONST_CAST(nsXULElement*, this);
02408 
02409         // Clear this value *first*, so we can re-enter the nsIContent
02410         // getters if needed.
02411         unconstThis->ClearLazyState(eChildrenMustBeRebuilt);
02412 
02413         // Walk up our ancestor chain, looking for an element with a
02414         // XUL content model builder attached to it.
02415         nsIContent* element = unconstThis;
02416 
02417         do {
02418             nsCOMPtr<nsIDOMXULElement> xulele = do_QueryInterface(element);
02419             if (xulele) {
02420                 nsCOMPtr<nsIXULTemplateBuilder> builder;
02421                 xulele->GetBuilder(getter_AddRefs(builder));
02422                 if (builder) {
02423                     if (HasAttr(kNameSpaceID_None, nsXULAtoms::xulcontentsgenerated)) {
02424                         unconstThis->ClearLazyState(eChildrenMustBeRebuilt);
02425                         return NS_OK;
02426                     }
02427 
02428                     return builder->CreateContents(unconstThis);
02429                 }
02430             }
02431 
02432             element = element->GetParent();
02433         } while (element);
02434 
02435         NS_ERROR("lazy state set with no XUL content builder in ancestor chain");
02436         return NS_ERROR_UNEXPECTED;
02437     }
02438 
02439     return NS_OK;
02440 }
02441 
02442 // nsIStyledContent Implementation
02444 nsIAtom*
02445 nsXULElement::GetID() const
02446 {
02447     const nsAttrValue* attrVal = FindLocalOrProtoAttr(kNameSpaceID_None, nsXULAtoms::id);
02448 
02449     NS_ASSERTION(!attrVal ||
02450                  attrVal->Type() == nsAttrValue::eAtom ||
02451                  (attrVal->Type() == nsAttrValue::eString &&
02452                   attrVal->GetStringValue().IsEmpty()),
02453                  "unexpected attribute type");
02454 
02455     if (attrVal && attrVal->Type() == nsAttrValue::eAtom) {
02456         return attrVal->GetAtomValue();
02457     }
02458     return nsnull;
02459 }
02460 
02461 const nsAttrValue*
02462 nsXULElement::GetClasses() const
02463 {
02464     return FindLocalOrProtoAttr(kNameSpaceID_None, nsXULAtoms::clazz);
02465 }
02466 
02467 NS_IMETHODIMP_(PRBool)
02468 nsXULElement::HasClass(nsIAtom* aClass, PRBool /*aCaseSensitive*/) const
02469 {
02470     const nsAttrValue* val = FindLocalOrProtoAttr(kNameSpaceID_None, nsXULAtoms::clazz);
02471     if (val) {
02472         if (val->Type() == nsAttrValue::eAtom) {
02473             return aClass == val->GetAtomValue();
02474         }
02475         if (val->Type() == nsAttrValue::eAtomArray) {
02476             return val->GetAtomArrayValue()->IndexOf(aClass) >= 0;
02477         }
02478     }
02479 
02480     return PR_FALSE;
02481 }
02482 
02483 NS_IMETHODIMP
02484 nsXULElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
02485 {
02486     return NS_OK;
02487 }
02488 
02489 nsICSSStyleRule*
02490 nsXULElement::GetInlineStyleRule()
02491 {
02492     // Fetch the cached style rule from the attributes.
02493     const nsAttrValue* attrVal = FindLocalOrProtoAttr(kNameSpaceID_None, nsXULAtoms::style);
02494 
02495     if (attrVal && attrVal->Type() == nsAttrValue::eCSSStyleRule) {
02496         return attrVal->GetCSSStyleRuleValue();
02497     }
02498 
02499     return nsnull;
02500 }
02501 
02502 NS_IMETHODIMP
02503 nsXULElement::SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify)
02504 {
02505     PRBool hasListeners = PR_FALSE;
02506     PRBool modification = PR_FALSE;
02507     nsAutoString oldValueStr;
02508 
02509     if (IsInDoc()) {
02510         hasListeners = nsGenericElement::HasMutationListeners(this,
02511             NS_EVENT_BITS_MUTATION_ATTRMODIFIED);
02512 
02513         // We can't compare the stringvalues of the old and the new rules
02514         // since both will point to the same declaration and thus will be
02515         // the same.
02516         if (hasListeners || aNotify) {
02517             modification = !!mAttrsAndChildren.GetAttr(nsXULAtoms::style);
02518         }
02519     }
02520 
02521     nsAttrValue attrValue(aStyleRule);
02522 
02523     return SetAttrAndNotify(kNameSpaceID_None, nsXULAtoms::style, nsnull,
02524                             oldValueStr, attrValue, modification, hasListeners,
02525                             aNotify);
02526 }
02527 
02528 nsChangeHint
02529 nsXULElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
02530                                      PRInt32 aModType) const
02531 {
02532     nsChangeHint retval(NS_STYLE_HINT_NONE);
02533 
02534     if (aAttribute == nsXULAtoms::value &&
02535         (aModType == nsIDOMMutationEvent::REMOVAL ||
02536          aModType == nsIDOMMutationEvent::ADDITION)) {
02537       nsIAtom *tag = Tag();
02538       if (tag == nsXULAtoms::label || tag == nsXULAtoms::description)
02539         // Label and description dynamically morph between a normal
02540         // block and a cropping single-line XUL text frame.  If the
02541         // value attribute is being added or removed, then we need to
02542         // return a hint of frame change.  (See bugzilla bug 95475 for
02543         // details.)
02544         retval = NS_STYLE_HINT_FRAMECHANGE;
02545     } else {
02546         // if left or top changes we reflow. This will happen in xul
02547         // containers that manage positioned children such as a
02548         // bulletinboard.
02549         if (nsXULAtoms::left == aAttribute || nsXULAtoms::top == aAttribute)
02550             retval = NS_STYLE_HINT_REFLOW;
02551     }
02552 
02553     return retval;
02554 }
02555 
02556 NS_IMETHODIMP_(PRBool)
02557 nsXULElement::IsAttributeMapped(const nsIAtom* aAttribute) const
02558 {
02559     return PR_FALSE;
02560 }
02561 
02562 nsIAtom *
02563 nsXULElement::GetIDAttributeName() const
02564 {
02565     return nsXULAtoms::id;
02566 }
02567 
02568 nsIAtom *
02569 nsXULElement::GetClassAttributeName() const
02570 {
02571     return nsXULAtoms::clazz;
02572 }
02573 
02574 // Controllers Methods
02575 NS_IMETHODIMP
02576 nsXULElement::GetControllers(nsIControllers** aResult)
02577 {
02578     if (! Controllers()) {
02579         nsDOMSlots* slots = GetDOMSlots();
02580         if (!slots)
02581           return NS_ERROR_OUT_OF_MEMORY;
02582 
02583         nsresult rv;
02584         rv = NS_NewXULControllers(nsnull, NS_GET_IID(nsIControllers),
02585                                   NS_REINTERPRET_CAST(void**, &slots->mControllers));
02586 
02587         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a controllers");
02588         if (NS_FAILED(rv)) return rv;
02589     }
02590 
02591     *aResult = Controllers();
02592     NS_IF_ADDREF(*aResult);
02593     return NS_OK;
02594 }
02595 
02596 NS_IMETHODIMP
02597 nsXULElement::GetBoxObject(nsIBoxObject** aResult)
02598 {
02599   *aResult = nsnull;
02600 
02601   // XXX sXBL/XBL2 issue! Owner or current document?
02602   // Be sure to get the same document as the NS_ENSURE_TRUE uses in
02603   // nsDocument.cpp::GetBoxObjectFor().
02604   nsCOMPtr<nsIDOMNSDocument> nsDoc(do_QueryInterface(GetOwnerDoc()));
02605 
02606   return nsDoc ? nsDoc->GetBoxObjectFor(this, aResult) : NS_ERROR_FAILURE;
02607 }
02608 
02609 // Methods for setting/getting attributes from nsIDOMXULElement
02610 #define NS_IMPL_XUL_STRING_ATTR(_method, _atom)                     \
02611   NS_IMETHODIMP                                                     \
02612   nsXULElement::Get##_method(nsAString& aReturn)                    \
02613   {                                                                 \
02614     return GetAttr(kNameSpaceID_None, nsXULAtoms::_atom, aReturn);  \
02615   }                                                                 \
02616   NS_IMETHODIMP                                                     \
02617   nsXULElement::Set##_method(const nsAString& aValue)               \
02618   {                                                                 \
02619     return SetAttr(kNameSpaceID_None, nsXULAtoms::_atom, aValue,    \
02620                    PR_TRUE);                                        \
02621   }
02622 
02623 #define NS_IMPL_XUL_BOOL_ATTR(_method, _atom)                       \
02624   NS_IMETHODIMP                                                     \
02625   nsXULElement::Get##_method(PRBool* aResult)                       \
02626   {                                                                 \
02627     *aResult = BoolAttrIsTrue(nsXULAtoms::_atom);                   \
02628                                                                     \
02629     return NS_OK;                                                   \
02630   }                                                                 \
02631   NS_IMETHODIMP                                                     \
02632   nsXULElement::Set##_method(PRBool aValue)                         \
02633   {                                                                 \
02634     if (aValue)                                                     \
02635       SetAttr(kNameSpaceID_None, nsXULAtoms::_atom,                 \
02636               NS_LITERAL_STRING("true"), PR_TRUE);                  \
02637     else                                                            \
02638       UnsetAttr(kNameSpaceID_None, nsXULAtoms::_atom, PR_TRUE);     \
02639                                                                     \
02640     return NS_OK;                                                   \
02641   }
02642 
02643 
02644 NS_IMPL_XUL_STRING_ATTR(Id, id)
02645 NS_IMPL_XUL_STRING_ATTR(ClassName, clazz)
02646 NS_IMPL_XUL_STRING_ATTR(Align, align)
02647 NS_IMPL_XUL_STRING_ATTR(Dir, dir)
02648 NS_IMPL_XUL_STRING_ATTR(Flex, flex)
02649 NS_IMPL_XUL_STRING_ATTR(FlexGroup, flexgroup)
02650 NS_IMPL_XUL_STRING_ATTR(Ordinal, ordinal)
02651 NS_IMPL_XUL_STRING_ATTR(Orient, orient)
02652 NS_IMPL_XUL_STRING_ATTR(Pack, pack)
02653 NS_IMPL_XUL_BOOL_ATTR(Hidden, hidden)
02654 NS_IMPL_XUL_BOOL_ATTR(Collapsed, collapsed)
02655 NS_IMPL_XUL_BOOL_ATTR(AllowEvents, allowevents)
02656 NS_IMPL_XUL_STRING_ATTR(Observes, observes)
02657 NS_IMPL_XUL_STRING_ATTR(Menu, menu)
02658 NS_IMPL_XUL_STRING_ATTR(ContextMenu, contextmenu)
02659 NS_IMPL_XUL_STRING_ATTR(Tooltip, tooltip)
02660 NS_IMPL_XUL_STRING_ATTR(Width, width)
02661 NS_IMPL_XUL_STRING_ATTR(Height, height)
02662 NS_IMPL_XUL_STRING_ATTR(MinWidth, minwidth)
02663 NS_IMPL_XUL_STRING_ATTR(MinHeight, minheight)
02664 NS_IMPL_XUL_STRING_ATTR(MaxWidth, maxwidth)
02665 NS_IMPL_XUL_STRING_ATTR(MaxHeight, maxheight)
02666 NS_IMPL_XUL_STRING_ATTR(Persist, persist)
02667 NS_IMPL_XUL_STRING_ATTR(Left, left)
02668 NS_IMPL_XUL_STRING_ATTR(Top, top)
02669 NS_IMPL_XUL_STRING_ATTR(Datasources, datasources)
02670 NS_IMPL_XUL_STRING_ATTR(Ref, ref)
02671 NS_IMPL_XUL_STRING_ATTR(TooltipText, tooltiptext)
02672 NS_IMPL_XUL_STRING_ATTR(StatusText, statustext)
02673 
02674 nsresult
02675 nsXULElement::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
02676 {
02677     nsDOMSlots* slots = GetDOMSlots();
02678     NS_ENSURE_TRUE(slots, NS_ERROR_OUT_OF_MEMORY);
02679 
02680     if (!slots->mStyle) {
02681         nsresult rv;
02682         if (!gCSSOMFactory) {
02683             rv = CallGetService(kCSSOMFactoryCID, &gCSSOMFactory);
02684             NS_ENSURE_SUCCESS(rv, rv);
02685         }
02686 
02687         rv = gCSSOMFactory->CreateDOMCSSAttributeDeclaration(this,
02688                 getter_AddRefs(slots->mStyle));
02689         NS_ENSURE_SUCCESS(rv, rv);
02690     }
02691 
02692     NS_IF_ADDREF(*aStyle = slots->mStyle);
02693 
02694     return NS_OK;
02695 }
02696 
02697 NS_IMETHODIMP
02698 nsXULElement::GetParentTree(nsIDOMXULMultiSelectControlElement** aTreeElement)
02699 {
02700     for (nsIContent* current = GetParent(); current;
02701          current = current->GetParent()) {
02702         if (current->GetNodeInfo()->Equals(nsXULAtoms::listbox,
02703                                            kNameSpaceID_XUL)) {
02704             CallQueryInterface(current, aTreeElement);
02705             // XXX returning NS_OK because that's what the code used to do;
02706             // is that the right thing, though?
02707 
02708             return NS_OK;
02709         }
02710     }
02711 
02712     return NS_OK;
02713 }
02714 
02715 NS_IMETHODIMP
02716 nsXULElement::Focus()
02717 {
02718     if (!nsGenericElement::ShouldFocus(this)) {
02719         return NS_OK;
02720     }
02721 
02722     nsIDocument* doc = GetCurrentDoc();
02723     // What kind of crazy tries to focus an element without a doc?
02724     if (!doc)
02725         return NS_OK;
02726 
02727     // Obtain a presentation context and then call SetFocus.
02728     if (doc->GetNumberOfShells() == 0)
02729         return NS_OK;
02730 
02731     nsIPresShell *shell = doc->GetShellAt(0);
02732 
02733     // Set focus
02734     nsCOMPtr<nsPresContext> context = shell->GetPresContext();
02735     SetFocus(context);
02736 
02737     return NS_OK;
02738 }
02739 
02740 NS_IMETHODIMP
02741 nsXULElement::Blur()
02742 {
02743     nsIDocument* doc = GetCurrentDoc();
02744     // What kind of crazy tries to blur an element without a doc?
02745     if (!doc)
02746         return NS_OK;
02747 
02748     // Obtain a presentation context and then call SetFocus.
02749     if (doc->GetNumberOfShells() == 0)
02750         return NS_OK;
02751 
02752     nsIPresShell *shell = doc->GetShellAt(0);
02753 
02754     // Set focus
02755     nsCOMPtr<nsPresContext> context = shell->GetPresContext();
02756     RemoveFocus(context);
02757 
02758     return NS_OK;
02759 }
02760 
02761 NS_IMETHODIMP
02762 nsXULElement::Click()
02763 {
02764     if (BoolAttrIsTrue(nsXULAtoms::disabled))
02765         return NS_OK;
02766 
02767     nsCOMPtr<nsIDocument> doc = GetCurrentDoc(); // Strong just in case
02768     if (doc) {
02769         PRUint32 numShells = doc->GetNumberOfShells();
02770         // strong ref to PresContext so events don't destroy it
02771         nsCOMPtr<nsPresContext> context;
02772 
02773         for (PRUint32 i = 0; i < numShells; ++i) {
02774             nsIPresShell *shell = doc->GetShellAt(i);
02775             context = shell->GetPresContext();
02776 
02777             PRBool isCallerChrome = nsContentUtils::IsCallerChrome();
02778 
02779             nsMouseEvent eventDown(isCallerChrome, NS_MOUSE_LEFT_BUTTON_DOWN,
02780                                    nsnull, nsMouseEvent::eReal);
02781             nsMouseEvent eventUp(isCallerChrome, NS_MOUSE_LEFT_BUTTON_UP,
02782                                  nsnull, nsMouseEvent::eReal);
02783             nsMouseEvent eventClick(isCallerChrome, NS_XUL_CLICK, nsnull,
02784                                     nsMouseEvent::eReal);
02785 
02786             // send mouse down
02787             nsEventStatus status = nsEventStatus_eIgnore;
02788             HandleDOMEvent(context, &eventDown,  nsnull, NS_EVENT_FLAG_INIT,
02789                            &status);
02790 
02791             // send mouse up
02792             status = nsEventStatus_eIgnore;  // reset status
02793             HandleDOMEvent(context, &eventUp, nsnull, NS_EVENT_FLAG_INIT,
02794                            &status);
02795 
02796             // send mouse click
02797             status = nsEventStatus_eIgnore;  // reset status
02798             HandleDOMEvent(context, &eventClick, nsnull, NS_EVENT_FLAG_INIT,
02799                            &status);
02800         }
02801     }
02802 
02803     // oncommand is fired when an element is clicked...
02804     return DoCommand();
02805 }
02806 
02807 NS_IMETHODIMP
02808 nsXULElement::DoCommand()
02809 {
02810     nsCOMPtr<nsIDocument> doc = GetCurrentDoc(); // strong just in case
02811     if (doc) {
02812         PRUint32 numShells = doc->GetNumberOfShells();
02813         nsCOMPtr<nsPresContext> context;
02814 
02815         for (PRUint32 i = 0; i < numShells; ++i) {
02816             nsIPresShell *shell = doc->GetShellAt(i);
02817             context = shell->GetPresContext();
02818 
02819             nsEventStatus status = nsEventStatus_eIgnore;
02820             nsXULCommandEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull);
02821             HandleDOMEvent(context, &event, nsnull, NS_EVENT_FLAG_INIT,
02822                            &status);
02823         }
02824     }
02825 
02826     return NS_OK;
02827 }
02828 
02829 // nsIFocusableContent interface and helpers
02830 
02831 void
02832 nsXULElement::SetFocus(nsPresContext* aPresContext)
02833 {
02834     if (BoolAttrIsTrue(nsXULAtoms::disabled))
02835         return;
02836 
02837     aPresContext->EventStateManager()->SetContentState(this,
02838                                                        NS_EVENT_STATE_FOCUS);
02839 }
02840 
02841 void
02842 nsXULElement::RemoveFocus(nsPresContext* aPresContext)
02843 {
02844 }
02845 
02846 nsIContent *
02847 nsXULElement::GetBindingParent() const
02848 {
02849     return mBindingParent;
02850 }
02851 
02852 PRBool
02853 nsXULElement::IsContentOfType(PRUint32 aFlags) const
02854 {
02855     return !(aFlags & ~(eELEMENT | eXUL));
02856 }
02857 
02858 nsresult
02859 nsXULElement::AddPopupListener(nsIAtom* aName)
02860 {
02861     // Add a popup listener to the element
02862     nsresult rv;
02863 
02864     nsCOMPtr<nsIXULPopupListener> popupListener =
02865         do_CreateInstance(kXULPopupListenerCID, &rv);
02866     NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to create an instance of the popup listener object.");
02867     if (NS_FAILED(rv)) return rv;
02868 
02869     XULPopupType popupType;
02870     if (aName == nsXULAtoms::context || aName == nsXULAtoms::contextmenu) {
02871         popupType = eXULPopupType_context;
02872     }
02873     else {
02874         popupType = eXULPopupType_popup;
02875     }
02876 
02877     // Add a weak reference to the node.
02878     popupListener->Init(this, popupType);
02879 
02880     // Add the popup as a listener on this element.
02881     nsCOMPtr<nsIDOMEventListener> eventListener = do_QueryInterface(popupListener);
02882     nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this)));
02883     NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
02884     target->AddEventListener(NS_LITERAL_STRING("mousedown"), eventListener, PR_FALSE);
02885     target->AddEventListener(NS_LITERAL_STRING("contextmenu"), eventListener, PR_FALSE);
02886 
02887     return NS_OK;
02888 }
02889 
02890 //*****************************************************************************
02891 // nsXULElement::nsIChromeEventHandler
02892 //*****************************************************************************
02893 
02894 NS_IMETHODIMP nsXULElement::HandleChromeEvent(nsPresContext* aPresContext,
02895    nsEvent* aEvent, nsIDOMEvent** aDOMEvent, PRUint32 aFlags,
02896    nsEventStatus* aEventStatus)
02897 {
02898   // XXX This is a disgusting hack to prevent the doc from going
02899   // away until after we've finished handling the event.
02900   // We will be coming up with a better general solution later.
02901   nsCOMPtr<nsIDocument> kungFuDeathGrip(GetCurrentDoc());
02902   return HandleDOMEvent(aPresContext, aEvent, aDOMEvent, aFlags,aEventStatus);
02903 }
02904 
02905 //----------------------------------------------------------------------
02906 
02907 const nsAttrValue*
02908 nsXULElement::FindLocalOrProtoAttr(PRInt32 aNamespaceID, nsIAtom *aName) const
02909 {
02910 
02911     const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNamespaceID);
02912     if (val) {
02913         return val;
02914     }
02915 
02916     nsXULPrototypeAttribute *protoattr =
02917         FindPrototypeAttribute(aNamespaceID, aName);
02918     if (protoattr) {
02919         return &protoattr->mValue;
02920     }
02921 
02922     return nsnull;
02923 }
02924 
02925 
02926 nsXULPrototypeAttribute *
02927 nsXULElement::FindPrototypeAttribute(PRInt32 aNamespaceID,
02928                                      nsIAtom* aLocalName) const
02929 {
02930     if (!mPrototype) {
02931         return nsnull;
02932     }
02933 
02934     PRUint32 i, count = mPrototype->mNumAttributes;
02935     if (aNamespaceID == kNameSpaceID_None) {
02936         // Common case so optimize for this
02937         for (i = 0; i < count; ++i) {
02938             nsXULPrototypeAttribute *protoattr = &mPrototype->mAttributes[i];
02939             if (protoattr->mName.Equals(aLocalName)) {
02940                 return protoattr;
02941             }
02942         }
02943     }
02944     else {
02945         for (i = 0; i < count; ++i) {
02946             nsXULPrototypeAttribute *protoattr = &mPrototype->mAttributes[i];
02947             if (protoattr->mName.Equals(aLocalName, aNamespaceID)) {
02948                 return protoattr;
02949             }
02950         }
02951     }
02952 
02953     return nsnull;
02954 }
02955 
02956 nsresult nsXULElement::MakeHeavyweight()
02957 {
02958     if (!mPrototype)
02959         return NS_OK;           // already heavyweight
02960 
02961     nsRefPtr<nsXULPrototypeElement> proto;
02962     proto.swap(mPrototype);
02963 
02964     PRBool hadAttributes = mAttrsAndChildren.AttrCount() > 0;
02965 
02966     PRUint32 i;
02967     nsresult rv;
02968     for (i = 0; i < proto->mNumAttributes; ++i) {
02969         nsXULPrototypeAttribute* protoattr = &proto->mAttributes[i];
02970 
02971         // We might have a local value for this attribute, in which case
02972         // we don't want to copy the prototype's value.
02973         if (hadAttributes &&
02974             mAttrsAndChildren.GetAttr(protoattr->mName.LocalName(),
02975                                       protoattr->mName.NamespaceID())) {
02976             continue;
02977         }
02978 
02979         // XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName
02980         nsAttrValue attrValue(protoattr->mValue);
02981         if (protoattr->mName.IsAtom()) {
02982             rv = mAttrsAndChildren.SetAndTakeAttr(protoattr->mName.Atom(), attrValue);
02983         }
02984         else {
02985             rv = mAttrsAndChildren.SetAndTakeAttr(protoattr->mName.NodeInfo(),
02986                                                   attrValue);
02987         }
02988         NS_ENSURE_SUCCESS(rv, rv);
02989     }
02990     return NS_OK;
02991 }
02992 
02993 nsresult
02994 nsXULElement::HideWindowChrome(PRBool aShouldHide)
02995 {
02996     nsIDocument* doc = GetCurrentDoc();
02997     if (!doc)
02998       return NS_ERROR_UNEXPECTED;
02999 
03000     // only top level chrome documents can hide the window chrome
03001     if (doc->GetParentDocument())
03002       return NS_OK;
03003 
03004     nsIPresShell *shell = doc->GetShellAt(0);
03005 
03006     if (shell) {
03007         nsIContent* content = NS_STATIC_CAST(nsIContent*, this);
03008         nsIFrame* frame = nsnull;
03009         shell->GetPrimaryFrameFor(content, &frame);
03010 
03011         nsPresContext *presContext = shell->GetPresContext();
03012 
03013         if (frame && presContext && presContext->IsChrome()) {
03014             nsIView* view = frame->GetClosestView();
03015 
03016             if (view) {
03017                 // XXXldb Um, not all views have widgets...
03018                 view->GetWidget()->HideWindowChrome(aShouldHide);
03019             }
03020         }
03021     }
03022 
03023     return NS_OK;
03024 }
03025 
03026 PRBool
03027 nsXULElement::BoolAttrIsTrue(nsIAtom* aName)
03028 {
03029     const nsAttrValue* attr =
03030         FindLocalOrProtoAttr(kNameSpaceID_None, aName);
03031 
03032     return attr && attr->Type() == nsAttrValue::eAtom &&
03033            attr->GetAtomValue() == nsXULAtoms::_true;
03034 }
03035 
03036 //----------------------------------------------------------------------
03037 //
03038 // nsXULPrototypeAttribute
03039 //
03040 
03041 nsXULPrototypeAttribute::~nsXULPrototypeAttribute()
03042 {
03043     MOZ_COUNT_DTOR(nsXULPrototypeAttribute);
03044     if (mEventHandler)
03045         nsContentUtils::RemoveJSGCRoot(&mEventHandler);
03046 }
03047 
03048 
03049 //----------------------------------------------------------------------
03050 //
03051 // nsXULPrototypeElement
03052 //
03053 
03054 nsresult
03055 nsXULPrototypeElement::Serialize(nsIObjectOutputStream* aStream,
03056                                  nsIScriptContext* aContext,
03057                                  const nsCOMArray<nsINodeInfo> *aNodeInfos)
03058 {
03059     nsresult rv;
03060 
03061     // Write basic prototype data
03062     rv = aStream->Write32(mType);
03063 
03064     // Write Node Info
03065     PRInt32 index = aNodeInfos->IndexOf(mNodeInfo);
03066     NS_ASSERTION(index >= 0, "unknown nsINodeInfo index");
03067     rv |= aStream->Write32(index);
03068 
03069     // Write Attributes
03070     rv |= aStream->Write32(mNumAttributes);
03071 
03072     nsAutoString attributeValue;
03073     PRUint32 i;
03074     for (i = 0; i < mNumAttributes; ++i) {
03075         nsCOMPtr<nsINodeInfo> ni;
03076         if (mAttributes[i].mName.IsAtom()) {
03077             mNodeInfo->NodeInfoManager()->
03078                 GetNodeInfo(mAttributes[i].mName.Atom(), nsnull,
03079                             kNameSpaceID_None, getter_AddRefs(ni));
03080             NS_ASSERTION(ni, "the nodeinfo should already exist");
03081         }
03082         else {
03083             ni = mAttributes[i].mName.NodeInfo();
03084         }
03085 
03086         index = aNodeInfos->IndexOf(ni);
03087         NS_ASSERTION(index >= 0, "unknown nsINodeInfo index");
03088         rv |= aStream->Write32(index);
03089 
03090         mAttributes[i].mValue.ToString(attributeValue);
03091         rv |= aStream->WriteWStringZ(attributeValue.get());
03092     }
03093 
03094     // Now write children
03095     rv |= aStream->Write32(PRUint32(mNumChildren));
03096     for (i = 0; i < mNumChildren; i++) {
03097         nsXULPrototypeNode* child = mChildren[i];
03098         switch (child->mType) {
03099         case eType_Element:
03100         case eType_Text:
03101             rv |= child->Serialize(aStream, aContext, aNodeInfos);
03102             break;
03103         case eType_Script:
03104             rv |= aStream->Write32(child->mType);
03105             nsXULPrototypeScript* script = NS_STATIC_CAST(nsXULPrototypeScript*, child);
03106 
03107             rv |= aStream->Write8(script->mOutOfLine);
03108             if (! script->mOutOfLine) {
03109                 rv |= script->Serialize(aStream, aContext, aNodeInfos);
03110             } else {
03111                 rv |= aStream->WriteCompoundObject(script->mSrcURI,
03112                                                    NS_GET_IID(nsIURI),
03113                                                    PR_TRUE);
03114 
03115                 if (script->mJSObject) {
03116                     // This may return NS_OK without muxing script->mSrcURI's
03117                     // data into the FastLoad file, in the case where that
03118                     // muxed document is already there (written by a prior
03119                     // session, or by an earlier FastLoad episode during this
03120                     // session).
03121                     rv |= script->SerializeOutOfLine(aStream, aContext);
03122                 }
03123             }
03124             break;
03125         }
03126     }
03127 
03128     return rv;
03129 }
03130 
03131 nsresult
03132 nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream,
03133                                    nsIScriptContext* aContext,
03134                                    nsIURI* aDocumentURI,
03135                                    const nsCOMArray<nsINodeInfo> *aNodeInfos)
03136 {
03137     NS_PRECONDITION(aNodeInfos, "missing nodeinfo array");
03138     nsresult rv;
03139 
03140     // Read Node Info
03141     PRUint32 number;
03142     rv = aStream->Read32(&number);
03143     mNodeInfo = aNodeInfos->SafeObjectAt(number);
03144     if (!mNodeInfo)
03145         return NS_ERROR_UNEXPECTED;
03146 
03147     // Read Attributes
03148     rv |= aStream->Read32(&number);
03149     mNumAttributes = PRInt32(number);
03150 
03151     PRUint32 i;
03152     if (mNumAttributes > 0) {
03153         mAttributes = new nsXULPrototypeAttribute[mNumAttributes];
03154         if (! mAttributes)
03155             return NS_ERROR_OUT_OF_MEMORY;
03156 
03157         nsAutoString attributeValue;
03158         for (i = 0; i < mNumAttributes; ++i) {
03159             rv |= aStream->Read32(&number);
03160             nsINodeInfo* ni = aNodeInfos->SafeObjectAt(number);
03161             if (!ni)
03162                 return NS_ERROR_UNEXPECTED;
03163 
03164             mAttributes[i].mName.SetTo(ni);
03165 
03166             rv |= aStream->ReadString(attributeValue);
03167             rv |= SetAttrAt(i, attributeValue, aDocumentURI);
03168         }
03169     }
03170 
03171     rv |= aStream->Read32(&number);
03172     mNumChildren = PRInt32(number);
03173 
03174     if (mNumChildren > 0) {
03175         mChildren = new nsXULPrototypeNode*[mNumChildren];
03176         if (! mChildren)
03177             return NS_ERROR_OUT_OF_MEMORY;
03178 
03179         memset(mChildren, 0, sizeof(nsXULPrototypeNode*) * mNumChildren);
03180 
03181         for (i = 0; i < mNumChildren; i++) {
03182             rv |= aStream->Read32(&number);
03183             Type childType = (Type)number;
03184 
03185             nsXULPrototypeNode* child = nsnull;
03186 
03187             switch (childType) {
03188             case eType_Element:
03189                 child = new nsXULPrototypeElement();
03190                 if (! child)
03191                     return NS_ERROR_OUT_OF_MEMORY;
03192                 child->mType = childType;
03193 
03194                 rv |= child->Deserialize(aStream, aContext, aDocumentURI,
03195                                          aNodeInfos);
03196                 break;
03197             case eType_Text:
03198                 child = new nsXULPrototypeText();
03199                 if (! child)
03200                     return NS_ERROR_OUT_OF_MEMORY;
03201                 child->mType = childType;
03202 
03203                 rv |= child->Deserialize(aStream, aContext, aDocumentURI,
03204                                          aNodeInfos);
03205                 break;
03206             case eType_Script: {
03207                 // language version/options obtained during deserialization.
03208                 // Don't clobber rv here, since it might already be a failure!
03209                 nsresult result;
03210                 nsXULPrototypeScript* script =
03211                     new nsXULPrototypeScript(0, nsnull, PR_FALSE, &result);
03212                 if (! script)
03213                     return NS_ERROR_OUT_OF_MEMORY;
03214                 if (NS_FAILED(result)) {
03215                     delete script;
03216                     return result;
03217                 }
03218                 child = script;
03219                 child->mType = childType;
03220 
03221                 rv |= aStream->Read8(&script->mOutOfLine);
03222                 if (! script->mOutOfLine) {
03223                     rv |= script->Deserialize(aStream, aContext, aDocumentURI,
03224                                               aNodeInfos);
03225                 } else {
03226                     rv |= aStream->ReadObject(PR_TRUE, getter_AddRefs(script->mSrcURI));
03227 
03228                     rv |= script->DeserializeOutOfLine(aStream, aContext);
03229                 }
03230                 break;
03231             }
03232             }
03233 
03234             mChildren[i] = child;
03235 
03236             // Oh dear. Something failed during the deserialization.
03237             // We don't know what.  But likely consequences of failed
03238             // deserializations included calls to |AbortFastLoads| which
03239             // shuts down the FastLoadService and closes our streams.
03240             // If that happens, next time through this loop, we die a messy
03241             // death. So, let's just fail now, and propagate that failure
03242             // upward so that the ChromeProtocolHandler knows it can't use
03243             // a cached chrome channel for this.
03244             if (NS_FAILED(rv))
03245                 return rv;
03246         }
03247     }
03248 
03249     return rv;
03250 }
03251 
03252 nsresult
03253 nsXULPrototypeElement::SetAttrAt(PRUint32 aPos, const nsAString& aValue,
03254                                  nsIURI* aDocumentURI)
03255 {
03256     NS_PRECONDITION(aPos < mNumAttributes, "out-of-bounds");
03257 
03258     // WARNING!!
03259     // This code is largely duplicated in nsXULElement::SetAttr.
03260     // Any changes should be made to both functions.
03261 
03262     if (!mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
03263         mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
03264 
03265         return NS_OK;
03266     }
03267 
03268     if (mAttributes[aPos].mName.Equals(nsXULAtoms::id) &&
03269         !aValue.IsEmpty()) {
03270         // Store id as atom.
03271         // id="" means that the element has no id. Not that it has
03272         // emptystring as id.
03273         mAttributes[aPos].mValue.ParseAtom(aValue);
03274 
03275         return NS_OK;
03276     }
03277     else if (mAttributes[aPos].mName.Equals(nsXULAtoms::clazz)) {
03278         // Compute the element's class list
03279         mAttributes[aPos].mValue.ParseAtomArray(aValue);
03280         
03281         return NS_OK;
03282     }
03283     else if (mAttributes[aPos].mName.Equals(nsXULAtoms::style)) {
03284         // Parse the element's 'style' attribute
03285         nsCOMPtr<nsICSSStyleRule> rule;
03286         nsICSSParser* parser = GetCSSParser();
03287         NS_ENSURE_TRUE(parser, NS_ERROR_OUT_OF_MEMORY);
03288 
03289         // XXX Get correct Base URI (need GetBaseURI on *prototype* element)
03290         parser->ParseStyleAttribute(aValue, aDocumentURI, aDocumentURI,
03291                                     getter_AddRefs(rule));
03292         if (rule) {
03293             mAttributes[aPos].mValue.SetTo(rule);
03294 
03295             return NS_OK;
03296         }
03297         // Don't abort if parsing failed, it could just be malformed css.
03298     }
03299 
03300     mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
03301 
03302     return NS_OK;
03303 }
03304 
03305 //----------------------------------------------------------------------
03306 //
03307 // nsXULPrototypeScript
03308 //
03309 
03310 nsXULPrototypeScript::nsXULPrototypeScript(PRUint32 aLineNo,
03311                                            const char *aVersion,
03312                                            PRBool aHasE4XOption,
03313                                            nsresult* rv)
03314     : nsXULPrototypeNode(eType_Script),
03315       mLineNo(aLineNo),
03316       mSrcLoading(PR_FALSE),
03317       mOutOfLine(PR_TRUE),
03318       mHasE4XOption(aHasE4XOption),
03319       mSrcLoadWaiters(nsnull),
03320       mJSObject(nsnull),
03321       mLangVersion(aVersion)
03322 {
03323     NS_LOG_ADDREF(this, 1, ClassName(), ClassSize());
03324     *rv = nsContentUtils::AddJSGCRoot(&mJSObject,
03325                                       "nsXULPrototypeScript::mJSObject");
03326     mAddedGCRoot = NS_SUCCEEDED(*rv);
03327 }
03328 
03329 
03330 nsXULPrototypeScript::~nsXULPrototypeScript()
03331 {
03332     if (mAddedGCRoot) {
03333         nsContentUtils::RemoveJSGCRoot(&mJSObject);
03334     }
03335 }
03336 
03337 
03338 nsresult
03339 nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
03340                                 nsIScriptContext* aContext,
03341                                 const nsCOMArray<nsINodeInfo> *aNodeInfos)
03342 {
03343     NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nsnull || !mJSObject,
03344                  "script source still loading when serializing?!");
03345     if (!mJSObject)
03346         return NS_ERROR_FAILURE;
03347 
03348     nsresult rv;
03349 
03350     // Write basic prototype data
03351     aStream->Write32(mLineNo);
03352 
03353     JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
03354                                         aContext->GetNativeContext());
03355     JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_ENCODE);
03356     if (! xdr)
03357         return NS_ERROR_OUT_OF_MEMORY;
03358     xdr->userdata = (void*) aStream;
03359 
03360     JSScript *script = NS_REINTERPRET_CAST(JSScript*,
03361                                            ::JS_GetPrivate(cx, mJSObject));
03362     if (! ::JS_XDRScript(xdr, &script)) {
03363         rv = NS_ERROR_FAILURE;  // likely to be a principals serialization error
03364     } else {
03365         // Get the encoded JSXDRState data and write it.  The JSXDRState owns
03366         // this buffer memory and will free it beneath ::JS_XDRDestroy.
03367         //
03368         // If an XPCOM object needs to be written in the midst of the JS XDR
03369         // encoding process, the C++ code called back from the JS engine (e.g.,
03370         // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
03371         // from the JSXDRState to aStream, then write the object, then return
03372         // to JS XDR code with xdr reset so new JS data is encoded at the front
03373         // of the xdr's data buffer.
03374         //
03375         // However many XPCOM objects are interleaved with JS XDR data in the
03376         // stream, when control returns here from ::JS_XDRScript, we'll have
03377         // one last buffer of data to write to aStream.
03378 
03379         uint32 size;
03380         const char* data = NS_REINTERPRET_CAST(const char*,
03381                                                ::JS_XDRMemGetData(xdr, &size));
03382         NS_ASSERTION(data, "no decoded JSXDRState data!");
03383 
03384         rv = aStream->Write32(size);
03385         if (NS_SUCCEEDED(rv))
03386             rv = aStream->WriteBytes(data, size);
03387     }
03388 
03389     ::JS_XDRDestroy(xdr);
03390     if (NS_FAILED(rv)) return rv;
03391 
03392     PRUint32 version = PRUint32(mLangVersion
03393                                 ? ::JS_StringToVersion(mLangVersion)
03394                                 : JSVERSION_DEFAULT);
03395     rv = aStream->Write32(version);
03396     if (NS_FAILED(rv)) return rv;
03397 
03398     return NS_OK;
03399 }
03400 
03401 nsresult
03402 nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
03403                                          nsIScriptContext* aContext)
03404 {
03405     nsIXULPrototypeCache* cache = GetXULCache();
03406 #ifdef NS_DEBUG
03407     PRBool useXULCache = PR_TRUE;
03408     cache->GetEnabled(&useXULCache);
03409     NS_ASSERTION(useXULCache,
03410                  "writing to the FastLoad file, but the XUL cache is off?");
03411 #endif
03412 
03413     nsCOMPtr<nsIFastLoadService> fastLoadService;
03414     cache->GetFastLoadService(getter_AddRefs(fastLoadService));
03415 
03416     nsresult rv = NS_OK;
03417     if (!fastLoadService)
03418         return NS_ERROR_NOT_AVAILABLE;
03419 
03420     nsCAutoString urispec;
03421     rv = mSrcURI->GetAsciiSpec(urispec);
03422     if (NS_FAILED(rv))
03423         return rv;
03424 
03425     PRBool exists = PR_FALSE;
03426     fastLoadService->HasMuxedDocument(urispec.get(), &exists);
03427     /* return will be NS_OK from GetAsciiSpec.
03428      * that makes no sense.
03429      * nor does returning NS_OK from HasMuxedDocument.
03430      * XXX return something meaningful.
03431      */
03432     if (exists)
03433         return NS_OK;
03434 
03435     // Allow callers to pass null for aStream, meaning
03436     // "use the FastLoad service's default output stream."
03437     // See nsXULDocument.cpp for one use of this.
03438     nsCOMPtr<nsIObjectOutputStream> objectOutput = aStream;
03439     if (! objectOutput) {
03440         fastLoadService->GetOutputStream(getter_AddRefs(objectOutput));
03441         if (! objectOutput)
03442             return NS_ERROR_NOT_AVAILABLE;
03443     }
03444 
03445     rv = fastLoadService->
03446          StartMuxedDocument(mSrcURI, urispec.get(),
03447                             nsIFastLoadService::NS_FASTLOAD_WRITE);
03448     NS_ASSERTION(rv != NS_ERROR_NOT_AVAILABLE, "reading FastLoad?!");
03449 
03450     nsCOMPtr<nsIURI> oldURI;
03451     rv |= fastLoadService->SelectMuxedDocument(mSrcURI, getter_AddRefs(oldURI));
03452     rv |= Serialize(objectOutput, aContext, nsnull);
03453     rv |= fastLoadService->EndMuxedDocument(mSrcURI);
03454 
03455     if (oldURI) {
03456         nsCOMPtr<nsIURI> tempURI;
03457         rv |= fastLoadService->
03458               SelectMuxedDocument(oldURI, getter_AddRefs(tempURI));
03459     }
03460 
03461     if (NS_FAILED(rv))
03462         cache->AbortFastLoads();
03463     return rv;
03464 }
03465 
03466 
03467 nsresult
03468 nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream,
03469                                   nsIScriptContext* aContext,
03470                                   nsIURI* aDocumentURI,
03471                                   const nsCOMArray<nsINodeInfo> *aNodeInfos)
03472 {
03473     NS_TIMELINE_MARK_FUNCTION("chrome js deserialize");
03474     nsresult rv;
03475 
03476     // Read basic prototype data
03477     aStream->Read32(&mLineNo);
03478 
03479     NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nsnull || !mJSObject,
03480                  "prototype script not well-initialized when deserializing?!");
03481 
03482     PRUint32 size;
03483     rv = aStream->Read32(&size);
03484     if (NS_FAILED(rv)) return rv;
03485 
03486     char* data;
03487     rv = aStream->ReadBytes(size, &data);
03488     if (NS_SUCCEEDED(rv)) {
03489         JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
03490                                             aContext->GetNativeContext());
03491 
03492         JSXDRState *xdr = ::JS_XDRNewMem(cx, JSXDR_DECODE);
03493         if (! xdr) {
03494             rv = NS_ERROR_OUT_OF_MEMORY;
03495         } else {
03496             xdr->userdata = (void*) aStream;
03497             ::JS_XDRMemSetData(xdr, data, size);
03498 
03499             JSScript *script = nsnull;
03500             if (! ::JS_XDRScript(xdr, &script)) {
03501                 rv = NS_ERROR_FAILURE;  // principals deserialization error?
03502             } else {
03503                 mJSObject = ::JS_NewScriptObject(cx, script);
03504                 if (! mJSObject) {
03505                     rv = NS_ERROR_OUT_OF_MEMORY;    // certain error
03506                     ::JS_DestroyScript(cx, script);
03507                 }
03508             }
03509 
03510             // Update data in case ::JS_XDRScript called back into C++ code to
03511             // read an XPCOM object.
03512             //
03513             // In that case, the serialization process must have flushed a run
03514             // of counted bytes containing JS data at the point where the XPCOM
03515             // object starts, after which an encoding C++ callback from the JS
03516             // XDR code must have written the XPCOM object directly into the
03517             // nsIObjectOutputStream.
03518             //
03519             // The deserialization process will XDR-decode counted bytes up to
03520             // but not including the XPCOM object, then call back into C++ to
03521             // read the object, then read more counted bytes and hand them off
03522             // to the JSXDRState, so more JS data can be decoded.
03523             //
03524             // This interleaving of JS XDR data and XPCOM object data may occur
03525             // several times beneath the call to ::JS_XDRScript, above.  At the
03526             // end of the day, we need to free (via nsMemory) the data owned by
03527             // the JSXDRState.  So we steal it back, nulling xdr's buffer so it
03528             // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
03529 
03530             uint32 junk;
03531             data = (char*) ::JS_XDRMemGetData(xdr, &junk);
03532             if (data)
03533                 ::JS_XDRMemSetData(xdr, NULL, 0);
03534             ::JS_XDRDestroy(xdr);
03535         }
03536 
03537         // If data is null now, it must have been freed while deserializing an
03538         // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
03539         if (data)
03540             nsMemory::Free(data);
03541     }
03542     if (NS_FAILED(rv)) return rv;
03543 
03544     PRUint32 version;
03545     rv = aStream->Read32(&version);
03546     if (NS_FAILED(rv)) return rv;
03547 
03548     mLangVersion = ::JS_VersionToString(JSVersion(version));
03549     return NS_OK;
03550 }
03551 
03552 
03553 nsresult
03554 nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
03555                                            nsIScriptContext* aContext)
03556 {
03557     // Keep track of FastLoad failure via rv, so we can
03558     // AbortFastLoads if things look bad.
03559     nsresult rv = NS_OK;
03560 
03561     nsIXULPrototypeCache* cache = GetXULCache();
03562     nsCOMPtr<nsIFastLoadService> fastLoadService;
03563     cache->GetFastLoadService(getter_AddRefs(fastLoadService));
03564 
03565     // Allow callers to pass null for aInput, meaning
03566     // "use the FastLoad service's default input stream."
03567     // See nsXULContentSink.cpp for one use of this.
03568     nsCOMPtr<nsIObjectInputStream> objectInput = aInput;
03569     if (! objectInput && fastLoadService)
03570         fastLoadService->GetInputStream(getter_AddRefs(objectInput));
03571 
03572     if (objectInput) {
03573         PRBool useXULCache = PR_TRUE;
03574         if (mSrcURI) {
03575             // NB: we must check the XUL script cache early, to avoid
03576             // multiple deserialization attempts for a given script, which
03577             // would exhaust the multiplexed stream containing the singly
03578             // serialized script.  Note that nsXULDocument::LoadScript
03579             // checks the XUL script cache too, in order to handle the
03580             // serialization case.
03581             //
03582             // We need do this only for <script src='strres.js'> and the
03583             // like, i.e., out-of-line scripts that are included by several
03584             // different XUL documents multiplexed in the FastLoad file.
03585             cache->GetEnabled(&useXULCache);
03586 
03587             if (useXULCache) {
03588                 cache->GetScript(mSrcURI, NS_REINTERPRET_CAST(void**, &mJSObject));
03589             }
03590         }
03591 
03592         if (! mJSObject) {
03593             nsCOMPtr<nsIURI> oldURI;
03594 
03595             if (mSrcURI) {
03596                 nsCAutoString spec;
03597                 mSrcURI->GetAsciiSpec(spec);
03598                 rv = fastLoadService->StartMuxedDocument(mSrcURI, spec.get(),
03599                                                          nsIFastLoadService::NS_FASTLOAD_READ);
03600                 if (NS_SUCCEEDED(rv))
03601                     rv = fastLoadService->SelectMuxedDocument(mSrcURI, getter_AddRefs(oldURI));
03602             } else {
03603                 // An inline script: check FastLoad multiplexing direction
03604                 // and skip Deserialize if we're not reading from a
03605                 // muxed stream to get inline objects that are contained in
03606                 // the current document.
03607                 PRInt32 direction;
03608                 fastLoadService->GetDirection(&direction);
03609                 if (direction != nsIFastLoadService::NS_FASTLOAD_READ)
03610                     rv = NS_ERROR_NOT_AVAILABLE;
03611             }
03612 
03613             // We do reflect errors into rv, but our caller may want to
03614             // ignore our return value, because mJSObject will be null
03615             // after any error, and that suffices to cause the script to
03616             // be reloaded (from the src= URI, if any) and recompiled.
03617             // We're better off slow-loading than bailing out due to a
03618             // FastLoad error.
03619             if (NS_SUCCEEDED(rv))
03620                 rv = Deserialize(objectInput, aContext, nsnull, nsnull);
03621 
03622             if (NS_SUCCEEDED(rv) && mSrcURI) {
03623                 rv = fastLoadService->EndMuxedDocument(mSrcURI);
03624 
03625                 if (NS_SUCCEEDED(rv) && oldURI) {
03626                     nsCOMPtr<nsIURI> tempURI;
03627                     rv = fastLoadService->SelectMuxedDocument(oldURI, getter_AddRefs(tempURI));
03628 
03629                     NS_ASSERTION(NS_SUCCEEDED(rv) && (!tempURI || tempURI == mSrcURI),
03630                                  "not currently deserializing into the script we thought we were!");
03631                 }
03632             }
03633 
03634             if (NS_SUCCEEDED(rv)) {
03635                 if (useXULCache && mSrcURI) {
03636                     PRBool isChrome = PR_FALSE;
03637                     mSrcURI->SchemeIs("chrome", &isChrome);
03638                     if (isChrome) {
03639                         cache->PutScript(mSrcURI, NS_REINTERPRET_CAST(void*, mJSObject));
03640                     }
03641                 }
03642             } else {
03643                 // If mSrcURI is not in the FastLoad multiplex,
03644                 // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to
03645                 // update the FastLoad file to hold a serialization of
03646                 // this script, once it has finished loading.
03647                 if (rv != NS_ERROR_NOT_AVAILABLE)
03648                     cache->AbortFastLoads();
03649             }
03650         }
03651     }
03652 
03653     return rv;
03654 }
03655 
03656 nsresult
03657 nsXULPrototypeScript::Compile(const PRUnichar* aText,
03658                               PRInt32 aTextLength,
03659                               nsIURI* aURI,
03660                               PRUint32 aLineNo,
03661                               nsIDocument* aDocument,
03662                               nsIXULPrototypeDocument* aPrototypeDocument)
03663 {
03664     // We'll compile the script using the prototype document's special
03665     // script object as the parent. This ensures that we won't end up
03666     // with an uncollectable reference.
03667     //
03668     // Compiling it using (for example) the first document's global
03669     // object would cause JS to keep a reference via the __proto__ or
03670     // __parent__ pointer to the first document's global. If that
03671     // happened, our script object would reference the first document,
03672     // and the first document would indirectly reference the prototype
03673     // document because it keeps the prototype cache alive. Circularity!
03674     nsresult rv;
03675 
03676     // Use the prototype document's special context
03677     nsIScriptContext *context;
03678 
03679     {
03680         nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner =
03681             do_QueryInterface(aPrototypeDocument);
03682         nsIScriptGlobalObject* global = globalOwner->GetScriptGlobalObject();
03683         NS_ASSERTION(global != nsnull, "prototype doc has no script global");
03684         if (! global)
03685             return NS_ERROR_UNEXPECTED;
03686 
03687         context = global->GetContext();
03688 
03689         NS_ASSERTION(context != nsnull, "no context for script global");
03690         if (! context)
03691             return NS_ERROR_UNEXPECTED;
03692     }
03693 
03694     // Use the enclosing document's principal
03695     // XXX is this right? or should we use the protodoc's?
03696     // If we start using the protodoc's, make sure
03697     // the DowngradePrincipalIfNeeded stuff in
03698     // nsXULDocument::OnStreamComplete still works!
03699     nsIPrincipal *principal = aDocument->GetPrincipal();
03700     if (!principal)
03701         return NS_ERROR_FAILURE;
03702 
03703     nsCAutoString urlspec;
03704     aURI->GetSpec(urlspec);
03705 
03706     // Ok, compile it to create a prototype script object!
03707 
03708     // XXXbe violate nsIScriptContext layering because its version parameter
03709     // is mis-typed as const char * -- if it were uint32, we could more easily
03710     // extend version to include compile-time option, as the JS engine does.
03711     // It'd also be more efficient than converting to and from a C string.
03712 
03713     JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
03714                                         context->GetNativeContext());
03715     uint32 options = ::JS_GetOptions(cx);
03716     JSBool changed = (mHasE4XOption ^ !!(options & JSOPTION_XML));
03717     if (changed) {
03718         ::JS_SetOptions(cx,
03719                         mHasE4XOption
03720                         ? options | JSOPTION_XML
03721                         : options & ~JSOPTION_XML);
03722     }
03723 
03724     rv = context->CompileScript(aText,
03725                                 aTextLength,
03726                                 nsnull,
03727                                 principal,
03728                                 urlspec.get(),
03729                                 aLineNo,
03730                                 mLangVersion,
03731                                 (void**)&mJSObject);
03732 
03733     if (changed) {
03734         ::JS_SetOptions(cx, options);
03735     }
03736     return rv;
03737 }
03738 
03739 //----------------------------------------------------------------------
03740 //
03741 // nsXULPrototypeText
03742 //
03743 
03744 nsresult
03745 nsXULPrototypeText::Serialize(nsIObjectOutputStream* aStream,
03746                               nsIScriptContext* aContext,
03747                               const nsCOMArray<nsINodeInfo> *aNodeInfos)
03748 {
03749     nsresult rv;
03750 
03751     // Write basic prototype data
03752     rv = aStream->Write32(mType);
03753 
03754     rv |= aStream->WriteWStringZ(mValue.get());
03755 
03756     return rv;
03757 }
03758 
03759 nsresult
03760 nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream,
03761                                 nsIScriptContext* aContext,
03762                                 nsIURI* aDocumentURI,
03763                                 const nsCOMArray<nsINodeInfo> *aNodeInfos)
03764 {
03765     nsresult rv;
03766 
03767     // Write basic prototype data
03768     rv = aStream->ReadString(mValue);
03769 
03770     return rv;
03771 }