/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Chris Waterson * Pierre Phaneuf * Peter Annema * Brendan Eich * Mike Shaver * Ben Goodger * * This Original Code has been modified by IBM Corporation. * Modifications made by IBM described herein are * Copyright (c) International Business Machines * Corporation, 2000 * * Modifications to Mozilla code or documentation * identified per MPL Section 3.3 * * Date Modified by Description of modification * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink * use in OS2 */ /* Implementation for a XUL content element. TO DO 1. Possibly clean up the attribute walking code (e.g., in SetDocument, GetAttrCount, etc.) by extracting into an iterator. */ #include "jsapi.h" // for JS_AddNamedRoot and JS_RemoveRootRT #include "jsxdrapi.h" #include "nsCOMPtr.h" #include "nsDOMCID.h" #include "nsDOMError.h" #include "nsDOMString.h" #include "nsIDOMEvent.h" #include "nsIPrivateDOMEvent.h" #include "nsHashtable.h" #include "nsIAtom.h" #include "nsIDOMAttr.h" #include "nsIDOMDocument.h" #include "nsIDOMElement.h" #include "nsIDOMMouseListener.h" #include "nsIDOMMouseMotionListener.h" #include "nsIDOMLoadListener.h" #include "nsIDOMFocusListener.h" #include "nsIDOMPaintListener.h" #include "nsIDOMKeyListener.h" #include "nsIDOMFormListener.h" #include "nsIDOMXULListener.h" #include "nsIDOMScrollListener.h" #include "nsIDOMContextMenuListener.h" #include "nsIDOMDragListener.h" #include "nsIDOMEventListener.h" #include "nsIDOMEventReceiver.h" #include "nsIDOMNodeList.h" #include "nsIDOMXULCommandDispatcher.h" #include "nsIDOMXULElement.h" #include "nsIDOMXULSelectCntrlItemEl.h" #include "nsIDocument.h" #include "nsIEventListenerManager.h" #include "nsIEventStateManager.h" #include "nsIFastLoadService.h" #include "nsIHTMLStyleSheet.h" #include "nsINameSpace.h" #include "nsINameSpaceManager.h" #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" #include "nsIPresShell.h" #include "nsIPrincipal.h" #include "nsIRDFCompositeDataSource.h" #include "nsIRDFNode.h" #include "nsIRDFService.h" #include "nsIScriptContext.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObjectOwner.h" #include "nsIServiceManager.h" #include "nsICSSStyleRule.h" #include "nsIStyleSheet.h" #include "nsIStyledContent.h" #include "nsIURL.h" #include "nsIViewManager.h" #include "nsIWidget.h" #include "nsIXULContent.h" #include "nsIXULDocument.h" #include "nsIXULPopupListener.h" #include "nsIXULPrototypeDocument.h" #include "nsIXULTemplateBuilder.h" #include "nsIXBLService.h" #include "nsLayoutCID.h" #include "nsContentCID.h" #include "nsRDFCID.h" #include "nsRDFDOMNodeList.h" #include "nsStyleConsts.h" #include "nsXPIDLString.h" #include "nsXULControllers.h" #include "nsIBoxObject.h" #include "nsPIBoxObject.h" #include "nsXULDocument.h" #include "nsRuleWalker.h" #include "nsIDOMViewCSS.h" #include "nsIDOMCSSStyleDeclaration.h" #include "nsCSSDeclaration.h" #include "nsIListBoxObject.h" #include "nsContentUtils.h" #include "nsGenericElement.h" #include "nsContentList.h" #include "nsMutationEvent.h" #include "nsIDOMMutationEvent.h" #include "nsPIDOMWindow.h" #include "nsDOMAttributeMap.h" #include "nsDOMCSSDeclaration.h" #include "nsGenericHTMLElement.h" #include "prlog.h" #include "rdf.h" #include "nsIControllers.h" // The XUL interfaces implemented by the RDF content node. #include "nsIDOMXULElement.h" // The XUL doc interface #include "nsIDOMXULDocument.h" #include "nsReadableUtils.h" #include "nsITimelineService.h" #include "nsIFrame.h" class nsIDocShell; // Global object maintenance nsICSSParser* nsXULPrototypeElement::sCSSParser = nsnull; nsIXULPrototypeCache* nsXULPrototypeScript::sXULPrototypeCache = nsnull; nsIXBLService * nsXULElement::gXBLService = nsnull; nsICSSOMFactory* nsXULElement::gCSSOMFactory = nsnull; //---------------------------------------------------------------------- static NS_DEFINE_CID(kEventListenerManagerCID, NS_EVENTLISTENERMANAGER_CID); static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_CID(kXULPopupListenerCID, NS_XULPOPUPLISTENER_CID); static NS_DEFINE_CID(kCSSOMFactoryCID, NS_CSSOMFACTORY_CID); //---------------------------------------------------------------------- #if 0 /* || defined(DEBUG_shaver) || defined(DEBUG_waterson) */ #define DEBUG_ATTRIBUTE_STATS #endif #ifdef DEBUG_ATTRIBUTE_STATS #include static struct { PRUint32 UnsetAttr; PRUint32 Create; PRUint32 Total; } gFaults; #endif #include "nsIJSRuntimeService.h" static nsIJSRuntimeService* gJSRuntimeService = nsnull; static JSRuntime* gScriptRuntime = nsnull; static PRInt32 gScriptRuntimeRefcnt = 0; static nsresult AddJSGCRoot(void* aScriptObjectRef, const char* aName) { if (++gScriptRuntimeRefcnt == 1 || !gScriptRuntime) { CallGetService("@mozilla.org/js/xpc/RuntimeService;1", &gJSRuntimeService); if (! gJSRuntimeService) { NS_NOTREACHED("couldn't add GC root"); return NS_ERROR_FAILURE; } gJSRuntimeService->GetRuntime(&gScriptRuntime); if (! gScriptRuntime) { NS_NOTREACHED("couldn't add GC root"); return NS_ERROR_FAILURE; } } PRBool ok; ok = ::JS_AddNamedRootRT(gScriptRuntime, aScriptObjectRef, aName); if (! ok) { NS_NOTREACHED("couldn't add GC root"); return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } static nsresult RemoveJSGCRoot(void* aScriptObjectRef) { if (! gScriptRuntime) { NS_NOTREACHED("couldn't remove GC root"); return NS_ERROR_FAILURE; } ::JS_RemoveRootRT(gScriptRuntime, aScriptObjectRef); if (--gScriptRuntimeRefcnt == 0) { NS_RELEASE(gJSRuntimeService); gScriptRuntime = nsnull; } return NS_OK; } //---------------------------------------------------------------------- struct EventHandlerMapEntry { const char* mAttributeName; nsIAtom* mAttributeAtom; }; static EventHandlerMapEntry kEventHandlerMap[] = { { "onclick", nsnull }, { "ondblclick", nsnull }, { "onmousedown", nsnull }, { "onmouseup", nsnull }, { "onmouseover", nsnull }, { "onmouseout", nsnull }, { "onmousemove", nsnull }, { "onkeydown", nsnull }, { "onkeyup", nsnull }, { "onkeypress", nsnull }, { "onload", nsnull }, { "onunload", nsnull }, { "onabort", nsnull }, { "onerror", nsnull }, { "onpopupshowing", nsnull }, { "onpopupshown", nsnull }, { "onpopuphiding" , nsnull }, { "onpopuphidden", nsnull }, { "onclose", nsnull }, { "oncommand", nsnull }, { "onbroadcast", nsnull }, { "oncommandupdate", nsnull }, { "onoverflow", nsnull }, { "onunderflow", nsnull }, { "onoverflowchanged",nsnull }, { "onfocus", nsnull }, { "onblur", nsnull }, { "onsubmit", nsnull }, { "onreset", nsnull }, { "onchange", nsnull }, { "onselect", nsnull }, { "oninput", nsnull }, { "onpaint", nsnull }, { "ondragenter", nsnull }, { "ondragover", nsnull }, { "ondragexit", nsnull }, { "ondragdrop", nsnull }, { "ondraggesture", nsnull }, { "oncontextmenu", nsnull }, { nsnull, nsnull } }; // XXX This function is called for every attribute on every element for // XXX which we SetDocument, among other places. A linear search might // XXX not be what we want. static PRBool IsEventHandler(nsIAtom* aName) { const char* name; aName->GetUTF8String(&name); if (name[0] != 'o' || name[1] != 'n') { return PR_FALSE; } EventHandlerMapEntry* entry = kEventHandlerMap; while (entry->mAttributeAtom) { if (entry->mAttributeAtom == aName) { return PR_TRUE; break; } ++entry; } return PR_FALSE; } static void InitEventHandlerMap() { EventHandlerMapEntry* entry = kEventHandlerMap; while (entry->mAttributeName) { entry->mAttributeAtom = NS_NewAtom(entry->mAttributeName); ++entry; } } static void FinishEventHandlerMap() { EventHandlerMapEntry* entry = kEventHandlerMap; while (entry->mAttributeName) { NS_IF_RELEASE(entry->mAttributeAtom); ++entry; } } //---------------------------------------------------------------------- static PRBool HasMutationListeners(nsIContent* aContent, PRUint32 aType) { nsIDocument* doc = aContent->GetDocument(); if (!doc) return PR_FALSE; nsIScriptGlobalObject *global = doc->GetScriptGlobalObject(); if (!global) return PR_FALSE; nsCOMPtr window(do_QueryInterface(global)); if (!window) return PR_FALSE; PRBool set; window->HasMutationListeners(aType, &set); if (!set) return PR_FALSE; // We know a mutation listener is registered, but it might not // be in our chain. Check quickly to see. nsCOMPtr manager; for (nsIContent* curr = aContent; curr; curr = curr->GetParent()) { nsCOMPtr rec(do_QueryInterface(curr)); if (rec) { rec->GetListenerManager(getter_AddRefs(manager)); if (manager) { PRBool hasMutationListeners = PR_FALSE; manager->HasMutationListeners(&hasMutationListeners); if (hasMutationListeners) return PR_TRUE; } } } nsCOMPtr rec(do_QueryInterface(doc)); if (rec) { rec->GetListenerManager(getter_AddRefs(manager)); if (manager) { PRBool hasMutationListeners = PR_FALSE; manager->HasMutationListeners(&hasMutationListeners); if (hasMutationListeners) return PR_TRUE; } } rec = do_QueryInterface(window); if (rec) { rec->GetListenerManager(getter_AddRefs(manager)); if (manager) { PRBool hasMutationListeners = PR_FALSE; manager->HasMutationListeners(&hasMutationListeners); if (hasMutationListeners) return PR_TRUE; } } return PR_FALSE; } //---------------------------------------------------------------------- nsrefcnt nsXULElement::gRefCnt; nsIRDFService* nsXULElement::gRDFService; #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING PRUint32 nsXULPrototypeAttribute::gNumElements; PRUint32 nsXULPrototypeAttribute::gNumAttributes; PRUint32 nsXULPrototypeAttribute::gNumEventHandlers; PRUint32 nsXULPrototypeAttribute::gNumCacheTests; PRUint32 nsXULPrototypeAttribute::gNumCacheHits; PRUint32 nsXULPrototypeAttribute::gNumCacheSets; PRUint32 nsXULPrototypeAttribute::gNumCacheFills; #endif //---------------------------------------------------------------------- // nsXULElement // nsXULElement::nsXULElement() : mPrototype(nsnull), mBindingParent(nsnull), mSlots(nsnull) { XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements); } nsresult nsXULElement::Init() { if (gRefCnt++ == 0) { nsresult rv; rv = CallGetService(kRDFServiceCID, &gRDFService); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF service"); if (NS_FAILED(rv)) return rv; InitEventHandlerMap(); } return NS_OK; } nsXULElement::~nsXULElement() { if (mPrototype) mPrototype->Release(); delete mSlots; // Clean up shared statics if (--gRefCnt == 0) { FinishEventHandlerMap(); NS_IF_RELEASE(gRDFService); } } nsresult nsXULElement::Create(nsXULPrototypeElement* aPrototype, nsIDocument* aDocument, PRBool aIsScriptable, nsIContent** aResult) { // Create an nsXULElement from a prototype NS_PRECONDITION(aPrototype != nsnull, "null ptr"); if (! aPrototype) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(aResult != nsnull, "null ptr"); if (! aResult) return NS_ERROR_NULL_POINTER; nsRefPtr element = new nsXULElement(); if (! element) return NS_ERROR_OUT_OF_MEMORY; nsresult rv = element->Init(); if (NS_FAILED(rv)) return rv; element->mPrototype = aPrototype; element->mDocument = aDocument; aPrototype->AddRef(); if (aIsScriptable) { // Check each attribute on the prototype to see if we need to do // any additional processing and hookup that would otherwise be // done 'automagically' by SetAttr(). for (PRUint32 i = 0; i < aPrototype->mNumAttributes; ++i) element->AddListenerFor(aPrototype->mAttributes[i].mName, PR_TRUE); } *aResult = NS_REINTERPRET_CAST(nsIStyledContent*, element.get()); NS_ADDREF(*aResult); return NS_OK; } nsresult nsXULElement::Create(nsINodeInfo *aNodeInfo, nsIContent** aResult) { NS_PRECONDITION(aNodeInfo, "need nodeinfo for non-proto Create"); *aResult = nsnull; // Create an nsXULElement with the specified namespace and tag. nsXULElement* element = new nsXULElement(); NS_ENSURE_TRUE(element, NS_ERROR_OUT_OF_MEMORY); // anchor the element so an early return will clean up properly. nsCOMPtr anchor = element; nsresult rv = element->Init(); NS_ENSURE_SUCCESS(rv, rv); rv = element->EnsureSlots(); NS_ENSURE_SUCCESS(rv, rv); element->mSlots->mNodeInfo = aNodeInfo; anchor.swap(*aResult); #ifdef DEBUG_ATTRIBUTE_STATS { gFaults.Create++; gFaults.Total++; nsAutoString tagstr; element->NodeInfo()->GetQualifiedName(tagstr); char *tagcstr = ToNewCString(tagstr); fprintf(stderr, "XUL: Heavyweight create of <%s>: %d/%d\n", tagcstr, gFaults.Create, gFaults.Total); nsMemory::Free(tagcstr); void *back[5]; backtrace(back, sizeof(back) / sizeof(back[0])); backtrace_symbols_fd(back, sizeof(back) / sizeof(back[0]), 2); } #endif return NS_OK; } //---------------------------------------------------------------------- // nsISupports interface NS_IMPL_ADDREF(nsXULElement) NS_IMPL_RELEASE(nsXULElement) NS_INTERFACE_MAP_BEGIN(nsXULElement) NS_INTERFACE_MAP_ENTRY(nsIStyledContent) NS_INTERFACE_MAP_ENTRY(nsIContent) NS_INTERFACE_MAP_ENTRY(nsIXULContent) NS_INTERFACE_MAP_ENTRY(nsIXMLContent) NS_INTERFACE_MAP_ENTRY(nsIDOMXULElement) NS_INTERFACE_MAP_ENTRY(nsIDOMElement) NS_INTERFACE_MAP_ENTRY(nsIDOMNode) NS_INTERFACE_MAP_ENTRY(nsIScriptEventHandlerOwner) NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventReceiver, nsDOMEventRTTearoff::Create(this)) NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventTarget, nsDOMEventRTTearoff::Create(this)) NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3EventTarget, nsDOMEventRTTearoff::Create(this)) NS_INTERFACE_MAP_ENTRY(nsIChromeEventHandler) NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this)) NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XULElement) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXULElement) if (mDocument) { return mDocument->GetBindingManager()->GetBindingImplementation(this, aIID, aInstancePtr); } else NS_INTERFACE_MAP_END //---------------------------------------------------------------------- // nsIDOMNode interface NS_IMETHODIMP nsXULElement::GetNodeName(nsAString& aNodeName) { NodeInfo()->GetQualifiedName(aNodeName); return NS_OK; } NS_IMETHODIMP nsXULElement::GetNodeValue(nsAString& aNodeValue) { SetDOMStringToNull(aNodeValue); return NS_OK; } NS_IMETHODIMP nsXULElement::SetNodeValue(const nsAString& aNodeValue) { // The DOM spec says that when nodeValue is defined to be null "setting it // has no effect", so we don't throw an exception. return NS_OK; } NS_IMETHODIMP nsXULElement::GetNodeType(PRUint16* aNodeType) { *aNodeType = (PRUint16)nsIDOMNode::ELEMENT_NODE; return NS_OK; } NS_IMETHODIMP nsXULElement::GetParentNode(nsIDOMNode** aParentNode) { if (GetParent()) { return CallQueryInterface(GetParent(), aParentNode); } if (mDocument) { nsIContent *thisIContent = this; if (mDocument->GetRootContent() == thisIContent) { // If we don't have a parent, and we're the root content // of the document, DOM says that our parent is the // document. return CallQueryInterface(mDocument, aParentNode); } } // A standalone element (i.e. one without a parent or a document) *aParentNode = nsnull; return NS_OK; } NS_IMETHODIMP nsXULElement::GetChildNodes(nsIDOMNodeList** aChildNodes) { nsresult rv; nsRDFDOMNodeList* children = new nsRDFDOMNodeList(); NS_ENSURE_TRUE(children, NS_ERROR_OUT_OF_MEMORY); NS_ADDREF(children); PRUint32 count = GetChildCount(); for (PRUint32 i = 0; i < count; ++i) { nsIContent *child = GetChildAt(i); nsCOMPtr domNode = do_QueryInterface(child); if (!domNode) { NS_WARNING("child content doesn't support nsIDOMNode"); continue; } rv = children->AppendNode(domNode); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to append node to list"); if (NS_FAILED(rv)) break; } // Create() addref'd for us *aChildNodes = children; return NS_OK; } NS_IMETHODIMP nsXULElement::GetFirstChild(nsIDOMNode** aFirstChild) { nsIContent *child = GetChildAt(0); if (child) { return CallQueryInterface(child, aFirstChild); } *aFirstChild = nsnull; return NS_OK; } NS_IMETHODIMP nsXULElement::GetLastChild(nsIDOMNode** aLastChild) { PRUint32 count = GetChildCount(); if (count > 0) { nsIContent *child = GetChildAt(count - 1); return CallQueryInterface(child, aLastChild); } *aLastChild = nsnull; return NS_OK; } NS_IMETHODIMP nsXULElement::GetPreviousSibling(nsIDOMNode** aPreviousSibling) { if (GetParent()) { PRInt32 pos = GetParent()->IndexOf(this); if (pos > 0) { nsIContent *prev = GetParent()->GetChildAt(--pos); if (prev) { nsresult rv = CallQueryInterface(prev, aPreviousSibling); NS_ASSERTION(*aPreviousSibling, "not a DOM node"); return rv; } } } // XXX Nodes that are just below the document (their parent is the // document) need to go to the document to find their previous sibling. *aPreviousSibling = nsnull; return NS_OK; } NS_IMETHODIMP nsXULElement::GetNextSibling(nsIDOMNode** aNextSibling) { if (GetParent()) { PRInt32 pos = GetParent()->IndexOf(this); if (pos > -1) { nsIContent *next = GetParent()->GetChildAt(++pos); if (next) { nsresult rv = CallQueryInterface(next, aNextSibling); NS_ASSERTION(*aNextSibling, "not a DOM Node"); return rv; } } } // XXX Nodes that are just below the document (their parent is the // document) need to go to the document to find their next sibling. *aNextSibling = nsnull; return NS_OK; } NS_IMETHODIMP nsXULElement::GetAttributes(nsIDOMNamedNodeMap** aAttributes) { NS_PRECONDITION(aAttributes, "nullpointer"); nsresult rv = EnsureSlots(); NS_ENSURE_SUCCESS(rv, rv); if (!mSlots->mAttributeMap) { mSlots->mAttributeMap = new nsDOMAttributeMap(this); NS_ENSURE_TRUE(mSlots->mAttributeMap, NS_ERROR_OUT_OF_MEMORY); } NS_ADDREF(*aAttributes = mSlots->mAttributeMap); return NS_OK; } NS_IMETHODIMP nsXULElement::GetOwnerDocument(nsIDOMDocument** aOwnerDocument) { if (mDocument) { return CallQueryInterface(mDocument, aOwnerDocument); } nsIDocument* doc = NodeInfo()->GetDocument(); if (doc) { return CallQueryInterface(doc, aOwnerDocument); } *aOwnerDocument = nsnull; return NS_OK; } NS_IMETHODIMP nsXULElement::GetNamespaceURI(nsAString& aNamespaceURI) { return NodeInfo()->GetNamespaceURI(aNamespaceURI); } NS_IMETHODIMP nsXULElement::GetPrefix(nsAString& aPrefix) { NodeInfo()->GetPrefix(aPrefix); return NS_OK; } NS_IMETHODIMP nsXULElement::SetPrefix(const nsAString& aPrefix) { // XXX: Validate the prefix string! nsCOMPtr newNodeInfo; nsCOMPtr prefix; if (!aPrefix.IsEmpty() && !DOMStringIsNull(aPrefix)) { prefix = do_GetAtom(aPrefix); NS_ENSURE_TRUE(prefix, NS_ERROR_OUT_OF_MEMORY); } nsresult rv = EnsureSlots(); NS_ENSURE_SUCCESS(rv, rv); rv = mSlots->mNodeInfo->PrefixChanged(prefix, getter_AddRefs(newNodeInfo)); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(newNodeInfo, "trying to assign null nodeinfo!"); mSlots->mNodeInfo = newNodeInfo; return NS_OK; } NS_IMETHODIMP nsXULElement::GetLocalName(nsAString& aLocalName) { NodeInfo()->GetLocalName(aLocalName); return NS_OK; } NS_IMETHODIMP nsXULElement::InsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, nsIDOMNode** aReturn) { return nsGenericElement::doInsertBefore(this, aNewChild, aRefChild, aReturn); } NS_IMETHODIMP nsXULElement::ReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild, nsIDOMNode** aReturn) { return nsGenericElement::doReplaceChild(this, aNewChild, aOldChild, aReturn); } NS_IMETHODIMP nsXULElement::RemoveChild(nsIDOMNode* aOldChild, nsIDOMNode** aReturn) { return nsGenericElement::doRemoveChild(this, aOldChild, aReturn); } NS_IMETHODIMP nsXULElement::AppendChild(nsIDOMNode* aNewChild, nsIDOMNode** aReturn) { return nsGenericElement::doInsertBefore(this, aNewChild, nsnull, aReturn); } NS_IMETHODIMP nsXULElement::HasChildNodes(PRBool* aReturn) { *aReturn = mAttrsAndChildren.ChildCount() > 0; return NS_OK; } NS_IMETHODIMP nsXULElement::HasAttributes(PRBool* aReturn) { *aReturn = mAttrsAndChildren.AttrCount() > 0 || (mPrototype && mPrototype->mNumAttributes > 0); return NS_OK; } NS_IMETHODIMP nsXULElement::CloneNode(PRBool aDeep, nsIDOMNode** aReturn) { nsresult rv; nsCOMPtr result; // If we have a prototype, so will our clone. if (mPrototype) { rv = nsXULElement::Create(mPrototype, mDocument, PR_TRUE, getter_AddRefs(result)); NS_ENSURE_SUCCESS(rv, rv); } else { NS_ASSERTION(mSlots, "no prototype and no slots!"); NS_ENSURE_TRUE(mSlots, NS_ERROR_UNEXPECTED); rv = nsXULElement::Create(mSlots->mNodeInfo, getter_AddRefs(result)); NS_ENSURE_SUCCESS(rv, rv); // XXX setting document on nodes not in a document so XBL will bind // and chrome won't break. Make XBL bind to document-less nodes! result->SetDocument(mDocument, PR_TRUE, PR_TRUE); } // Copy attributes PRInt32 count = mAttrsAndChildren.AttrCount(); for (PRInt32 i = 0; i < count; ++i) { const nsAttrName* name = mAttrsAndChildren.GetSafeAttrNameAt(i); nsAutoString valStr; mAttrsAndChildren.AttrAt(i)->ToString(valStr); rv = result->SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(), valStr, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); } // XXX TODO: set up RDF generic builder n' stuff if there is a // 'datasources' attribute? This is really kind of tricky, // because then we'd need to -selectively- copy children that // -weren't- generated from RDF. Ugh. Forget it. // Note that we're _not_ copying mControllers. if (aDeep) { // Copy cloned children! PRInt32 i, count = mAttrsAndChildren.ChildCount(); for (i = 0; i < count; ++i) { nsIContent* child = mAttrsAndChildren.ChildAt(i); NS_ASSERTION(child != nsnull, "null ptr"); if (! child) return NS_ERROR_UNEXPECTED; nsCOMPtr domchild = do_QueryInterface(child); NS_ASSERTION(domchild != nsnull, "child is not a DOM node"); if (! domchild) return NS_ERROR_UNEXPECTED; nsCOMPtr newdomchild; rv = domchild->CloneNode(PR_TRUE, getter_AddRefs(newdomchild)); if (NS_FAILED(rv)) return rv; nsCOMPtr newchild = do_QueryInterface(newdomchild); NS_ASSERTION(newchild != nsnull, "newdomchild is not an nsIContent"); if (! newchild) return NS_ERROR_UNEXPECTED; rv = result->AppendChildTo(newchild, PR_FALSE, PR_FALSE); if (NS_FAILED(rv)) return rv; } } return CallQueryInterface(result, aReturn); } NS_IMETHODIMP nsXULElement::Normalize() { NS_NOTYETIMPLEMENTED("write me!"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsXULElement::IsSupported(const nsAString& aFeature, const nsAString& aVersion, PRBool* aReturn) { NS_NOTYETIMPLEMENTED("write me!"); return NS_ERROR_NOT_IMPLEMENTED; } //---------------------------------------------------------------------- // nsIDOMElement interface NS_IMETHODIMP nsXULElement::GetTagName(nsAString& aTagName) { NodeInfo()->GetQualifiedName(aTagName); return NS_OK; } nsINodeInfo * nsXULElement::GetNodeInfo() const { return NodeInfo(); } NS_IMETHODIMP nsXULElement::GetAttribute(const nsAString& aName, nsAString& aReturn) { const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); if (!name) { // XXX should be SetDOMStringToNull(aReturn); // See bug 232598 aReturn.Truncate(); return NS_OK; } GetAttr(name->NamespaceID(), name->LocalName(), aReturn); return NS_OK; } NS_IMETHODIMP nsXULElement::SetAttribute(const nsAString& aName, const nsAString& aValue) { const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); if (!name) { nsresult rv = nsContentUtils::CheckQName(aName, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr nameAtom = do_GetAtom(aName); NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY); return SetAttr(kNameSpaceID_None, nameAtom, aValue, PR_TRUE); } return SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(), aValue, PR_TRUE); } NS_IMETHODIMP nsXULElement::RemoveAttribute(const nsAString& aName) { const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); if (!name) { return NS_OK; } // Hold a strong reference here so that the atom or nodeinfo doesn't go // away during UnsetAttr. If it did UnsetAttr would be left with a // dangling pointer as argument without knowing it. nsAttrName tmp(*name); return UnsetAttr(name->NamespaceID(), name->LocalName(), PR_TRUE); } NS_IMETHODIMP nsXULElement::GetAttributeNode(const nsAString& aName, nsIDOMAttr** aReturn) { NS_PRECONDITION(aReturn, "nullpointer"); *aReturn = nsnull; nsCOMPtr map; nsresult rv = GetAttributes(getter_AddRefs(map)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr node; rv = map->GetNamedItem(aName, getter_AddRefs(node)); NS_ENSURE_SUCCESS(rv, rv); if (node) { return CallQueryInterface(node, aReturn); } return NS_OK; } NS_IMETHODIMP nsXULElement::SetAttributeNode(nsIDOMAttr* aNewAttr, nsIDOMAttr** aReturn) { NS_PRECONDITION(aReturn, "nullpointer"); NS_ENSURE_ARG_POINTER(aNewAttr); *aReturn = nsnull; nsCOMPtr map; nsresult rv = GetAttributes(getter_AddRefs(map)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr returnNode; rv = map->SetNamedItem(aNewAttr, getter_AddRefs(returnNode)); NS_ENSURE_SUCCESS(rv, rv); if (returnNode) { return CallQueryInterface(returnNode, aReturn); } return NS_OK; } NS_IMETHODIMP nsXULElement::RemoveAttributeNode(nsIDOMAttr* aOldAttr, nsIDOMAttr** aReturn) { NS_PRECONDITION(aReturn, "nullpointer"); NS_ENSURE_ARG_POINTER(aOldAttr); *aReturn = nsnull; nsCOMPtr map; nsresult rv = GetAttributes(getter_AddRefs(map)); NS_ENSURE_SUCCESS(rv, rv); nsAutoString name; rv = aOldAttr->GetName(name); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr node; rv = map->RemoveNamedItem(name, getter_AddRefs(node)); NS_ENSURE_SUCCESS(rv, rv); if (node) { return CallQueryInterface(node, aReturn); } return NS_OK; } NS_IMETHODIMP nsXULElement::GetElementsByTagName(const nsAString& aTagname, nsIDOMNodeList** aReturn) { nsCOMPtr nameAtom = do_GetAtom(aTagname); NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY); nsCOMPtr list; NS_GetContentList(mDocument, nameAtom, kNameSpaceID_Unknown, this, getter_AddRefs(list)); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); return CallQueryInterface(list, aReturn); } NS_IMETHODIMP nsXULElement::GetAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, nsAString& aReturn) { nsCOMPtr name = do_GetAtom(aLocalName); PRInt32 nsid; nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI, &nsid); if (nsid == kNameSpaceID_Unknown) { // Unkonwn namespace means no attr... aReturn.Truncate(); return NS_OK; } GetAttr(nsid, name, aReturn); return NS_OK; } NS_IMETHODIMP nsXULElement::SetAttributeNS(const nsAString& aNamespaceURI, const nsAString& aQualifiedName, const nsAString& aValue) { nsCOMPtr ni; nsresult rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName, NodeInfo()->NodeInfoManager(), getter_AddRefs(ni)); NS_ENSURE_SUCCESS(rv, rv); return SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(), aValue, PR_TRUE); } NS_IMETHODIMP nsXULElement::RemoveAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName) { PRInt32 nameSpaceId; nsCOMPtr tag = do_GetAtom(aLocalName); nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI, &nameSpaceId); return UnsetAttr(nameSpaceId, tag, PR_TRUE); } NS_IMETHODIMP nsXULElement::GetAttributeNodeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, nsIDOMAttr** aReturn) { NS_PRECONDITION(aReturn, "nullpointer"); *aReturn = nsnull; nsCOMPtr map; nsresult rv = GetAttributes(getter_AddRefs(map)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr node; rv = map->GetNamedItemNS(aNamespaceURI, aLocalName, getter_AddRefs(node)); NS_ENSURE_SUCCESS(rv, rv); if (node) { return CallQueryInterface(node, aReturn); } return NS_OK; } NS_IMETHODIMP nsXULElement::SetAttributeNodeNS(nsIDOMAttr* aNewAttr, nsIDOMAttr** aReturn) { NS_PRECONDITION(aReturn, "nullpointer"); NS_ENSURE_ARG_POINTER(aNewAttr); *aReturn = nsnull; nsCOMPtr map; nsresult rv = GetAttributes(getter_AddRefs(map)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr returnNode; rv = map->SetNamedItemNS(aNewAttr, getter_AddRefs(returnNode)); NS_ENSURE_SUCCESS(rv, rv); if (returnNode) { return CallQueryInterface(returnNode, aReturn); } return NS_OK; } NS_IMETHODIMP nsXULElement::GetElementsByTagNameNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, nsIDOMNodeList** aReturn) { nsCOMPtr nameAtom = do_GetAtom(aLocalName); NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY); PRInt32 nameSpaceId = kNameSpaceID_Unknown; nsCOMPtr list; if (!aNamespaceURI.Equals(NS_LITERAL_STRING("*"))) { nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI, &nameSpaceId); if (nameSpaceId == kNameSpaceID_Unknown) { // Unknown namespace means no matches, we create an empty list... NS_GetContentList(mDocument, nsnull, kNameSpaceID_None, nsnull, getter_AddRefs(list)); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); } } if (!list) { NS_GetContentList(mDocument, nameAtom, nameSpaceId, this, getter_AddRefs(list)); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); } return CallQueryInterface(list, aReturn); } NS_IMETHODIMP nsXULElement::HasAttribute(const nsAString& aName, PRBool* aReturn) { NS_PRECONDITION(aReturn, "nullpointer"); const nsAttrName* name = InternalGetExistingAttrNameFromQName(aName); *aReturn = (name != nsnull); return NS_OK; } NS_IMETHODIMP nsXULElement::HasAttributeNS(const nsAString& aNamespaceURI, const nsAString& aLocalName, PRBool* aReturn) { NS_ENSURE_ARG_POINTER(aReturn); nsCOMPtr name = do_GetAtom(aLocalName); PRInt32 nsid; nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(aNamespaceURI, &nsid); if (nsid == kNameSpaceID_Unknown) { // Unkonwn namespace means no attr... *aReturn = PR_FALSE; return NS_OK; } *aReturn = HasAttr(nsid, name); return NS_OK; } NS_IMETHODIMP nsXULElement::GetElementsByAttribute(const nsAString& aAttribute, const nsAString& aValue, nsIDOMNodeList** aReturn) { // XXX This should use nsContentList, but that does not support // _two_ strings being passed to the match func. Ah, the ability // to create real closures, where art thou? nsRDFDOMNodeList* elements = new nsRDFDOMNodeList(); NS_ENSURE_TRUE(elements, NS_ERROR_OUT_OF_MEMORY); NS_ADDREF(elements); GetElementsByAttribute(this, aAttribute, aValue, elements); *aReturn = elements; return NS_OK; } //---------------------------------------------------------------------- // nsIXMLContent interface NS_IMETHODIMP nsXULElement::MaybeTriggerAutoLink(nsIDocShell *aShell) { return NS_OK; } //---------------------------------------------------------------------- // nsIXULContent interface NS_IMETHODIMP_(PRUint32) nsXULElement::PeekChildCount() const { return mAttrsAndChildren.ChildCount(); } NS_IMETHODIMP nsXULElement::SetLazyState(LazyState aFlags) { nsresult rv = EnsureSlots(); NS_ENSURE_SUCCESS(rv, rv); mSlots->mLazyState |= aFlags; return NS_OK; } NS_IMETHODIMP nsXULElement::ClearLazyState(LazyState aFlags) { // No need to clear a flag we've never set. if (mSlots) { mSlots->mLazyState &= ~aFlags; } return NS_OK; } NS_IMETHODIMP nsXULElement::GetLazyState(LazyState aFlag, PRBool& aResult) { aResult = mSlots && mSlots->mLazyState & aFlag; return NS_OK; } NS_IMETHODIMP nsXULElement::AddScriptEventListener(nsIAtom* aName, const nsAString& aValue) { if (! mDocument) return NS_OK; // XXX nsresult rv; nsISupports *target = NS_STATIC_CAST(nsIContent *, this); PRBool defer = PR_TRUE; nsCOMPtr manager; nsIContent *root = mDocument->GetRootContent(); nsCOMPtr content(do_QueryInterface(NS_STATIC_CAST(nsIStyledContent*, this))); if ((!root || root == content) && !NodeInfo()->Equals(nsXULAtoms::overlay)) { nsIScriptGlobalObject *global = mDocument->GetScriptGlobalObject(); nsCOMPtr receiver = do_QueryInterface(global); if (! receiver) return NS_ERROR_UNEXPECTED; rv = receiver->GetListenerManager(getter_AddRefs(manager)); target = global; defer = PR_FALSE; } else { rv = GetListenerManager(getter_AddRefs(manager)); } if (NS_FAILED(rv)) return rv; return manager->AddScriptEventListener(target, aName, aValue, defer); } nsresult nsXULElement::GetListenerManager(nsIEventListenerManager** aResult) { if (!mListenerManager) { nsresult rv = NS_NewEventListenerManager(getter_AddRefs(mListenerManager)); if (NS_FAILED(rv)) return rv; mListenerManager->SetListenerTarget(NS_STATIC_CAST(nsIContent*, this)); } *aResult = mListenerManager; NS_ADDREF(*aResult); return NS_OK; } //---------------------------------------------------------------------- // nsIScriptEventHandlerOwner interface nsresult nsXULElement::GetCompiledEventHandler(nsIAtom *aName, void** aHandler) { XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheTests); *aHandler = nsnull; nsXULPrototypeAttribute *attr = FindPrototypeAttribute(kNameSpaceID_None, aName); if (attr) { XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheHits); *aHandler = attr->mEventHandler; } return NS_OK; } nsresult nsXULElement::CompileEventHandler(nsIScriptContext* aContext, void* aTarget, nsIAtom *aName, const nsAString& aBody, const char* aURL, PRUint32 aLineNo, void** aHandler) { nsresult rv; JSObject* scopeObject; XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheSets); nsIScriptContext *context; if (mPrototype) { // It'll be shared among the instances of the prototype. // Use null for the scope object when precompiling shared // prototype scripts. scopeObject = nsnull; // Use the prototype document's special context. Because // scopeObject is null, the JS engine has no other source of // .__proto__ than to look in // cx->globalObject for Function.prototype. That prototype // keeps the global object alive, so if we use this document's // global object, we'll be putting something in the prototype // that protects this document's global object from GC. nsCOMPtr xuldoc = do_QueryInterface(mDocument); NS_ENSURE_TRUE(xuldoc, NS_ERROR_UNEXPECTED); nsCOMPtr protodoc; rv = xuldoc->GetMasterPrototype(getter_AddRefs(protodoc)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(protodoc, NS_ERROR_UNEXPECTED); nsCOMPtr globalOwner = do_QueryInterface(protodoc); nsCOMPtr global; globalOwner->GetScriptGlobalObject(getter_AddRefs(global)); NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED); context = global->GetContext(); } else { // We don't have a prototype; do a one-off compile. NS_ASSERTION(aTarget != nsnull, "no prototype and no target?!"); scopeObject = NS_REINTERPRET_CAST(JSObject*, aTarget); context = aContext; } // Compile the event handler rv = context->CompileEventHandler(scopeObject, aName, aBody, aURL, aLineNo, !scopeObject, aHandler); if (NS_FAILED(rv)) return rv; if (! scopeObject) { // If it's a shared handler, we need to bind the shared // function object to the real target. // XXX: Shouldn't this use context and not aContext? rv = aContext->BindCompiledEventHandler(aTarget, aName, *aHandler); if (NS_FAILED(rv)) return rv; } nsXULPrototypeAttribute *attr = FindPrototypeAttribute(kNameSpaceID_None, aName); if (attr) { XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheFills); attr->mEventHandler = *aHandler; if (attr->mEventHandler) { JSContext *cx = (JSContext*) context->GetNativeContext(); if (!cx) return NS_ERROR_UNEXPECTED; rv = AddJSGCRoot(&attr->mEventHandler, "nsXULPrototypeAttribute::mEventHandler"); if (NS_FAILED(rv)) return rv; } } return NS_OK; } void nsXULElement::AddListenerFor(const nsAttrName& aName, PRBool aCompileEventHandlers) { // If appropriate, add a popup listener and/or compile the event // handler. Called when we change the element's document, create a // new element, change an attribute's value, etc. // Eventlistenener-attributes are always in the null namespace if (aName.IsAtom()) { nsIAtom *attr = aName.Atom(); MaybeAddPopupListener(attr); if (aCompileEventHandlers && IsEventHandler(attr)) { nsAutoString value; GetAttr(kNameSpaceID_None, attr, value); AddScriptEventListener(attr, value); } } } void nsXULElement::MaybeAddPopupListener(nsIAtom* aLocalName) { // If appropriate, add a popup listener. Called when we change the // element's document, create a new element, change an attribute's // value, etc. if (aLocalName == nsXULAtoms::menu || aLocalName == nsXULAtoms::contextmenu || // XXXdwh popup and context are deprecated aLocalName == nsXULAtoms::popup || aLocalName == nsXULAtoms::context) { AddPopupListener(aLocalName); } } //---------------------------------------------------------------------- // // nsIContent interface // void nsXULElement::SetDocument(nsIDocument* aDocument, PRBool aDeep, PRBool aCompileEventHandlers) { if (aDocument != mDocument) { if (mDocument) { // Notify XBL- & nsIAnonymousContentCreator-generated // anonymous content that the document is changing. nsIBindingManager *bindingManager = mDocument->GetBindingManager(); NS_ASSERTION(bindingManager, "no binding manager"); if (bindingManager) { bindingManager->ChangeDocumentFor(this, mDocument, aDocument); } nsCOMPtr nsDoc(do_QueryInterface(mDocument)); nsDoc->SetBoxObjectFor(this, nsnull); } // mControllers can own objects that are implemented // in JavaScript (such as some implementations of // nsIControllers. These objects prevent their global // object's script object from being garbage collected, // which means JS continues to hold an owning reference // to the GlobalWindowImpl, which owns the document, // which owns this content. That's a cycle, so we break // it here. (It might be better to break this by releasing // mDocument in GlobalWindowImpl::SetDocShell, but I'm not // sure whether that would fix all possible cycles through // mControllers.) if (!aDocument && mSlots) { mSlots->mControllers = nsnull; // Forces release } if (mListenerManager) mListenerManager->SetListenerTarget(nsnull); mListenerManager = nsnull; nsIContent::SetDocument(aDocument, aDeep, aCompileEventHandlers); if (mDocument) { // When we SetDocument(), we're either adding an element // into the document that wasn't there before, or we're // moving the element from one document to // another. Regardless, we need to (re-)initialize several // attributes that are dependant on the document. Do that // now. PRInt32 count = mAttrsAndChildren.AttrCount(); PRBool haveLocalAttributes = (count > 0); PRInt32 i; for (i = 0; i < count; i++) { AddListenerFor(*mAttrsAndChildren.GetSafeAttrNameAt(i), aCompileEventHandlers); } if (mPrototype) { PRInt32 count = mPrototype->mNumAttributes; for (i = 0; i < count; i++) { nsXULPrototypeAttribute *protoattr = &mPrototype->mAttributes[i]; // Don't clobber a locally modified attribute. if (haveLocalAttributes && mAttrsAndChildren.GetAttr(protoattr->mName.LocalName(), protoattr->mName.NamespaceID())) { continue; } AddListenerFor(protoattr->mName, aCompileEventHandlers); } } } } if (aDeep) { PRInt32 i; for (i = mAttrsAndChildren.ChildCount() - 1; i >= 0; --i) { mAttrsAndChildren.ChildAt(i)->SetDocument(aDocument, aDeep, aCompileEventHandlers); } } } void nsXULElement::SetParent(nsIContent* aParent) { nsIContent::SetParent(aParent); if (aParent) { nsIContent* bindingPar = aParent->GetBindingParent(); if (bindingPar) SetBindingParent(bindingPar); } } PRBool nsXULElement::IsNativeAnonymous() const { return PR_FALSE; } void nsXULElement::SetNativeAnonymous(PRBool aAnonymous) { // XXX Need to make this actually do something - bug 165110 } PRUint32 nsXULElement::GetChildCount() const { if (NS_FAILED(EnsureContentsGenerated())) { return 0; } return PeekChildCount(); } nsIContent * nsXULElement::GetChildAt(PRUint32 aIndex) const { if (NS_FAILED(EnsureContentsGenerated())) { return nsnull; } return mAttrsAndChildren.GetSafeChildAt(aIndex); } PRInt32 nsXULElement::IndexOf(nsIContent* aPossibleChild) const { if (NS_FAILED(EnsureContentsGenerated())) { return -1; } return mAttrsAndChildren.IndexOfChild(aPossibleChild); } nsresult nsXULElement::InsertChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify, PRBool aDeepSetDocument) { nsresult rv = EnsureContentsGenerated(); NS_ENSURE_SUCCESS(rv, rv); NS_PRECONDITION(nsnull != aKid, "null ptr"); // Make sure that we're not trying to insert the same child // twice. If we do, the DOM APIs (e.g., GetNextSibling()), will // freak out. NS_ASSERTION(mAttrsAndChildren.IndexOfChild(aKid) < 0, "element is already a child"); PRBool isAppend = aIndex == mAttrsAndChildren.ChildCount(); mozAutoDocUpdate updateBatch(mDocument, UPDATE_CONTENT_MODEL, aNotify); if (aIndex > mAttrsAndChildren.ChildCount()) { // XXX This *does* happen, probably a bug in the overlay code. // See bug 233641. return NS_ERROR_FAILURE; } rv = mAttrsAndChildren.InsertChildAt(aKid, aIndex); NS_ENSURE_SUCCESS(rv, rv); aKid->SetParent(this); //nsRange::OwnerChildInserted(this, aIndex); if (mDocument) { aKid->SetDocument(mDocument, aDeepSetDocument, PR_TRUE); if (aNotify) { if (isAppend) { mDocument->ContentAppended(this, aIndex); } else { mDocument->ContentInserted(this, aKid, aIndex); } } if (HasMutationListeners(this, NS_EVENT_BITS_MUTATION_NODEINSERTED)) { nsMutationEvent mutation(NS_MUTATION_NODEINSERTED, aKid); mutation.mRelatedNode = do_QueryInterface(NS_STATIC_CAST(nsIStyledContent*, this)); nsEventStatus status = nsEventStatus_eIgnore; aKid->HandleDOMEvent(nsnull, &mutation, nsnull, NS_EVENT_FLAG_INIT, &status); } } return NS_OK; } nsresult nsXULElement::ReplaceChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify, PRBool aDeepSetDocument) { nsresult rv = EnsureContentsGenerated(); NS_ENSURE_SUCCESS(rv, rv); NS_PRECONDITION(nsnull != aKid, "null ptr"); if (!aKid) return NS_ERROR_NULL_POINTER; nsCOMPtr oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex); NS_ASSERTION(oldKid != nsnull, "out-of-bounds"); if (!oldKid) { return NS_ERROR_FAILURE; } if (oldKid == aKid) return NS_OK; mozAutoDocUpdate updateBatch(mDocument, UPDATE_CONTENT_MODEL, aNotify); mAttrsAndChildren.ReplaceChildAt(aKid, aIndex); aKid->SetParent(this); //nsRange::OwnerChildReplaced(this, aIndex, oldKid); if (mDocument) { aKid->SetDocument(mDocument, aDeepSetDocument, PR_TRUE); if (aNotify) { mDocument->ContentReplaced(this, oldKid, aKid, aIndex); } if (HasMutationListeners(this, NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) { nsMutationEvent mutation(NS_MUTATION_SUBTREEMODIFIED, this); mutation.mRelatedNode = do_QueryInterface(oldKid); nsEventStatus status = nsEventStatus_eIgnore; HandleDOMEvent(nsnull, &mutation, nsnull, NS_EVENT_FLAG_INIT, &status); } } // This will cause the script object to be unrooted for each // element in the subtree. oldKid->SetDocument(nsnull, PR_TRUE, PR_TRUE); // We've got no mo' parent. oldKid->SetParent(nsnull); return NS_OK; } nsresult nsXULElement::AppendChildTo(nsIContent* aKid, PRBool aNotify, PRBool aDeepSetDocument) { nsresult rv = EnsureContentsGenerated(); NS_ENSURE_SUCCESS(rv, rv); NS_PRECONDITION((nsnull != aKid) && (aKid != NS_STATIC_CAST(nsIStyledContent*, this)), "null ptr"); mozAutoDocUpdate updateBatch(mDocument, UPDATE_CONTENT_MODEL, aNotify); rv = mAttrsAndChildren.AppendChild(aKid); NS_ENSURE_SUCCESS(rv, rv); aKid->SetParent(this); // ranges don't need adjustment since new child is at end of list if (mDocument) { aKid->SetDocument(mDocument, aDeepSetDocument, PR_TRUE); if (aNotify) { mDocument->ContentAppended(this, mAttrsAndChildren.ChildCount() - 1); } if (HasMutationListeners(this, NS_EVENT_BITS_MUTATION_NODEINSERTED)) { nsMutationEvent mutation(NS_MUTATION_NODEINSERTED, aKid); mutation.mRelatedNode = do_QueryInterface(NS_STATIC_CAST(nsIStyledContent*, this)); nsEventStatus status = nsEventStatus_eIgnore; aKid->HandleDOMEvent(nsnull, &mutation, nsnull, NS_EVENT_FLAG_INIT, &status); } } return NS_OK; } nsresult nsXULElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify) { nsresult rv = EnsureContentsGenerated(); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr oldKid = mAttrsAndChildren.ChildAt(aIndex); NS_ENSURE_TRUE(oldKid, NS_ERROR_FAILURE); mozAutoDocUpdate updateBatch(mDocument, UPDATE_CONTENT_MODEL, aNotify); if (HasMutationListeners(this, NS_EVENT_BITS_MUTATION_NODEREMOVED)) { nsMutationEvent mutation(NS_MUTATION_NODEREMOVED, oldKid); mutation.mRelatedNode = do_QueryInterface(NS_STATIC_CAST(nsIStyledContent*, this)); nsEventStatus status = nsEventStatus_eIgnore; oldKid->HandleDOMEvent(nsnull, &mutation, nsnull, NS_EVENT_FLAG_INIT, &status); } // On the removal of a , , or element, // the possibility exists that some of the items in the removed subtree // are selected (and therefore need to be deselected). We need to account for this. nsCOMPtr controlElement; nsCOMPtr listBox; PRBool fireSelectionHandler = PR_FALSE; // -1 = do nothing, -2 = null out current item // anything else = index to re-set as current PRInt32 newCurrentIndex = -1; nsINodeInfo *ni = oldKid->GetNodeInfo(); if (ni && ni->Equals(nsXULAtoms::listitem, kNameSpaceID_XUL)) { // This is the nasty case. We have (potentially) a slew of selected items // and cells going away. // First, retrieve the tree. // Check first whether this element IS the tree controlElement = do_QueryInterface((nsIDOMXULElement*)this); // If it's not, look at our parent if (!controlElement) rv = GetParentTree(getter_AddRefs(controlElement)); if (controlElement) { nsCOMPtr parentKid = do_QueryInterface(oldKid); // Iterate over all of the items and find out if they are contained inside // the removed subtree. PRInt32 length; controlElement->GetSelectedCount(&length); for (PRInt32 i = 0; i < length; i++) { nsCOMPtr node; controlElement->GetSelectedItem(i, getter_AddRefs(node)); nsCOMPtr selNode(do_QueryInterface(node)); if (selNode == parentKid && NS_SUCCEEDED(rv = controlElement->RemoveItemFromSelection(node))) { length--; i--; fireSelectionHandler = PR_TRUE; } } nsCOMPtr curItem; controlElement->GetCurrentItem(getter_AddRefs(curItem)); nsCOMPtr curNode = do_QueryInterface(curItem); if (IsAncestor(parentKid, curNode)) { // Current item going away nsCOMPtr box; controlElement->GetBoxObject(getter_AddRefs(box)); listBox = do_QueryInterface(box); if (listBox) { nsCOMPtr domElem = do_QueryInterface(parentKid); if (domElem) listBox->GetIndexOfItem(domElem, &newCurrentIndex); } // If any of this fails, we'll just set the current item to null if (newCurrentIndex == -1) newCurrentIndex = -2; } } } mAttrsAndChildren.RemoveChildAt(aIndex); //nsRange::OwnerChildRemoved(this, aIndex, oldKid); if (aNotify && mDocument) { mDocument->ContentRemoved(this, oldKid, aIndex); } if (newCurrentIndex == -2) controlElement->SetCurrentItem(nsnull); else if (newCurrentIndex > -1) { // Make sure the index is still valid PRInt32 treeRows; listBox->GetRowCount(&treeRows); if (treeRows > 0) { newCurrentIndex = PR_MIN((treeRows - 1), newCurrentIndex); nsCOMPtr newCurrentItem; listBox->GetItemAtIndex(newCurrentIndex, getter_AddRefs(newCurrentItem)); nsCOMPtr xulCurItem = do_QueryInterface(newCurrentItem); if (xulCurItem) controlElement->SetCurrentItem(xulCurItem); } else { controlElement->SetCurrentItem(nsnull); } } if (fireSelectionHandler) { nsCOMPtr doc(do_QueryInterface(mDocument)); nsCOMPtr event; doc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event)); if (event) { event->InitEvent(NS_LITERAL_STRING("select"), PR_FALSE, PR_TRUE); PRBool noDefault; nsCOMPtr target(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this))); NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); target->DispatchEvent(event, &noDefault); } } // This will cause the script object to be unrooted for each // element in the subtree. oldKid->SetDocument(nsnull, PR_TRUE, PR_TRUE); // We've got no mo' parent. oldKid->SetParent(nsnull); return NS_OK; } void nsXULElement::GetNameSpaceID(PRInt32* aNameSpaceID) const { *aNameSpaceID = NodeInfo()->NamespaceID(); } nsIAtom * nsXULElement::Tag() const { return NodeInfo()->NameAtom(); } already_AddRefed nsXULElement::GetExistingAttrNameFromQName(const nsAString& aStr) const { const nsAttrName* name = InternalGetExistingAttrNameFromQName(aStr); if (!name) { return nsnull; } nsINodeInfo* nodeInfo; if (name->IsAtom()) { NodeInfo()->NodeInfoManager()->GetNodeInfo(name->Atom(), nsnull, kNameSpaceID_None, &nodeInfo); } else { NS_ADDREF(nodeInfo = name->NodeInfo()); } return nodeInfo; } void nsXULElement::UnregisterAccessKey(const nsAString& aOldValue) { // If someone changes the accesskey, unregister the old one // if (mDocument && !aOldValue.IsEmpty()) { nsIPresShell *shell = mDocument->GetShellAt(0); if (shell) { PRBool validElement = PR_TRUE; // find out what type of content node this is if (NodeInfo()->Equals(nsXULAtoms::label)) { // XXXjag a side-effect is that we filter out // anonymous