/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * 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. * * Original Author: David W. Hyatt (hyatt@netscape.com) * * Contributor(s): Brendan Eich (brendan@mozilla.org) */ #include "nsCOMPtr.h" #include "nsIXBLBinding.h" #include "nsIInputStream.h" #include "nsINameSpaceManager.h" #include "nsHashtable.h" #include "nsIURI.h" #include "nsIURL.h" #include "nsIDOMEventReceiver.h" #include "nsIChannel.h" #include "nsXPIDLString.h" #include "nsIParser.h" #include "nsParserCIID.h" #include "nsNetUtil.h" #include "plstr.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsIXMLContent.h" #include "nsIXULContent.h" #include "nsIXMLContentSink.h" #include "nsLayoutCID.h" #include "nsXMLDocument.h" #include "nsIDOMElement.h" #include "nsIDOMText.h" #include "nsSupportsArray.h" #include "nsINameSpace.h" #include "nsJSUtils.h" #include "nsIJSRuntimeService.h" #include "nsXBLService.h" // Event listeners #include "nsIEventListenerManager.h" #include "nsIDOMMouseListener.h" #include "nsIDOMMouseMotionListener.h" #include "nsIDOMLoadListener.h" #include "nsIDOMFocusListener.h" #include "nsIDOMPaintListener.h" #include "nsIDOMKeyListener.h" #include "nsIDOMFormListener.h" #include "nsIDOMMenuListener.h" #include "nsIDOMDragListener.h" #include "nsIDOMAttr.h" #include "nsIDOMNamedNodeMap.h" #include "nsXBLEventHandler.h" #include "nsXBLBinding.h" // Static IIDs/CIDs. Try to minimize these. static char kNameSpaceSeparator = ':'; // Helper classes // {A2892B81-CED9-11d3-97FB-00400553EEF0} #define NS_IXBLATTR_IID \ { 0xa2892b81, 0xced9, 0x11d3, { 0x97, 0xfb, 0x0, 0x40, 0x5, 0x53, 0xee, 0xf0 } } class nsIXBLAttributeEntry : public nsISupports { public: static const nsIID& GetIID() { static nsIID iid = NS_IXBLATTR_IID; return iid; } NS_IMETHOD GetAttribute(nsIAtom** aResult) = 0; NS_IMETHOD GetElement(nsIContent** aResult) = 0; NS_IMETHOD GetNext(nsIXBLAttributeEntry** aResult) = 0; NS_IMETHOD SetNext(nsIXBLAttributeEntry* aEntry) = 0; }; class nsXBLAttributeEntry : public nsIXBLAttributeEntry { public: NS_IMETHOD GetAttribute(nsIAtom** aResult) { *aResult = mAttribute; NS_IF_ADDREF(*aResult); return NS_OK; }; NS_IMETHOD GetElement(nsIContent** aResult) { *aResult = mElement; NS_IF_ADDREF(*aResult); return NS_OK; }; NS_IMETHOD GetNext(nsIXBLAttributeEntry** aResult) { NS_IF_ADDREF(*aResult = mNext); return NS_OK; } NS_IMETHOD SetNext(nsIXBLAttributeEntry* aEntry) { mNext = aEntry; return NS_OK; } nsCOMPtr mElement; nsCOMPtr mAttribute; nsCOMPtr mNext; nsXBLAttributeEntry(nsIAtom* aAtom, nsIContent* aContent) { NS_INIT_REFCNT(); mAttribute = aAtom; mElement = aContent; }; virtual ~nsXBLAttributeEntry() {}; NS_DECL_ISUPPORTS }; NS_IMPL_ISUPPORTS1(nsXBLAttributeEntry, nsIXBLAttributeEntry) /***********************************************************************/ // // The JS class for XBLBinding // PR_STATIC_CALLBACK(void) XBLFinalize(JSContext *cx, JSObject *obj) { nsXBLJSClass* c = NS_STATIC_CAST(nsXBLJSClass*, ::JS_GetClass(cx, obj)); c->Drop(); } nsXBLJSClass::nsXBLJSClass(const nsCString& aClassName) { memset(this, 0, sizeof(nsXBLJSClass)); next = prev = NS_STATIC_CAST(JSCList*, this); name = nsXPIDLCString::Copy(aClassName); addProperty = delProperty = setProperty = getProperty = ::JS_PropertyStub; enumerate = ::JS_EnumerateStub; resolve = ::JS_ResolveStub; convert = ::JS_ConvertStub; finalize = XBLFinalize; } nsrefcnt nsXBLJSClass::Destroy() { NS_ASSERTION(next == prev && prev == NS_STATIC_CAST(JSCList*, this), "referenced nsXBLJSClass is on LRU list already!?"); if (nsXBLService::gClassTable) { nsStringKey key(name); (nsXBLService::gClassTable)->Remove(&key); } if (nsXBLService::gClassLRUListLength >= nsXBLService::gClassLRUListQuota) { // Over LRU list quota, just unhash and delete this class. delete this; } else { // Put this most-recently-used class on end of the LRU-sorted freelist. JSCList* mru = NS_STATIC_CAST(JSCList*, this); JS_APPEND_LINK(mru, &nsXBLService::gClassLRUList); nsXBLService::gClassLRUListLength++; } return 0; } // Static initialization PRUint32 nsXBLBinding::gRefCnt = 0; nsIAtom* nsXBLBinding::kContentAtom = nsnull; nsIAtom* nsXBLBinding::kInterfaceAtom = nsnull; nsIAtom* nsXBLBinding::kHandlersAtom = nsnull; nsIAtom* nsXBLBinding::kExcludesAtom = nsnull; nsIAtom* nsXBLBinding::kIncludesAtom = nsnull; nsIAtom* nsXBLBinding::kInheritsAtom = nsnull; nsIAtom* nsXBLBinding::kTypeAtom = nsnull; nsIAtom* nsXBLBinding::kCapturerAtom = nsnull; nsIAtom* nsXBLBinding::kExtendsAtom = nsnull; nsIAtom* nsXBLBinding::kChildrenAtom = nsnull; nsIAtom* nsXBLBinding::kValueAtom = nsnull; nsIAtom* nsXBLBinding::kHTMLAtom = nsnull; nsIAtom* nsXBLBinding::kMethodAtom = nsnull; nsIAtom* nsXBLBinding::kArgumentAtom = nsnull; nsIAtom* nsXBLBinding::kBodyAtom = nsnull; nsIAtom* nsXBLBinding::kPropertyAtom = nsnull; nsIAtom* nsXBLBinding::kOnSetAtom = nsnull; nsIAtom* nsXBLBinding::kOnGetAtom = nsnull; nsIAtom* nsXBLBinding::kGetterAtom = nsnull; nsIAtom* nsXBLBinding::kSetterAtom = nsnull; nsIAtom* nsXBLBinding::kNameAtom = nsnull; nsIAtom* nsXBLBinding::kReadOnlyAtom = nsnull; nsIAtom* nsXBLBinding::kURIAtom = nsnull; nsIAtom* nsXBLBinding::kAttachToAtom = nsnull; nsIAtom* nsXBLBinding::kBindingAttachedAtom = nsnull; nsXBLBinding::EventHandlerMapEntry nsXBLBinding::kEventHandlerMap[] = { { "click", nsnull, &NS_GET_IID(nsIDOMMouseListener) }, { "dblclick", nsnull, &NS_GET_IID(nsIDOMMouseListener) }, { "mousedown", nsnull, &NS_GET_IID(nsIDOMMouseListener) }, { "mouseup", nsnull, &NS_GET_IID(nsIDOMMouseListener) }, { "mouseover", nsnull, &NS_GET_IID(nsIDOMMouseListener) }, { "mouseout", nsnull, &NS_GET_IID(nsIDOMMouseListener) }, { "mousemove", nsnull, &NS_GET_IID(nsIDOMMouseMotionListener) }, { "keydown", nsnull, &NS_GET_IID(nsIDOMKeyListener) }, { "keyup", nsnull, &NS_GET_IID(nsIDOMKeyListener) }, { "keypress", nsnull, &NS_GET_IID(nsIDOMKeyListener) }, { "load", nsnull, &NS_GET_IID(nsIDOMLoadListener) }, { "unload", nsnull, &NS_GET_IID(nsIDOMLoadListener) }, { "abort", nsnull, &NS_GET_IID(nsIDOMLoadListener) }, { "error", nsnull, &NS_GET_IID(nsIDOMLoadListener) }, { "create", nsnull, &NS_GET_IID(nsIDOMMenuListener) }, { "close", nsnull, &NS_GET_IID(nsIDOMMenuListener) }, { "destroy", nsnull, &NS_GET_IID(nsIDOMMenuListener) }, { "command", nsnull, &NS_GET_IID(nsIDOMMenuListener) }, { "broadcast", nsnull, &NS_GET_IID(nsIDOMMenuListener) }, { "commandupdate", nsnull, &NS_GET_IID(nsIDOMMenuListener) }, { "overflow", nsnull, &NS_GET_IID(nsIDOMScrollListener) }, { "underflow", nsnull, &NS_GET_IID(nsIDOMScrollListener) }, { "overflowchanged", nsnull, &NS_GET_IID(nsIDOMScrollListener) }, { "focus", nsnull, &NS_GET_IID(nsIDOMFocusListener) }, { "blur", nsnull, &NS_GET_IID(nsIDOMFocusListener) }, { "submit", nsnull, &NS_GET_IID(nsIDOMFormListener) }, { "reset", nsnull, &NS_GET_IID(nsIDOMFormListener) }, { "change", nsnull, &NS_GET_IID(nsIDOMFormListener) }, { "select", nsnull, &NS_GET_IID(nsIDOMFormListener) }, { "input", nsnull, &NS_GET_IID(nsIDOMFormListener) }, { "paint", nsnull, &NS_GET_IID(nsIDOMPaintListener) }, { "dragenter", nsnull, &NS_GET_IID(nsIDOMDragListener) }, { "dragover", nsnull, &NS_GET_IID(nsIDOMDragListener) }, { "dragexit", nsnull, &NS_GET_IID(nsIDOMDragListener) }, { "dragdrop", nsnull, &NS_GET_IID(nsIDOMDragListener) }, { "draggesture", nsnull, &NS_GET_IID(nsIDOMDragListener) }, { nsnull, nsnull, nsnull } }; // Implementation ///////////////////////////////////////////////////////////////// // Implement our nsISupports methods NS_IMPL_ISUPPORTS1(nsXBLBinding, nsIXBLBinding) // Constructors/Destructors nsXBLBinding::nsXBLBinding(void) : mFirstHandler(nsnull), mIsStyleBinding(PR_TRUE), mAttributeTable(nsnull), mInsertionPointTable(nsnull) { NS_INIT_REFCNT(); gRefCnt++; if (gRefCnt == 1) { kContentAtom = NS_NewAtom("content"); kInterfaceAtom = NS_NewAtom("interface"); kHandlersAtom = NS_NewAtom("handlers"); kExcludesAtom = NS_NewAtom("excludes"); kIncludesAtom = NS_NewAtom("includes"); kInheritsAtom = NS_NewAtom("inherits"); kTypeAtom = NS_NewAtom("type"); kCapturerAtom = NS_NewAtom("capturer"); kExtendsAtom = NS_NewAtom("extends"); kChildrenAtom = NS_NewAtom("children"); kHTMLAtom = NS_NewAtom("html"); kValueAtom = NS_NewAtom("value"); kMethodAtom = NS_NewAtom("method"); kArgumentAtom = NS_NewAtom("argument"); kBodyAtom = NS_NewAtom("body"); kPropertyAtom = NS_NewAtom("property"); kOnSetAtom = NS_NewAtom("onset"); kOnGetAtom = NS_NewAtom("onget"); kGetterAtom = NS_NewAtom("getter"); kSetterAtom = NS_NewAtom("setter"); kNameAtom = NS_NewAtom("name"); kReadOnlyAtom = NS_NewAtom("readonly"); kURIAtom = NS_NewAtom("uri"); kAttachToAtom = NS_NewAtom("attachto"); kBindingAttachedAtom = NS_NewAtom("bindingattached"); EventHandlerMapEntry* entry = kEventHandlerMap; while (entry->mAttributeName) { entry->mAttributeAtom = NS_NewAtom(entry->mAttributeName); ++entry; } } } nsXBLBinding::~nsXBLBinding(void) { delete mAttributeTable; delete mInsertionPointTable; gRefCnt--; if (gRefCnt == 0) { NS_RELEASE(kContentAtom); NS_RELEASE(kInterfaceAtom); NS_RELEASE(kHandlersAtom); NS_RELEASE(kExcludesAtom); NS_RELEASE(kIncludesAtom); NS_RELEASE(kInheritsAtom); NS_RELEASE(kTypeAtom); NS_RELEASE(kCapturerAtom); NS_RELEASE(kExtendsAtom); NS_RELEASE(kChildrenAtom); NS_RELEASE(kHTMLAtom); NS_RELEASE(kValueAtom); NS_RELEASE(kMethodAtom); NS_RELEASE(kArgumentAtom); NS_RELEASE(kBodyAtom); NS_RELEASE(kPropertyAtom); NS_RELEASE(kOnSetAtom); NS_RELEASE(kOnGetAtom); NS_RELEASE(kGetterAtom); NS_RELEASE(kSetterAtom); NS_RELEASE(kNameAtom); NS_RELEASE(kReadOnlyAtom); NS_RELEASE(kURIAtom); NS_RELEASE(kAttachToAtom); NS_RELEASE(kBindingAttachedAtom); EventHandlerMapEntry* entry = kEventHandlerMap; while (entry->mAttributeName) { NS_IF_RELEASE(entry->mAttributeAtom); ++entry; } } } // nsIXBLBinding Interface //////////////////////////////////////////////////////////////// NS_IMETHODIMP nsXBLBinding::GetBaseBinding(nsIXBLBinding** aResult) { *aResult = mNextBinding; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsXBLBinding::SetBaseBinding(nsIXBLBinding* aBinding) { mNextBinding = aBinding; // Comptr handles rel/add return NS_OK; } NS_IMETHODIMP nsXBLBinding::GetAnonymousContent(nsIContent** aResult) { *aResult = mContent; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsXBLBinding::SetAnonymousContent(nsIContent* aParent) { // First cache the element. mContent = aParent; // Now we need to ensure two things. // (1) The anonymous content should be fooled into thinking it's in the bound // element's document. nsCOMPtr doc; mBoundElement->GetDocument(*getter_AddRefs(doc)); mContent->SetDocument(doc, PR_TRUE, AllowScripts()); // (2) The children's parent back pointer should not be to this synthetic root // but should instead point to the bound element. PRInt32 childCount; mContent->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; mContent->ChildAt(i, *getter_AddRefs(child)); child->SetParent(mBoundElement); } // (3) We need to insert entries into our attribute table for any elements // that are inheriting attributes. This table allows us to quickly determine // which elements in our anonymous content need to be updated when attributes change. ConstructAttributeTable(aParent); return NS_OK; } NS_IMETHODIMP nsXBLBinding::GetBindingElement(nsIContent** aResult) { *aResult = mBinding; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsXBLBinding::SetBindingElement(nsIContent* aElement) { mBinding = aElement; return NS_OK; } NS_IMETHODIMP nsXBLBinding::GetBoundElement(nsIContent** aResult) { *aResult = mBoundElement; NS_IF_ADDREF(*aResult); return NS_OK; } NS_IMETHODIMP nsXBLBinding::SetBoundElement(nsIContent* aElement) { mBoundElement = aElement; if (mNextBinding) mNextBinding->SetBoundElement(aElement); return NS_OK; } NS_IMETHODIMP nsXBLBinding::GenerateAnonymousContent(nsIContent* aBoundElement) { // Fetch the content element for this binding. nsCOMPtr content; GetImmediateChild(kContentAtom, getter_AddRefs(content)); if (!content) { // We have no anonymous content. if (mNextBinding) return mNextBinding->GenerateAnonymousContent(aBoundElement); else return NS_OK; } // Plan to build the content by default. PRBool buildContent = PR_TRUE; // See if there's an excludes attribute. nsAutoString excludes; content->GetAttribute(kNameSpaceID_None, kExcludesAtom, excludes); if (!excludes.EqualsWithConversion("*")) { PRInt32 childCount; aBoundElement->ChildCount(childCount); if (childCount > 0) { // We'll only build content if all the explicit children are // in the excludes list. if (!excludes.IsEmpty()) { // Walk the children and ensure that all of them // are in the excludes array. for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; aBoundElement->ChildAt(i, *getter_AddRefs(child)); nsCOMPtr tag; child->GetTag(*getter_AddRefs(tag)); if (!IsInExcludesList(tag, excludes)) { buildContent = PR_FALSE; break; } } } else buildContent = PR_FALSE; } } nsCOMPtr childrenElement; // see if we have a element GetNestedChild(kChildrenAtom, content, getter_AddRefs(childrenElement)); if (childrenElement) buildContent = PR_TRUE; if (buildContent) { // Always check the content element for potential attributes. nsCOMPtr node(do_QueryInterface(content)); nsCOMPtr namedMap; node->GetAttributes(getter_AddRefs(namedMap)); PRUint32 length; namedMap->GetLength(&length); nsCOMPtr attribute; for (PRUint32 i = 0; i < length; ++i) { namedMap->Item(i, getter_AddRefs(attribute)); nsCOMPtr attr(do_QueryInterface(attribute)); nsAutoString name; attr->GetName(name); if (!name.EqualsWithConversion("excludes")) { nsAutoString value; nsCOMPtr element(do_QueryInterface(mBoundElement)); element->GetAttribute(name, value); if (value.IsEmpty()) { nsAutoString value2; attr->GetValue(value2); nsCOMPtr atom = getter_AddRefs(NS_NewAtom(name)); mBoundElement->SetAttribute(kNameSpaceID_None, atom, value2, PR_FALSE); } } } nsCOMPtr domElement = do_QueryInterface(content); nsCOMPtr clonedNode; domElement->CloneNode(PR_TRUE, getter_AddRefs(clonedNode)); nsCOMPtr clonedContent = do_QueryInterface(clonedNode); SetAnonymousContent(clonedContent); if (childrenElement) BuildInsertionTable(); } /* XXX Handle selective decision to build anonymous content. if (mNextBinding) { return mNextBinding->GenerateAnonymousContent(aBoundElement); } */ return NS_OK; } NS_IMETHODIMP nsXBLBinding::InstallEventHandlers(nsIContent* aBoundElement, nsIXBLBinding** aBinding) { // Fetch the handlers element for this binding. nsCOMPtr handlers; GetImmediateChild(kHandlersAtom, getter_AddRefs(handlers)); nsXBLEventHandler* currHandler = nsnull; if (handlers && AllowScripts()) { // Now walk the handlers and add event listeners to the bound // element. PRInt32 childCount; handlers->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; handlers->ChildAt(i, *getter_AddRefs(child)); // Fetch the type attribute. // XXX Deal with a comma-separated list of types nsAutoString type; child->GetAttribute(kNameSpaceID_None, kTypeAtom, type); if (!type.IsEmpty()) { nsIID iid; PRBool found = PR_FALSE; PRBool special = PR_FALSE; nsCOMPtr eventAtom = getter_AddRefs(NS_NewAtom(type)); if (eventAtom.get() == kBindingAttachedAtom) { *aBinding = this; NS_ADDREF(*aBinding); special = PR_TRUE; } else GetEventHandlerIID(eventAtom, &iid, &found); if (found || special) { // Add an event listener for mouse and key events only. PRBool mouse = IsMouseHandler(type); PRBool key = IsKeyHandler(type); PRBool focus = IsFocusHandler(type); PRBool xul = IsXULHandler(type); PRBool scroll = IsScrollHandler(type); nsCOMPtr receiver = do_QueryInterface(mBoundElement); nsAutoString attachType; child->GetAttribute(kNameSpaceID_None, kAttachToAtom, attachType); if (attachType.EqualsWithConversion("document") || attachType.EqualsWithConversion("window")) { nsCOMPtr boundDoc; mBoundElement->GetDocument(*getter_AddRefs(boundDoc)); if (attachType.EqualsWithConversion("window")) { nsCOMPtr global; boundDoc->GetScriptGlobalObject(getter_AddRefs(global)); receiver = do_QueryInterface(global); } else receiver = do_QueryInterface(boundDoc); } if (mouse || key || focus || xul || scroll || special) { // Create a new nsXBLEventHandler. nsXBLEventHandler* handler; NS_NewXBLEventHandler(mBoundElement, child, type, &handler); // We chain all our event handlers together for easy // removal later (if/when the binding dies). if (!currHandler) mFirstHandler = handler; else currHandler->SetNextHandler(handler); currHandler = handler; // Figure out if we're using capturing or not. PRBool useCapture = PR_FALSE; nsAutoString capturer; child->GetAttribute(kNameSpaceID_None, kCapturerAtom, capturer); if (capturer.EqualsWithConversion("true")) useCapture = PR_TRUE; // Add the event listener. if (mouse) receiver->AddEventListener(type, (nsIDOMMouseListener*)handler, useCapture); else if(key) receiver->AddEventListener(type, (nsIDOMKeyListener*)handler, useCapture); else if(focus) receiver->AddEventListener(type, (nsIDOMFocusListener*)handler, useCapture); else if (xul) receiver->AddEventListener(type, (nsIDOMScrollListener*)handler, useCapture); else if (scroll) receiver->AddEventListener(type, (nsIDOMScrollListener*)handler, useCapture); if (!special) // Let the listener manager hold on to the handler. NS_RELEASE(handler); } else { // Call AddScriptEventListener for other IID types // XXX Want this to all go away! nsAutoString value; child->GetAttribute(kNameSpaceID_None, kValueAtom, value); if (value.IsEmpty()) GetTextData(child, value); AddScriptEventListener(mBoundElement, eventAtom, value, iid); } } } } } if (mNextBinding) { nsCOMPtr binding; mNextBinding->InstallEventHandlers(aBoundElement, getter_AddRefs(binding)); if (!*aBinding) { *aBinding = binding; NS_IF_ADDREF(*aBinding); } } return NS_OK; } const char* gPropertyArg[] = { "val" }; NS_IMETHODIMP nsXBLBinding::InstallProperties(nsIContent* aBoundElement) { // Always install the base class properties first, so that // derived classes can reference the base class properties. if (mNextBinding) mNextBinding->InstallProperties(aBoundElement); // Fetch the interface element for this binding. nsCOMPtr interfaceElement; GetImmediateChild(kInterfaceAtom, getter_AddRefs(interfaceElement)); if (interfaceElement && AllowScripts()) { // Get our bound element's script context. nsresult rv; nsCOMPtr document; mBoundElement->GetDocument(*getter_AddRefs(document)); if (!document) return NS_OK; nsCOMPtr global; document->GetScriptGlobalObject(getter_AddRefs(global)); if (!global) return NS_OK; nsCOMPtr context; rv = global->GetContext(getter_AddRefs(context)); if (NS_FAILED(rv)) return rv; // Init our class and insert it into the prototype chain. nsAutoString className; interfaceElement->GetAttribute(kNameSpaceID_None, kNameAtom, className); if (className.IsEmpty()) { mBinding->GetAttribute(kNameSpaceID_None, kURIAtom, className); if (className.IsEmpty()) return NS_ERROR_FAILURE; } nsCAutoString classStr; classStr.AssignWithConversion(className); JSObject* scriptObject; JSObject* classObject; if (NS_FAILED(rv = InitClass(classStr, context, document, (void**)&scriptObject, (void**)&classObject))) return rv; JSContext* cx = (JSContext*)context->GetNativeContext(); // Do a walk. PRInt32 childCount; interfaceElement->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; interfaceElement->ChildAt(i, *getter_AddRefs(child)); // See if we're a property or a method. nsCOMPtr tagName; child->GetTag(*getter_AddRefs(tagName)); if (tagName.get() == kMethodAtom && classObject) { // Obtain our name attribute. nsAutoString name, body; child->GetAttribute(kNameSpaceID_None, kNameAtom, name); // Now walk all of our args. // XXX I'm lame. 32 max args allowed. char* args[32]; PRUint32 argCount = 0; PRInt32 kidCount; child->ChildCount(kidCount); for (PRInt32 j = 0; j < kidCount; j++) { nsCOMPtr arg; child->ChildAt(j, *getter_AddRefs(arg)); nsCOMPtr kidTagName; arg->GetTag(*getter_AddRefs(kidTagName)); if (kidTagName.get() == kArgumentAtom) { // Get the argname and add it to the array. nsAutoString argName; arg->GetAttribute(kNameSpaceID_None, kNameAtom, argName); char* argStr = argName.ToNewCString(); args[argCount] = argStr; argCount++; } else if (kidTagName.get() == kBodyAtom) { PRInt32 textCount; arg->ChildCount(textCount); for (PRInt32 k = 0; k < textCount; k++) { // Get the child. nsCOMPtr textChild; arg->ChildAt(k, *getter_AddRefs(textChild)); nsCOMPtr text(do_QueryInterface(textChild)); if (text) { nsAutoString data; text->GetData(data); body += data; } } } } // Now that we have a body and args, compile the function // and then define it as a property. if (!body.IsEmpty()) { void* myFunc; nsCAutoString cname; cname.AssignWithConversion(name.GetUnicode()); rv = context->CompileFunction(classObject, cname, argCount, (const char**)args, body, nsnull, 0, PR_FALSE, &myFunc); } for (PRUint32 l = 0; l < argCount; l++) { nsMemory::Free(args[l]); } } else if (tagName.get() == kPropertyAtom) { // Obtain our name attribute. nsAutoString name; child->GetAttribute(kNameSpaceID_None, kNameAtom, name); if (!name.IsEmpty()) { // We have a property. nsAutoString getter, setter, readOnly; child->GetAttribute(kNameSpaceID_None, kOnGetAtom, getter); child->GetAttribute(kNameSpaceID_None, kOnSetAtom, setter); child->GetAttribute(kNameSpaceID_None, kReadOnlyAtom, readOnly); void* getFunc = nsnull; void* setFunc = nsnull; uintN attrs = JSPROP_ENUMERATE; if (readOnly.EqualsWithConversion("true")) attrs |= JSPROP_READONLY; // try for first tag if (getter.IsEmpty()) { PRInt32 childCount; child->ChildCount(childCount); nsCOMPtr getterElement; for (PRInt32 j=0; jChildAt(j, *getter_AddRefs(getterElement)); if (!getterElement) continue; nsCOMPtr getterTag; getterElement->GetTag(*getter_AddRefs(getterTag)); if (getterTag.get() == kGetterAtom) { GetTextData(getterElement, getter); break; // stop at first tag } } } if (!getter.IsEmpty() && classObject) { rv = context->CompileFunction(classObject, "onget", 0, nsnull, getter, nsnull, 0, PR_FALSE, &getFunc); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; attrs |= JSPROP_GETTER; } // try for first tag if (setter.IsEmpty()) { PRInt32 childCount; child->ChildCount(childCount); nsCOMPtr setterElement; for (PRInt32 j=0; jChildAt(j, *getter_AddRefs(setterElement)); if (!setterElement) continue; nsCOMPtr setterTag; setterElement->GetTag(*getter_AddRefs(setterTag)); if (setterTag.get() == kSetterAtom) { GetTextData(setterElement, setter); break; // stop at first tag } } } if (!setter.IsEmpty() && classObject) { rv = context->CompileFunction(classObject, "onset", 1, gPropertyArg, setter, nsnull, 0, PR_FALSE, &setFunc); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; attrs |= JSPROP_SETTER; } if ((getFunc || setFunc) && classObject) { // Having either a getter or setter results in the // destruction of any initial value that might be set. // This means we only have to worry about defining the getter // or setter. ::JS_DefineUCProperty(cx, (JSObject*)classObject, NS_REINTERPRET_CAST(const jschar*, name.GetUnicode()), name.Length(), JSVAL_VOID, (JSPropertyOp) getFunc, (JSPropertyOp) setFunc, attrs); } else { // Look for a normal value and just define that. nsCOMPtr textChild; PRInt32 textCount; child->ChildCount(textCount); nsAutoString answer; for (PRInt32 j = 0; j < textCount; j++) { // Get the child. child->ChildAt(j, *getter_AddRefs(textChild)); nsCOMPtr text(do_QueryInterface(textChild)); if (text) { nsAutoString data; text->GetData(data); answer += data; } } if (!answer.IsEmpty()) { // Evaluate our script and obtain a value. jsval result = nsnull; PRBool undefined; rv = context->EvaluateStringWithValue(answer, scriptObject, nsnull, nsnull, 0, nsnull, (void*) &result, &undefined); if (!undefined) { // Define that value as a property ::JS_DefineUCProperty(cx, (JSObject*)scriptObject, NS_REINTERPRET_CAST(const jschar*, name.GetUnicode()), name.Length(), result, nsnull, nsnull, attrs); } } } } } } } return NS_OK; } NS_IMETHODIMP nsXBLBinding::GetBaseTag(PRInt32* aNameSpaceID, nsIAtom** aResult) { if (mNextBinding) return mNextBinding->GetBaseTag(aNameSpaceID, aResult); // XXX Cache the value as a "base" attribute so that we don't do this // check over and over each time the bound element occurs. // We are the base binding. Obtain the extends attribute. nsAutoString extends; mBinding->GetAttribute(kNameSpaceID_None, kExtendsAtom, extends); if (!extends.IsEmpty()) { // Obtain the namespace prefix. nsAutoString prefix; PRInt32 offset = extends.FindChar(kNameSpaceSeparator); if (-1 != offset) { extends.Left(prefix, offset); extends.Cut(0, offset+1); } if (prefix.Length() > 0) { // Look up the prefix. nsCOMPtr prefixAtom = getter_AddRefs(NS_NewAtom(prefix)); nsCOMPtr nameSpace; nsCOMPtr xmlContent(do_QueryInterface(mBinding)); if (xmlContent) { xmlContent->GetContainingNameSpace(*getter_AddRefs(nameSpace)); if (nameSpace) { nsCOMPtr tagSpace; nameSpace->FindNameSpace(prefixAtom, *getter_AddRefs(tagSpace)); if (tagSpace) { // Score! Return the tag. tagSpace->GetNameSpaceID(*aNameSpaceID); *aResult = NS_NewAtom(extends); // The addref happens here } } } } } return NS_OK; } NS_IMETHODIMP nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, PRInt32 aNameSpaceID, PRBool aRemoveFlag) { // XXX check to see if we inherit anonymous content from a base binding // if (mNextBinding) // mNextBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag); if (!mAttributeTable) return NS_OK; nsISupportsKey key(aAttribute); nsCOMPtr supports = getter_AddRefs(NS_STATIC_CAST(nsISupports*, mAttributeTable->Get(&key))); nsCOMPtr xblAttr = do_QueryInterface(supports); if (!xblAttr) return NS_OK; // Iterate over the elements in the array. while (xblAttr) { nsCOMPtr element; nsCOMPtr setAttr; xblAttr->GetElement(getter_AddRefs(element)); xblAttr->GetAttribute(getter_AddRefs(setAttr)); if (aRemoveFlag) element->UnsetAttribute(aNameSpaceID, setAttr, PR_TRUE); else { nsAutoString value; nsresult result = mBoundElement->GetAttribute(aNameSpaceID, aAttribute, value); PRBool attrPresent = (result == NS_CONTENT_ATTR_NO_VALUE || result == NS_CONTENT_ATTR_HAS_VALUE); if (attrPresent) element->SetAttribute(aNameSpaceID, setAttr, value, PR_TRUE); } // See if we're the tag in XUL, and see if value is being // set or unset on us. nsCOMPtr tag; element->GetTag(*getter_AddRefs(tag)); if ((tag.get() == kHTMLAtom) && (setAttr.get() == kValueAtom)) { // Flush out all our kids. PRInt32 childCount; element->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) element->RemoveChildAt(0, PR_TRUE); if (!aRemoveFlag) { // Construct a new text node and insert it. nsAutoString value; mBoundElement->GetAttribute(aNameSpaceID, aAttribute, value); if (!value.IsEmpty()) { nsCOMPtr textNode; nsCOMPtr doc; mBoundElement->GetDocument(*getter_AddRefs(doc)); nsCOMPtr domDoc(do_QueryInterface(doc)); domDoc->CreateTextNode(value, getter_AddRefs(textNode)); nsCOMPtr dummy; nsCOMPtr domElement(do_QueryInterface(element)); domElement->AppendChild(textNode, getter_AddRefs(dummy)); } } } nsCOMPtr tmpAttr = xblAttr; tmpAttr->GetNext(getter_AddRefs(xblAttr)); } return NS_OK; } NS_IMETHODIMP nsXBLBinding::ExecuteAttachedHandler() { if (mNextBinding) mNextBinding->ExecuteAttachedHandler(); if (mFirstHandler) mFirstHandler->BindingAttached(); return NS_OK; } NS_IMETHODIMP nsXBLBinding::ExecuteDetachedHandler() { if (mFirstHandler) mFirstHandler->BindingDetached(); if (mNextBinding) mNextBinding->ExecuteDetachedHandler(); return NS_OK; } NS_IMETHODIMP nsXBLBinding::UnhookEventHandlers() { if (mFirstHandler) { // Unhook our event handlers. mFirstHandler->RemoveEventHandlers(); mFirstHandler = nsnull; } return NS_OK; } NS_IMETHODIMP nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument) { if (aOldDocument != aNewDocument) { mFirstHandler = nsnull; if (mNextBinding) mNextBinding->ChangeDocument(aOldDocument, aNewDocument); // Only style bindings get their prototypes unhooked. if (mIsStyleBinding) { // Now the binding dies. Unhook our prototypes. nsCOMPtr interfaceElement; GetImmediateChild(kInterfaceAtom, getter_AddRefs(interfaceElement)); if (interfaceElement) { nsCOMPtr global; aOldDocument->GetScriptGlobalObject(getter_AddRefs(global)); if (global) { nsCOMPtr context; global->GetContext(getter_AddRefs(context)); if (context) { JSObject* scriptObject; nsCOMPtr owner(do_QueryInterface(mBoundElement)); owner->GetScriptObject(context, (void**)&scriptObject); if (scriptObject) { // XXX Stay in sync! What if a layered binding has an ?! // XXX Sanity check to make sure our class name matches // Pull ourselves out of the proto chain. JSContext* jscontext = (JSContext*)context->GetNativeContext(); JSObject* ourProto = ::JS_GetPrototype(jscontext, scriptObject); JSObject* grandProto = ::JS_GetPrototype(jscontext, ourProto); ::JS_SetPrototype(jscontext, scriptObject, grandProto); } } } } } // Update the anonymous content. nsCOMPtr anonymous; GetAnonymousContent(getter_AddRefs(anonymous)); if (anonymous) { if (mIsStyleBinding) anonymous->SetDocument(nsnull, PR_TRUE, AllowScripts()); // Kill it. else anonymous->SetDocument(aNewDocument, PR_TRUE, AllowScripts()); // Keep it around. } } return NS_OK; } NS_IMETHODIMP nsXBLBinding::GetBindingURI(nsString& aResult) { return mBinding->GetAttribute(kNameSpaceID_None, kURIAtom, aResult); } // Internal helper methods //////////////////////////////////////////////////////////////// NS_IMETHODIMP nsXBLBinding::InitClass(const nsCString& aClassName, nsIScriptContext* aContext, nsIDocument* aDocument, void** aScriptObject, void** aClassObject) { *aClassObject = nsnull; *aScriptObject = nsnull; // Obtain the bound element's current script object. nsCOMPtr owner(do_QueryInterface(mBoundElement)); owner->GetScriptObject(aContext, aScriptObject); if (!(*aScriptObject)) return NS_ERROR_FAILURE; JSObject* object = (JSObject*)(*aScriptObject); // First ensure our JS class is initialized. JSContext* jscontext = (JSContext*)aContext->GetNativeContext(); JSObject* global = ::JS_GetGlobalObject(jscontext); jsval vp; JSObject* proto; if ((! ::JS_LookupProperty(jscontext, global, aClassName, &vp)) || JSVAL_IS_PRIMITIVE(vp)) { // We need to initialize the class. nsXBLJSClass* c; void* classObject; nsStringKey key(aClassName); classObject = (nsXBLService::gClassTable)->Get(&key); if (classObject) { c = NS_STATIC_CAST(nsXBLJSClass*, classObject); // If c is on the LRU list (i.e., not linked to itself), remove it now! JSCList* link = NS_STATIC_CAST(JSCList*, c); if (c->next != link) { JS_REMOVE_AND_INIT_LINK(link); nsXBLService::gClassLRUListLength--; } } else { if (JS_CLIST_IS_EMPTY(&nsXBLService::gClassLRUList)) { // We need to create a struct for this class. c = new nsXBLJSClass(aClassName); if (!c) return NS_ERROR_OUT_OF_MEMORY; } else { // Pull the least recently used class struct off the list. JSCList* lru = (nsXBLService::gClassLRUList).next; JS_REMOVE_AND_INIT_LINK(lru); nsXBLService::gClassLRUListLength--; // Remove any mapping from the old name to the class struct. c = NS_STATIC_CAST(nsXBLJSClass*, lru); nsStringKey oldKey(c->name); (nsXBLService::gClassTable)->Remove(&oldKey); // Change the class name and we're done. nsMemory::Free(c->name); c->name = nsXPIDLCString::Copy(aClassName); } // Add c to our table. (nsXBLService::gClassTable)->Put(&key, (void*)c); } // Retrieve the current prototype of the JS object. JSObject* parent_proto = ::JS_GetPrototype(jscontext, object); // Make a new object prototyped by parent_proto and parented by global. proto = ::JS_InitClass(jscontext, // context global, // global object parent_proto, // parent proto c, // JSClass NULL, // JSNative ctor 0, // ctor args nsnull, // proto props nsnull, // proto funcs nsnull, // ctor props (static) nsnull); // ctor funcs (static) if (!proto) { (nsXBLService::gClassTable)->Remove(&key); delete c; return NS_ERROR_OUT_OF_MEMORY; } // The prototype holds a strong reference to its class struct. c->Hold(); *aClassObject = (void*)proto; } else { proto = JSVAL_TO_OBJECT(vp); } // Set the prototype of our object to be the new class. ::JS_SetPrototype(jscontext, object, proto); return NS_OK; } void nsXBLBinding::GetImmediateChild(nsIAtom* aTag, nsIContent** aResult) { *aResult = nsnull; PRInt32 childCount; mBinding->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; mBinding->ChildAt(i, *getter_AddRefs(child)); nsCOMPtr tag; child->GetTag(*getter_AddRefs(tag)); if (aTag == tag.get()) { *aResult = child; NS_ADDREF(*aResult); return; } } return; } void nsXBLBinding::GetNestedChild(nsIAtom* aTag, nsIContent* aContent, nsIContent** aResult) { *aResult = nsnull; PRInt32 childCount; aContent->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; aContent->ChildAt(i, *getter_AddRefs(child)); nsCOMPtr tag; child->GetTag(*getter_AddRefs(tag)); if (aTag == tag.get()) { *aResult = aContent; // We return the parent of the correct child. NS_ADDREF(*aResult); return; } else { GetNestedChild(aTag, child, aResult); if (*aResult) return; } } } void nsXBLBinding::GetNestedChildren(nsIAtom* aTag, nsIContent* aContent, nsISupportsArray* aList) { PRInt32 childCount; aContent->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; aContent->ChildAt(i, *getter_AddRefs(child)); nsCOMPtr tag; child->GetTag(*getter_AddRefs(tag)); if (aTag == tag.get()) aList->AppendElement(child); else GetNestedChildren(aTag, child, aList); } } void nsXBLBinding::BuildInsertionTable() { if (!mInsertionPointTable) mInsertionPointTable = new nsSupportsHashtable; nsCOMPtr childrenElements; NS_NewISupportsArray(getter_AddRefs(childrenElements)); GetNestedChildren(kChildrenAtom, mContent, childrenElements); PRUint32 count; childrenElements->Count(&count); PRUint32 i; for (i = 0; i < count; i++) { nsCOMPtr supp; childrenElements->GetElementAt(i, getter_AddRefs(supp)); nsCOMPtr child(do_QueryInterface(supp)); if (child) { nsCOMPtr parent; child->GetParent(*getter_AddRefs(parent)); nsAutoString includes; child->GetAttribute(kNameSpaceID_None, kIncludesAtom, includes); if (includes.IsEmpty()) { nsISupportsKey key(kChildrenAtom); mInsertionPointTable->Put(&key, parent); } else { // The user specified at least one attribute. char* str = includes.ToNewCString(); char* newStr; // XXX We should use a strtok function that tokenizes PRUnichar's // so that we don't have to convert from Unicode to ASCII and then back char* token = nsCRT::strtok( str, ", ", &newStr ); while( token != NULL ) { // Build an atom out of this string. nsCOMPtr atom; nsAutoString tok; tok.AssignWithConversion(token); atom = getter_AddRefs(NS_NewAtom(tok.GetUnicode())); nsISupportsKey key(atom); mInsertionPointTable->Put(&key, parent); token = nsCRT::strtok( newStr, ", ", &newStr ); } nsMemory::Free(str); } } } // Now remove the elements. for (i = 0; i < count; i++) { nsCOMPtr supp; childrenElements->GetElementAt(i, getter_AddRefs(supp)); nsCOMPtr child(do_QueryInterface(supp)); if (child) { nsCOMPtr parent; child->GetParent(*getter_AddRefs(parent)); PRInt32 index; parent->IndexOf(child, index); parent->RemoveChildAt(index, PR_FALSE); } } } PRBool nsXBLBinding::IsInExcludesList(nsIAtom* aTag, const nsString& aList) { nsAutoString element; aTag->ToString(element); if (aList.EqualsWithConversion("*")) return PR_TRUE; // match _everything_! PRInt32 indx = aList.Find(element); if (indx == -1) return PR_FALSE; // not in the list at all // okay, now make sure it's not a substring snafu; e.g., 'ur' // found inside of 'blur'. if (indx > 0) { PRUnichar ch = aList[indx - 1]; if (! nsCRT::IsAsciiSpace(ch) && ch != PRUnichar(',')) return PR_FALSE; } if (indx + element.Length() < aList.Length()) { PRUnichar ch = aList[indx + element.Length()]; if (! nsCRT::IsAsciiSpace(ch) && ch != PRUnichar(',')) return PR_FALSE; } return PR_TRUE; } NS_IMETHODIMP nsXBLBinding::ConstructAttributeTable(nsIContent* aElement) { // XXX This function still needs to deal with the // ability to map one attribute to another. nsAutoString inherits; aElement->GetAttribute(kNameSpaceID_None, kInheritsAtom, inherits); if (!inherits.IsEmpty()) { if (!mAttributeTable) { mAttributeTable = new nsSupportsHashtable(4); } // The user specified at least one attribute. char* str = inherits.ToNewCString(); char* newStr; // XXX We should use a strtok function that tokenizes PRUnichar's // so that we don't have to convert from Unicode to ASCII and then back char* token = nsCRT::strtok( str, ", ", &newStr ); while( token != NULL ) { // Build an atom out of this attribute. nsCOMPtr atom; nsCOMPtr attribute; // Figure out if this token contains a :. nsAutoString attrTok; attrTok.AssignWithConversion(token); PRInt32 index = attrTok.Find(":", PR_TRUE); if (index != -1) { // This attribute maps to something different. nsAutoString left, right; attrTok.Left(left, index); attrTok.Right(right, attrTok.Length()-index-1); atom = getter_AddRefs(NS_NewAtom(left.GetUnicode())); attribute = getter_AddRefs(NS_NewAtom(right.GetUnicode())); } else { nsAutoString tok; tok.AssignWithConversion(token); atom = getter_AddRefs(NS_NewAtom(tok.GetUnicode())); attribute = atom; } // Create an XBL attribute entry. nsXBLAttributeEntry* xblAttr = new nsXBLAttributeEntry(attribute, aElement); // Now we should see if some element within our anonymous // content is already observing this attribute. nsISupportsKey key(atom); nsCOMPtr supports = getter_AddRefs(NS_STATIC_CAST(nsISupports*, mAttributeTable->Get(&key))); nsCOMPtr entry = do_QueryInterface(supports); if (!entry) { // Put it in the table. mAttributeTable->Put(&key, xblAttr); } else { nsCOMPtr attr = entry; nsCOMPtr tmpAttr = entry; do { attr = tmpAttr; attr->GetNext(getter_AddRefs(tmpAttr)); } while (tmpAttr); attr->SetNext(xblAttr); } // Now make sure that this attribute is initially set. // XXX How to deal with NAMESPACES!!!? nsAutoString value; nsresult result = mBoundElement->GetAttribute(kNameSpaceID_None, atom, value); PRBool attrPresent = (result == NS_CONTENT_ATTR_NO_VALUE || result == NS_CONTENT_ATTR_HAS_VALUE); if (attrPresent) { aElement->SetAttribute(kNameSpaceID_None, attribute, value, PR_FALSE); nsCOMPtr tag; aElement->GetTag(*getter_AddRefs(tag)); if ((tag.get() == kHTMLAtom) && (attribute.get() == kValueAtom) && !value.IsEmpty()) { nsCOMPtr textNode; nsCOMPtr doc; mBoundElement->GetDocument(*getter_AddRefs(doc)); nsCOMPtr domDoc(do_QueryInterface(doc)); domDoc->CreateTextNode(value, getter_AddRefs(textNode)); nsCOMPtr dummy; nsCOMPtr domElement(do_QueryInterface(aElement)); domElement->AppendChild(textNode, getter_AddRefs(dummy)); } } token = nsCRT::strtok( newStr, ", ", &newStr ); } nsMemory::Free(str); } // Recur into our children. PRInt32 childCount; aElement->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; aElement->ChildAt(i, *getter_AddRefs(child)); ConstructAttributeTable(child); } return NS_OK; } void nsXBLBinding::GetEventHandlerIID(nsIAtom* aName, nsIID* aIID, PRBool* aFound) { *aFound = PR_FALSE; EventHandlerMapEntry* entry = kEventHandlerMap; while (entry->mAttributeAtom) { if (entry->mAttributeAtom == aName) { *aIID = *entry->mHandlerIID; *aFound = PR_TRUE; break; } ++entry; } } PRBool nsXBLBinding::IsMouseHandler(const nsString& aName) { return ((aName.EqualsWithConversion("click")) || (aName.EqualsWithConversion("dblclick")) || (aName.EqualsWithConversion("mousedown")) || (aName.EqualsWithConversion("mouseover")) || (aName.EqualsWithConversion("mouseout")) || (aName.EqualsWithConversion("mouseup"))); } PRBool nsXBLBinding::IsKeyHandler(const nsString& aName) { return ((aName.EqualsWithConversion("keypress")) || (aName.EqualsWithConversion("keydown")) || (aName.EqualsWithConversion("keyup"))); } PRBool nsXBLBinding::IsFocusHandler(const nsString& aName) { return ((aName.EqualsWithConversion("focus")) || (aName.EqualsWithConversion("blur"))); } PRBool nsXBLBinding::IsXULHandler(const nsString& aName) { return ((aName.EqualsWithConversion("create")) || (aName.EqualsWithConversion("destroy")) || (aName.EqualsWithConversion("broadcast")) || (aName.EqualsWithConversion("command")) || (aName.EqualsWithConversion("commandupdate")) || (aName.EqualsWithConversion("close"))); } PRBool nsXBLBinding::IsScrollHandler(const nsString& aName) { return (aName.EqualsWithConversion("overflow") || aName.EqualsWithConversion("underflow") || aName.EqualsWithConversion("overflowchanged")); } NS_IMETHODIMP nsXBLBinding::AddScriptEventListener(nsIContent* aElement, nsIAtom* aName, const nsString& aValue, REFNSIID aIID) { nsAutoString val; aName->ToString(val); nsAutoString eventStr; eventStr.AssignWithConversion("on"); eventStr += val; nsCOMPtr eventName = getter_AddRefs(NS_NewAtom(eventStr)); nsresult rv; nsCOMPtr document; aElement->GetDocument(*getter_AddRefs(document)); if (!document) return NS_OK; nsCOMPtr receiver(do_QueryInterface(aElement)); if (!receiver) return NS_OK; nsCOMPtr global; document->GetScriptGlobalObject(getter_AddRefs(global)); // This can happen normally as part of teardown code. if (!global) return NS_OK; nsCOMPtr context; rv = global->GetContext(getter_AddRefs(context)); if (NS_FAILED(rv)) return rv; nsCOMPtr manager; rv = receiver->GetListenerManager(getter_AddRefs(manager)); if (NS_FAILED(rv)) return rv; nsCOMPtr scriptOwner(do_QueryInterface(receiver)); if (!scriptOwner) return NS_OK; rv = manager->AddScriptEventListener(context, scriptOwner, eventName, aValue, aIID, PR_FALSE); return rv; } nsresult nsXBLBinding::GetTextData(nsIContent *aParent, nsString& aResult) { aResult.Truncate(0); nsCOMPtr textChild; PRInt32 textCount; aParent->ChildCount(textCount); nsAutoString answer; for (PRInt32 j = 0; j < textCount; j++) { // Get the child. aParent->ChildAt(j, *getter_AddRefs(textChild)); nsCOMPtr text(do_QueryInterface(textChild)); if (text) { nsAutoString data; text->GetData(data); aResult += data; } } return NS_OK; } PRBool nsXBLBinding::AllowScripts() { nsresult rv; nsCOMPtr xblService(do_GetService("component://netscape/xbl", &rv)); if (xblService) { PRBool allowScripts; xblService->AllowScripts(mBinding, &allowScripts); return allowScripts; } return PR_FALSE; } NS_IMETHODIMP nsXBLBinding::GetInsertionPoint(nsIContent* aChild, nsIContent** aResult) { *aResult = nsnull; if (mInsertionPointTable) { nsCOMPtr tag; aChild->GetTag(*getter_AddRefs(tag)); nsISupportsKey key(tag); nsCOMPtr content = getter_AddRefs(NS_STATIC_CAST(nsIContent*, mInsertionPointTable->Get(&key))); if (!content) { nsISupportsKey key2(kChildrenAtom); content = getter_AddRefs(NS_STATIC_CAST(nsIContent*, mInsertionPointTable->Get(&key2))); } *aResult = content; NS_IF_ADDREF(*aResult); } return NS_OK; } NS_IMETHODIMP nsXBLBinding::GetSingleInsertionPoint(nsIContent** aResult, PRBool* aMultipleInsertionPoints) { *aResult = nsnull; *aMultipleInsertionPoints = PR_FALSE; if (mInsertionPointTable) { if(mInsertionPointTable->Count() == 1) { nsISupportsKey key(kChildrenAtom); nsCOMPtr content = getter_AddRefs(NS_STATIC_CAST(nsIContent*, mInsertionPointTable->Get(&key))); *aResult = content; NS_IF_ADDREF(*aResult); } else *aMultipleInsertionPoints = PR_TRUE; } return NS_OK; } NS_IMETHODIMP nsXBLBinding::GetRootBinding(nsIXBLBinding** aResult) { if (mNextBinding) return mNextBinding->GetRootBinding(aResult); *aResult = this; NS_ADDREF(this); return NS_OK; } NS_IMETHODIMP nsXBLBinding::GetFirstStyleBinding(nsIXBLBinding** aResult) { if (mIsStyleBinding) { *aResult = this; NS_ADDREF(this); return NS_OK; } else if (mNextBinding) return mNextBinding->GetFirstStyleBinding(aResult); *aResult = nsnull; return NS_OK; } // Creation Routine /////////////////////////////////////////////////////////////////////// nsresult NS_NewXBLBinding(nsIXBLBinding** aResult) { *aResult = new nsXBLBinding; if (!*aResult) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*aResult); return NS_OK; }