- Doc-comments for nsIScriptContext methods I added or modified.

- Rename nsIScriptContext::CompileFunction to CompileEventHandler, and add BindCompiledEventHandler, to reflect function name restrictions and help brutal sharing.
- Add adjunct-interface nsIScriptEventHandlerOwner to nsIScriptObjectOwner.h; this interface is queried for by nsEventListenerManger.cpp to test and fill the XUL prototype event handler "cache".
- PR_LOG JS warnings, and look in javascript.options.{strict,werror} for those options.  
- Implement nsIScriptEventHandlerOwner in nsXULElement, keeping pointers to pre-compiled event handlers in nsXULPrototypeAttribute.
 
(bug 13218, r=waterson@netscape.com,vidur@netscape.com)
This commit is contained in:
brendan%mozilla.org 1999-12-03 03:40:30 +00:00
Родитель f44c56f451
Коммит f3855ca6b0
10 изменённых файлов: 674 добавлений и 255 удалений

Просмотреть файл

@ -552,7 +552,7 @@ nsEventListenerManager::AddScriptEventListener(nsIScriptContext* aContext,
rv = aScriptObjectOwner->GetScriptObject(aContext, (void**)&scriptObject);
if (NS_FAILED(rv))
return rv;
rv = aContext->CompileFunction(scriptObject, aName, aBody);
rv = aContext->CompileEventHandler(scriptObject, aName, aBody, nsnull);
if (NS_FAILED(rv))
return rv;
}
@ -587,26 +587,41 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
if (NS_SUCCEEDED(result)) {
JSObject* jsobj;
result = owner->GetScriptObject(scriptCX, (void**)&jsobj);
// This should never happen for anything but content
// XXX I don't like that we have to reference content
// from here. The alternative is to store the event handler
// string on the JS object itself.
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIContent> content = do_QueryInterface(owner);
NS_ASSERTION(content, "only content should have event handler attributes");
if (content) {
nsAutoString eventString;
if (NS_SUCCEEDED(aDOMEvent->GetType(eventString))) {
nsAutoString eventString;
if (NS_SUCCEEDED(aDOMEvent->GetType(eventString))) {
eventString.Insert("on", 0, 2);
nsCOMPtr<nsIAtom> atom = getter_AddRefs(NS_NewAtom(eventString));
eventString.Insert("on", 0, 2);
nsCOMPtr<nsIAtom> atom = getter_AddRefs(NS_NewAtom(eventString));
nsAutoString handlerBody;
result = content->GetAttribute(kNameSpaceID_None, atom, handlerBody);
if (NS_SUCCEEDED(result)) {
result = scriptCX->CompileFunction(jsobj, atom, handlerBody);
nsCOMPtr<nsIScriptEventHandlerOwner> handlerOwner = do_QueryInterface(owner);
void* handler = nsnull;
if (handlerOwner) {
result = handlerOwner->GetCompiledEventHandler(atom, &handler);
if (NS_SUCCEEDED(result) && handler) {
result = scriptCX->BindCompiledEventHandler(jsobj, atom, handler);
aListenerStruct->mHandlerIsString &= ~aSubType;
}
}
if (aListenerStruct->mHandlerIsString & aSubType) {
// This should never happen for anything but content
// XXX I don't like that we have to reference content
// from here. The alternative is to store the event handler
// string on the JS object itself.
nsCOMPtr<nsIContent> content = do_QueryInterface(owner);
NS_ASSERTION(content, "only content should have event handler attributes");
if (content) {
nsAutoString handlerBody;
result = content->GetAttribute(kNameSpaceID_None, atom, handlerBody);
if (NS_SUCCEEDED(result)) {
result = scriptCX->CompileEventHandler(jsobj, atom, handlerBody, &handler);
aListenerStruct->mHandlerIsString &= ~aSubType;
if (handlerOwner)
handlerOwner->SetCompiledEventHandler(atom, handler);
}
}
}
}
}
}

Просмотреть файл

@ -124,14 +124,14 @@ static NS_DEFINE_IID(kIScriptObjectOwnerIID, NS_ISCRIPTOBJECTOWNER_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIXMLContentIID, NS_IXMLCONTENT_IID);
static NS_DEFINE_CID(kEventListenerManagerCID, NS_EVENTLISTENERMANAGER_CID);
static NS_DEFINE_CID(kEventListenerManagerCID, NS_EVENTLISTENERMANAGER_CID);
static NS_DEFINE_IID(kIDOMEventTargetIID, NS_IDOMEVENTTARGET_IID);
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kXULContentUtilsCID, NS_XULCONTENTUTILS_CID);
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kXULContentUtilsCID, NS_XULCONTENTUTILS_CID);
static NS_DEFINE_IID(kIXULPopupListenerIID, NS_IXULPOPUPLISTENER_IID);
static NS_DEFINE_CID(kXULPopupListenerCID, NS_XULPOPUPLISTENER_CID);
static NS_DEFINE_IID(kIXULPopupListenerIID, NS_IXULPOPUPLISTENER_IID);
static NS_DEFINE_CID(kXULPopupListenerCID, NS_XULPOPUPLISTENER_CID);
static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
@ -247,6 +247,13 @@ nsIAtom* nsXULElement::kTreeRowAtom;
nsIAtom* nsXULElement::kEditorAtom;
nsIAtom* nsXULElement::kWindowAtom;
nsIAtom* nsXULElement::kNullAtom;
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
PRUint32 nsXULPrototypeAttribute::gNumElements;
PRUint32 nsXULPrototypeAttribute::gNumAttributes;
PRUint32 nsXULPrototypeAttribute::gNumEventHandlers;
#endif
//----------------------------------------------------------------------
// nsXULElement
@ -261,6 +268,9 @@ nsXULElement::nsXULElement()
mSlots(nsnull)
{
NS_INIT_REFCNT();
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
nsXULPrototypeAttribute::gNumElements++;
#endif
}
@ -294,7 +304,7 @@ nsXULElement::Init()
kTreeRowAtom = NS_NewAtom("treerow");
kEditorAtom = NS_NewAtom("editor");
kWindowAtom = NS_NewAtom("window");
kNullAtom = NS_NewAtom("");
kNullAtom = NS_NewAtom("");
rv = nsComponentManager::CreateInstance(kNameSpaceManagerCID,
nsnull,
@ -365,7 +375,7 @@ nsXULElement::~nsXULElement()
NS_IF_RELEASE(kTreeRowAtom);
NS_IF_RELEASE(kEditorAtom);
NS_IF_RELEASE(kWindowAtom);
NS_IF_RELEASE(kNullAtom);
NS_IF_RELEASE(kNullAtom);
NS_IF_RELEASE(gNameSpaceManager);
@ -426,6 +436,9 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype,
if (NS_FAILED(rv)) return rv;
if (found) {
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
nsXULPrototypeAttribute::gNumEventHandlers++;
#endif
rv = element->AddScriptEventListener(attr->mName, attr->mValue, iid);
if (NS_FAILED(rv)) return rv;
}
@ -510,6 +523,9 @@ nsXULElement::QueryInterface(REFNSIID iid, void** result)
else if (iid.Equals(kIScriptObjectOwnerIID)) {
*result = NS_STATIC_CAST(nsIScriptObjectOwner*, this);
}
else if (iid.Equals(NS_GET_IID(nsIScriptEventHandlerOwner))) {
*result = NS_STATIC_CAST(nsIScriptEventHandlerOwner*, this);
}
else if (iid.Equals(kIDOMEventReceiverIID)) {
*result = NS_STATIC_CAST(nsIDOMEventReceiver*, this);
}
@ -1483,6 +1499,45 @@ nsXULElement::SetScriptObject(void *aScriptObject)
}
//----------------------------------------------------------------------
// nsIScriptEventHandlerOwner interface
NS_IMETHODIMP
nsXULElement::GetCompiledEventHandler(nsIAtom *aName, void** aHandler)
{
*aHandler = nsnull;
if (mPrototype) {
for (PRInt32 i = 0; i < mPrototype->mNumAttributes; ++i) {
nsXULPrototypeAttribute* attr = &(mPrototype->mAttributes[i]);
if ((attr->mNameSpaceID == kNameSpaceID_None) &&
(attr->mName == aName)) {
*aHandler = attr->mEventHandler;
break;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXULElement::SetCompiledEventHandler(nsIAtom *aName, void* aHandler)
{
if (mPrototype) {
for (PRInt32 i = 0; i < mPrototype->mNumAttributes; ++i) {
nsXULPrototypeAttribute* attr = &(mPrototype->mAttributes[i]);
if ((attr->mNameSpaceID == kNameSpaceID_None) &&
(attr->mName == aName)) {
attr->mEventHandler = aHandler;
break;
}
}
}
return NS_OK;
}
//----------------------------------------------------------------------
// nsIJSScriptObject interface
@ -2136,8 +2191,8 @@ nsXULElement::SetAttribute(PRInt32 aNameSpaceID,
NS_IMETHODIMP
nsXULElement::GetAttribute(PRInt32 aNameSpaceID,
nsIAtom* aName,
nsString& aResult) const
nsIAtom* aName,
nsString& aResult) const
{
NS_ASSERTION(nsnull != aName, "must have attribute name");
if (nsnull == aName) {
@ -2199,18 +2254,21 @@ nsXULElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotif
if (! Attributes())
return NS_OK;
// Check to see if the CLASS attribute is being unset. If so, we need to delete our
// class list.
if (mDocument && (aNameSpaceID == kNameSpaceID_None) && (aName == kClassAtom)) {
// Check to see if the CLASS attribute is being unset. If so, we need to
// delete our class list.
// XXXbe fuse common (mDocument && aNameSpaceId == kNameSpaceID_None)
if (mDocument &&
(aNameSpaceID == kNameSpaceID_None) &&
(aName == kClassAtom)) {
Attributes()->UpdateClassList("");
}
if (mDocument && (aNameSpaceID == kNameSpaceID_None) && aName == kStyleAtom) {
if (mDocument &&
(aNameSpaceID == kNameSpaceID_None) &&
aName == kStyleAtom) {
nsCOMPtr <nsIURI> docURL;
if (nsnull != mDocument) {
mDocument->GetBaseURL(*getter_AddRefs(docURL));
}
mDocument->GetBaseURL(*getter_AddRefs(docURL));
Attributes()->UpdateStyleRule(docURL, "");
// XXX Some kind of special document update might need to happen here.
@ -2222,34 +2280,34 @@ nsXULElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotif
nsCOMPtr<nsIAtom> tag;
GetTag(*getter_AddRefs(tag));
if (aNameSpaceID == kNameSpaceID_None) {
// See if we're a treeitem atom.
// XXX Forgive me father, for I know exactly what I do, and I'm
// doing it anyway. Need to make an nsIRDFNodeList interface that
// I can QI to for additions and removals of nodes. For now
// do an evil cast.
nsCOMPtr<nsIRDFNodeList> nodeList;
if (tag && (tag.get() == kTreeItemAtom) && (aName == kSelectedAtom)) {
nsCOMPtr<nsIDOMXULTreeElement> treeElement;
GetParentTree(getter_AddRefs(treeElement));
if (treeElement) {
nsCOMPtr<nsIDOMNodeList> nodes;
treeElement->GetSelectedItems(getter_AddRefs(nodes));
nodeList = do_QueryInterface(nodes);
// See if we're a treeitem atom.
// XXX Forgive me father, for I know exactly what I do, and I'm
// doing it anyway. Need to make an nsIRDFNodeList interface that
// I can QI to for additions and removals of nodes. For now
// do an evil cast.
nsCOMPtr<nsIRDFNodeList> nodeList;
if (tag && (tag.get() == kTreeItemAtom) && (aName == kSelectedAtom)) {
nsCOMPtr<nsIDOMXULTreeElement> treeElement;
GetParentTree(getter_AddRefs(treeElement));
if (treeElement) {
nsCOMPtr<nsIDOMNodeList> nodes;
treeElement->GetSelectedItems(getter_AddRefs(nodes));
nodeList = do_QueryInterface(nodes);
}
}
}
else if (tag && (tag.get() == kTreeCellAtom) && (aName == kSelectedAtom)) {
nsCOMPtr<nsIDOMXULTreeElement> treeElement;
GetParentTree(getter_AddRefs(treeElement));
if (treeElement) {
nsCOMPtr<nsIDOMNodeList> nodes;
treeElement->GetSelectedCells(getter_AddRefs(nodes));
nodeList = do_QueryInterface(nodes);
else if (tag && (tag.get() == kTreeCellAtom) && (aName == kSelectedAtom)) {
nsCOMPtr<nsIDOMXULTreeElement> treeElement;
GetParentTree(getter_AddRefs(treeElement));
if (treeElement) {
nsCOMPtr<nsIDOMNodeList> nodes;
treeElement->GetSelectedCells(getter_AddRefs(nodes));
nodeList = do_QueryInterface(nodes);
}
}
if (nodeList) {
// Remove this node from the list.
nodeList->RemoveNode(this);
}
}
if (nodeList) {
// Remove this node from the list.
nodeList->RemoveNode(this);
}
}
// XXX Know how to remove POPUP event listeners when an attribute is unset?
@ -2263,7 +2321,8 @@ nsXULElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotif
PRInt32 i;
for (i = 0; i < count; i++) {
nsXULAttribute* attr = NS_REINTERPRET_CAST(nsXULAttribute*, Attributes()->ElementAt(i));
if ((attr->GetNameSpaceID() == aNameSpaceID) && (attr->GetName() == aName)) {
if ((attr->GetNameSpaceID() == aNameSpaceID) &&
(attr->GetName() == aName)) {
attr->GetValue(oldValue);
Attributes()->RemoveElementAt(i);
NS_RELEASE(attr);
@ -2275,50 +2334,50 @@ nsXULElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotif
// XUL Only. Find out if we have a broadcast listener for this element.
if (successful) {
// Check to see if the OBSERVES attribute is being unset. If so, we need to remove
// ourselves completely.
if (mDocument && (aNameSpaceID == kNameSpaceID_None) &&
(aName == kObservesAtom))
{
// Do a getElementById to retrieve the broadcaster.
nsCOMPtr<nsIDOMElement> broadcaster;
nsCOMPtr<nsIDOMXULDocument> domDoc = do_QueryInterface(mDocument);
domDoc->GetElementById(oldValue, getter_AddRefs(broadcaster));
if (broadcaster) {
nsCOMPtr<nsIDOMXULElement> xulBroadcaster = do_QueryInterface(broadcaster);
if (xulBroadcaster) {
xulBroadcaster->RemoveBroadcastListener("*", this);
}
}
}
if (BroadcastListeners()) {
PRInt32 count = BroadcastListeners()->Count();
for (PRInt32 i = 0; i < count; i++)
// Check to see if the OBSERVES attribute is being unset. If so, we
// need to remove ourselves completely.
if (mDocument &&
(aNameSpaceID == kNameSpaceID_None) &&
(aName == kObservesAtom))
{
XULBroadcastListener* xulListener =
NS_REINTERPRET_CAST(XULBroadcastListener*, BroadcastListeners()->ElementAt(i));
nsAutoString str;
aName->ToString(str);
if (xulListener->ObservingAttribute(str) &&
(aName != kIdAtom)) {
// XXX Should have a function that knows which attributes are special.
// Unset the attribute in the broadcast listener.
nsCOMPtr<nsIDOMElement> element;
element = do_QueryInterface(xulListener->mListener);
if (element)
element->RemoveAttribute(str);
// Do a getElementById to retrieve the broadcaster.
nsCOMPtr<nsIDOMElement> broadcaster;
nsCOMPtr<nsIDOMXULDocument> domDoc = do_QueryInterface(mDocument);
domDoc->GetElementById(oldValue, getter_AddRefs(broadcaster));
if (broadcaster) {
nsCOMPtr<nsIDOMXULElement> xulBroadcaster = do_QueryInterface(broadcaster);
if (xulBroadcaster) {
xulBroadcaster->RemoveBroadcastListener("*", this);
}
}
}
}
// Notify document
if (NS_SUCCEEDED(rv) && aNotify && (nsnull != mDocument)) {
mDocument->AttributeChanged(NS_STATIC_CAST(nsIStyledContent*, this),
aNameSpaceID, aName,
NS_STYLE_HINT_UNKNOWN);
}
if (BroadcastListeners()) {
PRInt32 count = BroadcastListeners()->Count();
for (PRInt32 i = 0; i < count; i++) {
XULBroadcastListener* xulListener =
NS_REINTERPRET_CAST(XULBroadcastListener*, BroadcastListeners()->ElementAt(i));
nsAutoString str;
aName->ToString(str);
if (xulListener->ObservingAttribute(str) &&
(aName != kIdAtom)) {
// XXX Should have a function that knows which attributes are special.
// Unset the attribute in the broadcast listener.
nsCOMPtr<nsIDOMElement> element;
element = do_QueryInterface(xulListener->mListener);
if (element)
element->RemoveAttribute(str);
}
}
}
// Notify document
if (NS_SUCCEEDED(rv) && aNotify && (nsnull != mDocument)) {
mDocument->AttributeChanged(NS_STATIC_CAST(nsIStyledContent*, this),
aNameSpaceID, aName,
NS_STYLE_HINT_UNKNOWN);
}
}
// End XUL Only Code
@ -3094,11 +3153,11 @@ nsXULElement::GetID(nsIAtom*& aResult) const
GetAttribute(kNameSpaceID_None, kIdAtom, value);
if (value.Length() > 0)
aResult = NS_NewAtom(value); // The NewAtom call does the AddRef.
aResult = NS_NewAtom(value); // The NewAtom call does the AddRef.
else
{
aResult = kNullAtom;
NS_ADDREF(kNullAtom);
aResult = kNullAtom;
NS_ADDREF(kNullAtom);
}
return NS_OK;
}

Просмотреть файл

@ -78,11 +78,63 @@ class nsXULAttributes;
class nsXULPrototypeAttribute
{
public:
nsXULPrototypeAttribute() : mNameSpaceID(kNameSpaceID_Unknown) {}
nsXULPrototypeAttribute()
: mNameSpaceID(kNameSpaceID_Unknown),
mEventHandler(nsnull)
{
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
gNumAttributes++;
#endif
}
PRInt32 mNameSpaceID;
nsCOMPtr<nsIAtom> mName;
nsString mValue;
void* mEventHandler;
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
/**
If enough attributes, on average, are event handlers, it pays to keep
mEventHandler here, instead of maintaining a separate mapping in each
nsXULElement associating certain mName values to their mEventHandlers.
Assume we don't need to keep mNameSpaceID along with mName in such an
event-handler-only name-to-function-pointer mapping.
Let
minAttrSize = sizeof(mNameSpaceID) + sizeof(mName) + sizeof(mValue)
mappingSize = sizeof(mName) + sizeof(mEventHandler)
elemOverhead = nElems * sizeof(void*)
Then
nAttrs * minAttrSize + nEventHandlers * mappingSize + elemOverhead
> nAttrs * (minAttrSize + mappingSize - sizeof(mName))
which simplifies to
nEventHandlers * mappingSize + elemOverhead
> nAttrs * (mappingSize - sizeof(mName))
or
nEventHandlers + (nElems * sizeof(void*)) / mappingSize
> nAttrs * (1 - sizeof(mName) / mappingSize)
If nsCOMPtr and all other pointers are the same size, this reduces to
nEventHandlers + nElems / 2 > nAttrs / 2
To measure how many attributes are event handlers, compile XUL source
with XUL_PROTOTYPE_ATTRIBUTE_METERING and watch the counters below.
Plug into the above relation -- if true, it pays to put mEventHandler
in nsXULPrototypeAttribute rather than to keep a separate mapping.
Recent numbers after opening four browser windows:
nElems 3537, nAttrs 2528, nEventHandlers 1042
giving 1042 + 3537/2 > 2528/2 or 2810 > 1264.
As it happens, mEventHandler also makes this struct power-of-2 sized,
8 words on most architectures, which makes for strength-reduced array
index-to-pointer calculations.
*/
static PRUint32 gNumElements;
static PRUint32 gNumAttributes;
static PRUint32 gNumEventHandlers;
#endif
};
@ -249,6 +301,7 @@ class nsXULElement : public nsIStyledContent,
public nsIDOMXULElement,
public nsIDOMEventReceiver,
public nsIScriptObjectOwner,
public nsIScriptEventHandlerOwner,
public nsIJSScriptObject,
public nsIStyleRule,
public nsIChromeEventHandler
@ -389,6 +442,10 @@ public:
NS_IMETHOD GetScriptObject(nsIScriptContext* aContext, void** aScriptObject);
NS_IMETHOD SetScriptObject(void *aScriptObject);
// nsIScriptEventHandlerOwner
NS_IMETHOD GetCompiledEventHandler(nsIAtom *aName, void** aHandler);
NS_IMETHOD SetCompiledEventHandler(nsIAtom *aName, void* aHandler);
// nsIJSScriptObject
virtual PRBool AddProperty(JSContext *aContext, jsval aID, jsval *aVp);
virtual PRBool DeleteProperty(JSContext *aContext, jsval aID, jsval *aVp);

Просмотреть файл

@ -17,7 +17,7 @@
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Contributor(s):
*/
#ifndef nsIScriptContext_h__
@ -70,7 +70,7 @@ public:
* @param aLineNo the starting line number for the script for error messages
* @param aVersion the script language version to use when executing
* @param aRetValue the result of executing the script
* @param aIsUndefined true if the result of executing the script is the
* @param aIsUndefined true if the result of executing the script is the
* undefined value
*
* @return NS_OK if the script was valid and got executed
@ -120,24 +120,66 @@ public:
* or nsnull to use a default scope
* @param aRetValue the result of executing the script, may be null in
* which case no result string is computed
* @param aIsUndefined true if the result of executing the script is the
* @param aIsUndefined true if the result of executing the script is the
* undefined value, may be null for "don't care"
*
* @return NS_OK if the script was valid and got executed
*
*/
*/
NS_IMETHOD ExecuteScript(void* aScriptObject,
void *aScopeObject,
nsString* aRetValue,
PRBool* aIsUndefined) = 0;
NS_IMETHOD CompileFunction(void *aObj, nsIAtom *aName,
const nsString& aBody) = 0;
/**
* Compile the event handler named by atom aName, with function body aBody
* into a function returned on success via *aFunction. Bind the lowercase
* ASCII name to the function in scope aObj, or in the context's global if
* aObj is null.
*
* @param aObj an object telling the scope in which to bind the compiled
* event handler function, or nsnull to use a default scope
* @param aName an nsIAtom pointer naming the function; it must be lowercase
* and ASCII, and should not be longer than 63 chars. This bound on
* length is enforced only by assertions, so caveat caller!
* @param aBody the event handler function's body
* @param aFunction the out parameter in which a void pointer to the compiled
* function is returned on success; may be null, meaning "don't care"
*
* @return NS_OK if the function body was valid and got compiled
*/
NS_IMETHOD CompileEventHandler(void *aObj,
nsIAtom *aName,
const nsString& aBody,
void** aFunction) = 0;
NS_IMETHOD CallFunction(void *aObj, void *aFunction,
PRUint32 argc, void *argv,
NS_IMETHOD CallFunction(void *aObj, void *aFunction,
PRUint32 argc, void *argv,
PRBool *aBoolResult) = 0;
/**
* Bind an already-compiled event handler function to a name in the given
* scope object. The same restrictions on aName (lowercase ASCII, not too
* long) applies here as for CompileEventHandler.
*
* @param aObj an object telling the scope in which to bind the compiled
* event handler function
* @param aName an nsIAtom pointer naming the function; it must be lowercase
* and ASCII, and should not be longer than 63 chars. This bound on
* length is enforced only by assertions, so caveat caller!
* @param aFunction the function to name, created by an earlier call to
* CompileEventHandler
* @return NS_OK if the function was successfully bound to the name
*/
NS_IMETHOD BindCompiledEventHandler(void *aObj,
nsIAtom *aName,
void *aFunction) = 0;
/**
* Set the default scripting language version for this context, which must
* be a context specific to a particular scripting language.
*
**/
NS_IMETHOD SetDefaultLanguageVersion(const char* aVersion) = 0;
/**
@ -172,7 +214,7 @@ public:
* Check to see if context is as yet intialized. Used to prevent
* reentrancy issues during the initialization process.
*
* @return NS_OK if initialized, NS_COMFALSE if not
* @return NS_OK if initialized, NS_COMFALSE if not
*
*/
NS_IMETHOD IsContextInitialized() = 0;
@ -181,7 +223,7 @@ public:
* Add a reference to a script object. For garbage collected systems
* the address of a slot to be used as a root is also provided. For
* reference counted systems, the object is provided.
*
*
* @param aSlot Slot to use as a root for garbage collected systems
* @param aScriptObject Script object being referenced
* @param aName Name of the reference (could be null)
@ -192,7 +234,7 @@ public:
const char *aName) = 0;
/**
* Remove a reference to a script object. For garbage collected
* Remove a reference to a script object. For garbage collected
* systems, this is equivalent to removing a root.
*
* @param aSlot Slot corresponding to the removed root
@ -209,7 +251,7 @@ public:
* @return NS_OK if the method is successful
*/
NS_IMETHOD GC() = 0;
/**
* Get the name space manager for this context.
* @return NS_OK if the method is successful

Просмотреть файл

@ -72,4 +72,27 @@ public:
NS_IMETHOD SetScriptObject(void* aScriptObject) = 0;
};
class nsIAtom;
#define NS_ISCRIPTEVENTHANDLEROWNER_IID \
{ /* 2ad54ae0-a839-11d3-ba97-00104ba02d3d */ \
0x2ad54ae0, 0xa839, 0x11d3, \
{0xba, 0x97, 0x00, 0x10, 0x4b, 0xa0, 0x2d, 0x3d} }
/**
* Associate a compiled event handler with its target object, which owns it
* This is an adjunct to nsIScriptObjectOwner that nsIEventListenerManager's
* implementation queries for, in order to avoid recompiling a recurrent or
* prototype-inherited event handler.
*/
class nsIScriptEventHandlerOwner : public nsISupports
{
public:
NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISCRIPTEVENTHANDLEROWNER_IID)
NS_IMETHOD GetCompiledEventHandler(nsIAtom *aName, void** aHandler) = 0;
NS_IMETHOD SetCompiledEventHandler(nsIAtom *aName, void* aHandler) = 0;
};
#endif // nsIScriptObjectOwner_h__

Просмотреть файл

@ -20,7 +20,9 @@
* Contributor(s):
*/
#ifdef NS_DEBUG
#include <ctype.h>
#endif
#include "nsJSEnvironment.h"
#include "nsIScriptObjectOwner.h"
#include "nsIScriptContextOwner.h"
@ -46,14 +48,24 @@
#include "nsIXPCSecurityManager.h"
#include "nsIJSContextStack.h"
#include "nsIJSRuntimeService.h"
#include "nsIPref.h"
#include "nsCOMPtr.h"
// Force PR_LOGGING so we can get JS strict warnings even in release builds
#define FORCE_PR_LOG 1
#include "prlog.h"
#include "nsIJVMManager.h"
#include "nsILiveConnectManager.h"
const size_t gStackSize = 8192;
static NS_DEFINE_IID(kCScriptNameSetRegistryCID, NS_SCRIPT_NAMESET_REGISTRY_CID);
static NS_DEFINE_IID(kPrefServiceCID, NS_PREF_CID);
#ifdef PR_LOGGING
static PRLogModuleInfo* gJSDiagnostics = nsnull;
#endif
void PR_CALLBACK
NS_ScriptErrorReporter(JSContext *cx,
@ -86,6 +98,25 @@ NS_ScriptErrorReporter(JSContext *cx,
}
}
#ifdef PR_LOGGING
if (report) {
if (!gJSDiagnostics)
gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
if (gJSDiagnostics) {
PR_LOG(gJSDiagnostics,
JSREPORT_IS_WARNING(report->flags) ? PR_LOG_WARNING : PR_LOG_ERROR,
("file %s, line %u: %s\n%s%s",
report->filename, report->lineno, message,
report->linebuf ? report->linebuf : "",
(report->linebuf &&
report->linebuf[strlen(report->linebuf)-1] != '\n')
? "\n"
: ""));
}
}
#endif
JS_ClearPendingException(cx);
}
@ -93,8 +124,33 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime)
{
mRefCnt = 0;
mContext = JS_NewContext(aRuntime, gStackSize);
if (mContext)
if (mContext) {
JS_SetContextPrivate(mContext, (void *)this);
// Check for the JS strict option, which enables extra error checks
nsresult rv;
PRBool strict, werror;
NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &rv);
if (NS_SUCCEEDED(rv)) {
uint32 options = 0;
#ifdef JSOPTION_STRICT
if (NS_SUCCEEDED(prefs->GetBoolPref("javascript.options.strict",
&strict)) &&
strict) {
options |= JSOPTION_STRICT;
}
#endif
#ifdef JSOPTION_WERROR
if (NS_SUCCEEDED(prefs->GetBoolPref("javascript.options.werror",
&werror)) &&
werror) {
options |= JSOPTION_WERROR;
}
#endif
if (options)
JS_SetOptions(mContext, options);
}
}
mIsInitialized = PR_FALSE;
mNumEvaluations = 0;
mSecurityManager = nsnull;
@ -354,10 +410,10 @@ nsJSContext::ExecuteScript(void* aScriptObject,
mRef = nsnull;
mTerminationFunc = nsnull;
ok = ::JS_ExecuteScript(mContext,
(JSObject*) aScopeObject,
(JSScript*) JS_GetPrivate(mContext,
(JSObject*)aScriptObject),
&val);
(JSObject*) aScopeObject,
(JSScript*) JS_GetPrivate(mContext,
(JSObject*)aScriptObject),
&val);
if (ok) {
// If all went well, convert val to a string (XXXbe unless undefined?).
@ -388,8 +444,32 @@ nsJSContext::ExecuteScript(void* aScriptObject,
const char *gEventArgv[] = {"event"};
void
AtomToEventHandlerName(nsIAtom *aName, char *charName, PRUint32 charNameSize)
{
// optimized to avoid ns*Str*.h explicit/implicit copying and malloc'ing
// even nsCAutoString may call an Append that copy-constructs an nsStr from
// a const PRUnichar*
const PRUnichar *name;
aName->GetUnicode(&name);
char c;
PRUint32 i = 0;
do {
NS_ASSERTION(name[i] < 128, "non-ASCII event handler name");
c = char(name[i]);
// The HTML content sink must have folded to lowercase already.
NS_ASSERTION(c == '\0' || islower(c), "non-alphabetic event handler name");
NS_ASSERTION(i < charNameSize, "overlong event handler name");
charName[i++] = c;
} while (c != '\0');
}
NS_IMETHODIMP
nsJSContext::CompileFunction(void *aObj, nsIAtom *aName, const nsString& aBody)
nsJSContext::CompileEventHandler(void *aObj, nsIAtom *aName, const nsString& aBody,
void** aFunction)
{
JSPrincipals *jsprin = nsnull;
@ -405,35 +485,24 @@ nsJSContext::CompileFunction(void *aObj, nsIAtom *aName, const nsString& aBody)
}
}
// optimized to avoid ns*Str*.h explicit/implicit copying and malloc'ing
// even nsCAutoString may call an Append that copy-constructs an nsStr from
// a const PRUnichar*
const PRUnichar *name;
aName->GetUnicode(&name);
char c, charName[64];
int i = 0;
char charName[64];
AtomToEventHandlerName(aName, charName, sizeof charName);
do {
NS_ASSERTION(name[i] < 128, "non-ASCII DOM function name");
c = char(name[i]);
NS_ASSERTION(c == '\0' || isalpha(c), "non-alphabetic DOM function name");
NS_ASSERTION(i < sizeof charName, "overlong DOM function name");
charName[i++] = tolower(c);
} while (c != '\0');
JSBool ok = JS_CompileUCFunctionForPrincipals(mContext,
(JSObject*)aObj, jsprin,
charName, 1, gEventArgv,
(jschar*)aBody.GetUnicode(),
aBody.Length(),
//XXXbe filename, lineno:
nsnull, 0)
!= 0;
JSFunction* fun = JS_CompileUCFunctionForPrincipals(mContext,
(JSObject*)aObj, jsprin,
charName, 1, gEventArgv,
(jschar*)aBody.GetUnicode(),
aBody.Length(),
//XXXbe filename, lineno:
nsnull, 0);
if (jsprin)
JSPRINCIPALS_DROP(mContext, jsprin);
return ok ? NS_OK : NS_ERROR_FAILURE;
if (!fun)
return NS_ERROR_FAILURE;
if (aFunction)
*aFunction = (void*) fun;
return NS_OK;
}
NS_IMETHODIMP
@ -477,6 +546,24 @@ nsJSContext::CallFunction(void *aObj, void *aFunction, PRUint32 argc,
return NS_OK;
}
NS_IMETHODIMP
nsJSContext::BindCompiledEventHandler(void *aObj, nsIAtom *aName, void *aFunction)
{
char charName[64];
AtomToEventHandlerName(aName, charName, sizeof charName);
JSObject *funobj = JS_GetFunctionObject((JSFunction *)aFunction);
if (!funobj)
return NS_ERROR_UNEXPECTED;
if (!::JS_DefineProperty(mContext, (JSObject *)aObj, charName,
OBJECT_TO_JSVAL(funobj), nsnull, nsnull,
JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsJSContext::SetDefaultLanguageVersion(const char* aVersion)
{

Просмотреть файл

@ -79,11 +79,16 @@ public:
void *aScopeObject,
nsString* aRetValue,
PRBool* aIsUndefined);
NS_IMETHOD CompileFunction(void *aObj, nsIAtom *aName,
const nsString& aBody);
NS_IMETHOD CompileEventHandler(void *aObj,
nsIAtom *aName,
const nsString& aBody,
void** aFunction);
NS_IMETHOD CallFunction(void *aObj, void *aFunction,
PRUint32 argc, void *argv,
PRBool *aBoolResult);
NS_IMETHOD BindCompiledEventHandler(void *aObj,
nsIAtom *aName,
void *aFunction);
NS_IMETHOD SetDefaultLanguageVersion(const char* aVersion);
NS_IMETHOD_(nsIScriptGlobalObject*) GetGlobalObject();
NS_IMETHOD_(void*) GetNativeContext();

Просмотреть файл

@ -552,7 +552,7 @@ nsEventListenerManager::AddScriptEventListener(nsIScriptContext* aContext,
rv = aScriptObjectOwner->GetScriptObject(aContext, (void**)&scriptObject);
if (NS_FAILED(rv))
return rv;
rv = aContext->CompileFunction(scriptObject, aName, aBody);
rv = aContext->CompileEventHandler(scriptObject, aName, aBody, nsnull);
if (NS_FAILED(rv))
return rv;
}
@ -587,26 +587,41 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
if (NS_SUCCEEDED(result)) {
JSObject* jsobj;
result = owner->GetScriptObject(scriptCX, (void**)&jsobj);
// This should never happen for anything but content
// XXX I don't like that we have to reference content
// from here. The alternative is to store the event handler
// string on the JS object itself.
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIContent> content = do_QueryInterface(owner);
NS_ASSERTION(content, "only content should have event handler attributes");
if (content) {
nsAutoString eventString;
if (NS_SUCCEEDED(aDOMEvent->GetType(eventString))) {
nsAutoString eventString;
if (NS_SUCCEEDED(aDOMEvent->GetType(eventString))) {
eventString.Insert("on", 0, 2);
nsCOMPtr<nsIAtom> atom = getter_AddRefs(NS_NewAtom(eventString));
eventString.Insert("on", 0, 2);
nsCOMPtr<nsIAtom> atom = getter_AddRefs(NS_NewAtom(eventString));
nsAutoString handlerBody;
result = content->GetAttribute(kNameSpaceID_None, atom, handlerBody);
if (NS_SUCCEEDED(result)) {
result = scriptCX->CompileFunction(jsobj, atom, handlerBody);
nsCOMPtr<nsIScriptEventHandlerOwner> handlerOwner = do_QueryInterface(owner);
void* handler = nsnull;
if (handlerOwner) {
result = handlerOwner->GetCompiledEventHandler(atom, &handler);
if (NS_SUCCEEDED(result) && handler) {
result = scriptCX->BindCompiledEventHandler(jsobj, atom, handler);
aListenerStruct->mHandlerIsString &= ~aSubType;
}
}
if (aListenerStruct->mHandlerIsString & aSubType) {
// This should never happen for anything but content
// XXX I don't like that we have to reference content
// from here. The alternative is to store the event handler
// string on the JS object itself.
nsCOMPtr<nsIContent> content = do_QueryInterface(owner);
NS_ASSERTION(content, "only content should have event handler attributes");
if (content) {
nsAutoString handlerBody;
result = content->GetAttribute(kNameSpaceID_None, atom, handlerBody);
if (NS_SUCCEEDED(result)) {
result = scriptCX->CompileEventHandler(jsobj, atom, handlerBody, &handler);
aListenerStruct->mHandlerIsString &= ~aSubType;
if (handlerOwner)
handlerOwner->SetCompiledEventHandler(atom, handler);
}
}
}
}
}
}

Просмотреть файл

@ -124,14 +124,14 @@ static NS_DEFINE_IID(kIScriptObjectOwnerIID, NS_ISCRIPTOBJECTOWNER_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIXMLContentIID, NS_IXMLCONTENT_IID);
static NS_DEFINE_CID(kEventListenerManagerCID, NS_EVENTLISTENERMANAGER_CID);
static NS_DEFINE_CID(kEventListenerManagerCID, NS_EVENTLISTENERMANAGER_CID);
static NS_DEFINE_IID(kIDOMEventTargetIID, NS_IDOMEVENTTARGET_IID);
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kXULContentUtilsCID, NS_XULCONTENTUTILS_CID);
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kXULContentUtilsCID, NS_XULCONTENTUTILS_CID);
static NS_DEFINE_IID(kIXULPopupListenerIID, NS_IXULPOPUPLISTENER_IID);
static NS_DEFINE_CID(kXULPopupListenerCID, NS_XULPOPUPLISTENER_CID);
static NS_DEFINE_IID(kIXULPopupListenerIID, NS_IXULPOPUPLISTENER_IID);
static NS_DEFINE_CID(kXULPopupListenerCID, NS_XULPOPUPLISTENER_CID);
static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
@ -247,6 +247,13 @@ nsIAtom* nsXULElement::kTreeRowAtom;
nsIAtom* nsXULElement::kEditorAtom;
nsIAtom* nsXULElement::kWindowAtom;
nsIAtom* nsXULElement::kNullAtom;
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
PRUint32 nsXULPrototypeAttribute::gNumElements;
PRUint32 nsXULPrototypeAttribute::gNumAttributes;
PRUint32 nsXULPrototypeAttribute::gNumEventHandlers;
#endif
//----------------------------------------------------------------------
// nsXULElement
@ -261,6 +268,9 @@ nsXULElement::nsXULElement()
mSlots(nsnull)
{
NS_INIT_REFCNT();
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
nsXULPrototypeAttribute::gNumElements++;
#endif
}
@ -294,7 +304,7 @@ nsXULElement::Init()
kTreeRowAtom = NS_NewAtom("treerow");
kEditorAtom = NS_NewAtom("editor");
kWindowAtom = NS_NewAtom("window");
kNullAtom = NS_NewAtom("");
kNullAtom = NS_NewAtom("");
rv = nsComponentManager::CreateInstance(kNameSpaceManagerCID,
nsnull,
@ -365,7 +375,7 @@ nsXULElement::~nsXULElement()
NS_IF_RELEASE(kTreeRowAtom);
NS_IF_RELEASE(kEditorAtom);
NS_IF_RELEASE(kWindowAtom);
NS_IF_RELEASE(kNullAtom);
NS_IF_RELEASE(kNullAtom);
NS_IF_RELEASE(gNameSpaceManager);
@ -426,6 +436,9 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype,
if (NS_FAILED(rv)) return rv;
if (found) {
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
nsXULPrototypeAttribute::gNumEventHandlers++;
#endif
rv = element->AddScriptEventListener(attr->mName, attr->mValue, iid);
if (NS_FAILED(rv)) return rv;
}
@ -510,6 +523,9 @@ nsXULElement::QueryInterface(REFNSIID iid, void** result)
else if (iid.Equals(kIScriptObjectOwnerIID)) {
*result = NS_STATIC_CAST(nsIScriptObjectOwner*, this);
}
else if (iid.Equals(NS_GET_IID(nsIScriptEventHandlerOwner))) {
*result = NS_STATIC_CAST(nsIScriptEventHandlerOwner*, this);
}
else if (iid.Equals(kIDOMEventReceiverIID)) {
*result = NS_STATIC_CAST(nsIDOMEventReceiver*, this);
}
@ -1483,6 +1499,45 @@ nsXULElement::SetScriptObject(void *aScriptObject)
}
//----------------------------------------------------------------------
// nsIScriptEventHandlerOwner interface
NS_IMETHODIMP
nsXULElement::GetCompiledEventHandler(nsIAtom *aName, void** aHandler)
{
*aHandler = nsnull;
if (mPrototype) {
for (PRInt32 i = 0; i < mPrototype->mNumAttributes; ++i) {
nsXULPrototypeAttribute* attr = &(mPrototype->mAttributes[i]);
if ((attr->mNameSpaceID == kNameSpaceID_None) &&
(attr->mName == aName)) {
*aHandler = attr->mEventHandler;
break;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXULElement::SetCompiledEventHandler(nsIAtom *aName, void* aHandler)
{
if (mPrototype) {
for (PRInt32 i = 0; i < mPrototype->mNumAttributes; ++i) {
nsXULPrototypeAttribute* attr = &(mPrototype->mAttributes[i]);
if ((attr->mNameSpaceID == kNameSpaceID_None) &&
(attr->mName == aName)) {
attr->mEventHandler = aHandler;
break;
}
}
}
return NS_OK;
}
//----------------------------------------------------------------------
// nsIJSScriptObject interface
@ -2136,8 +2191,8 @@ nsXULElement::SetAttribute(PRInt32 aNameSpaceID,
NS_IMETHODIMP
nsXULElement::GetAttribute(PRInt32 aNameSpaceID,
nsIAtom* aName,
nsString& aResult) const
nsIAtom* aName,
nsString& aResult) const
{
NS_ASSERTION(nsnull != aName, "must have attribute name");
if (nsnull == aName) {
@ -2199,18 +2254,21 @@ nsXULElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotif
if (! Attributes())
return NS_OK;
// Check to see if the CLASS attribute is being unset. If so, we need to delete our
// class list.
if (mDocument && (aNameSpaceID == kNameSpaceID_None) && (aName == kClassAtom)) {
// Check to see if the CLASS attribute is being unset. If so, we need to
// delete our class list.
// XXXbe fuse common (mDocument && aNameSpaceId == kNameSpaceID_None)
if (mDocument &&
(aNameSpaceID == kNameSpaceID_None) &&
(aName == kClassAtom)) {
Attributes()->UpdateClassList("");
}
if (mDocument && (aNameSpaceID == kNameSpaceID_None) && aName == kStyleAtom) {
if (mDocument &&
(aNameSpaceID == kNameSpaceID_None) &&
aName == kStyleAtom) {
nsCOMPtr <nsIURI> docURL;
if (nsnull != mDocument) {
mDocument->GetBaseURL(*getter_AddRefs(docURL));
}
mDocument->GetBaseURL(*getter_AddRefs(docURL));
Attributes()->UpdateStyleRule(docURL, "");
// XXX Some kind of special document update might need to happen here.
@ -2222,34 +2280,34 @@ nsXULElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotif
nsCOMPtr<nsIAtom> tag;
GetTag(*getter_AddRefs(tag));
if (aNameSpaceID == kNameSpaceID_None) {
// See if we're a treeitem atom.
// XXX Forgive me father, for I know exactly what I do, and I'm
// doing it anyway. Need to make an nsIRDFNodeList interface that
// I can QI to for additions and removals of nodes. For now
// do an evil cast.
nsCOMPtr<nsIRDFNodeList> nodeList;
if (tag && (tag.get() == kTreeItemAtom) && (aName == kSelectedAtom)) {
nsCOMPtr<nsIDOMXULTreeElement> treeElement;
GetParentTree(getter_AddRefs(treeElement));
if (treeElement) {
nsCOMPtr<nsIDOMNodeList> nodes;
treeElement->GetSelectedItems(getter_AddRefs(nodes));
nodeList = do_QueryInterface(nodes);
// See if we're a treeitem atom.
// XXX Forgive me father, for I know exactly what I do, and I'm
// doing it anyway. Need to make an nsIRDFNodeList interface that
// I can QI to for additions and removals of nodes. For now
// do an evil cast.
nsCOMPtr<nsIRDFNodeList> nodeList;
if (tag && (tag.get() == kTreeItemAtom) && (aName == kSelectedAtom)) {
nsCOMPtr<nsIDOMXULTreeElement> treeElement;
GetParentTree(getter_AddRefs(treeElement));
if (treeElement) {
nsCOMPtr<nsIDOMNodeList> nodes;
treeElement->GetSelectedItems(getter_AddRefs(nodes));
nodeList = do_QueryInterface(nodes);
}
}
}
else if (tag && (tag.get() == kTreeCellAtom) && (aName == kSelectedAtom)) {
nsCOMPtr<nsIDOMXULTreeElement> treeElement;
GetParentTree(getter_AddRefs(treeElement));
if (treeElement) {
nsCOMPtr<nsIDOMNodeList> nodes;
treeElement->GetSelectedCells(getter_AddRefs(nodes));
nodeList = do_QueryInterface(nodes);
else if (tag && (tag.get() == kTreeCellAtom) && (aName == kSelectedAtom)) {
nsCOMPtr<nsIDOMXULTreeElement> treeElement;
GetParentTree(getter_AddRefs(treeElement));
if (treeElement) {
nsCOMPtr<nsIDOMNodeList> nodes;
treeElement->GetSelectedCells(getter_AddRefs(nodes));
nodeList = do_QueryInterface(nodes);
}
}
if (nodeList) {
// Remove this node from the list.
nodeList->RemoveNode(this);
}
}
if (nodeList) {
// Remove this node from the list.
nodeList->RemoveNode(this);
}
}
// XXX Know how to remove POPUP event listeners when an attribute is unset?
@ -2263,7 +2321,8 @@ nsXULElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotif
PRInt32 i;
for (i = 0; i < count; i++) {
nsXULAttribute* attr = NS_REINTERPRET_CAST(nsXULAttribute*, Attributes()->ElementAt(i));
if ((attr->GetNameSpaceID() == aNameSpaceID) && (attr->GetName() == aName)) {
if ((attr->GetNameSpaceID() == aNameSpaceID) &&
(attr->GetName() == aName)) {
attr->GetValue(oldValue);
Attributes()->RemoveElementAt(i);
NS_RELEASE(attr);
@ -2275,50 +2334,50 @@ nsXULElement::UnsetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotif
// XUL Only. Find out if we have a broadcast listener for this element.
if (successful) {
// Check to see if the OBSERVES attribute is being unset. If so, we need to remove
// ourselves completely.
if (mDocument && (aNameSpaceID == kNameSpaceID_None) &&
(aName == kObservesAtom))
{
// Do a getElementById to retrieve the broadcaster.
nsCOMPtr<nsIDOMElement> broadcaster;
nsCOMPtr<nsIDOMXULDocument> domDoc = do_QueryInterface(mDocument);
domDoc->GetElementById(oldValue, getter_AddRefs(broadcaster));
if (broadcaster) {
nsCOMPtr<nsIDOMXULElement> xulBroadcaster = do_QueryInterface(broadcaster);
if (xulBroadcaster) {
xulBroadcaster->RemoveBroadcastListener("*", this);
}
}
}
if (BroadcastListeners()) {
PRInt32 count = BroadcastListeners()->Count();
for (PRInt32 i = 0; i < count; i++)
// Check to see if the OBSERVES attribute is being unset. If so, we
// need to remove ourselves completely.
if (mDocument &&
(aNameSpaceID == kNameSpaceID_None) &&
(aName == kObservesAtom))
{
XULBroadcastListener* xulListener =
NS_REINTERPRET_CAST(XULBroadcastListener*, BroadcastListeners()->ElementAt(i));
nsAutoString str;
aName->ToString(str);
if (xulListener->ObservingAttribute(str) &&
(aName != kIdAtom)) {
// XXX Should have a function that knows which attributes are special.
// Unset the attribute in the broadcast listener.
nsCOMPtr<nsIDOMElement> element;
element = do_QueryInterface(xulListener->mListener);
if (element)
element->RemoveAttribute(str);
// Do a getElementById to retrieve the broadcaster.
nsCOMPtr<nsIDOMElement> broadcaster;
nsCOMPtr<nsIDOMXULDocument> domDoc = do_QueryInterface(mDocument);
domDoc->GetElementById(oldValue, getter_AddRefs(broadcaster));
if (broadcaster) {
nsCOMPtr<nsIDOMXULElement> xulBroadcaster = do_QueryInterface(broadcaster);
if (xulBroadcaster) {
xulBroadcaster->RemoveBroadcastListener("*", this);
}
}
}
}
// Notify document
if (NS_SUCCEEDED(rv) && aNotify && (nsnull != mDocument)) {
mDocument->AttributeChanged(NS_STATIC_CAST(nsIStyledContent*, this),
aNameSpaceID, aName,
NS_STYLE_HINT_UNKNOWN);
}
if (BroadcastListeners()) {
PRInt32 count = BroadcastListeners()->Count();
for (PRInt32 i = 0; i < count; i++) {
XULBroadcastListener* xulListener =
NS_REINTERPRET_CAST(XULBroadcastListener*, BroadcastListeners()->ElementAt(i));
nsAutoString str;
aName->ToString(str);
if (xulListener->ObservingAttribute(str) &&
(aName != kIdAtom)) {
// XXX Should have a function that knows which attributes are special.
// Unset the attribute in the broadcast listener.
nsCOMPtr<nsIDOMElement> element;
element = do_QueryInterface(xulListener->mListener);
if (element)
element->RemoveAttribute(str);
}
}
}
// Notify document
if (NS_SUCCEEDED(rv) && aNotify && (nsnull != mDocument)) {
mDocument->AttributeChanged(NS_STATIC_CAST(nsIStyledContent*, this),
aNameSpaceID, aName,
NS_STYLE_HINT_UNKNOWN);
}
}
// End XUL Only Code
@ -3094,11 +3153,11 @@ nsXULElement::GetID(nsIAtom*& aResult) const
GetAttribute(kNameSpaceID_None, kIdAtom, value);
if (value.Length() > 0)
aResult = NS_NewAtom(value); // The NewAtom call does the AddRef.
aResult = NS_NewAtom(value); // The NewAtom call does the AddRef.
else
{
aResult = kNullAtom;
NS_ADDREF(kNullAtom);
aResult = kNullAtom;
NS_ADDREF(kNullAtom);
}
return NS_OK;
}

Просмотреть файл

@ -78,11 +78,63 @@ class nsXULAttributes;
class nsXULPrototypeAttribute
{
public:
nsXULPrototypeAttribute() : mNameSpaceID(kNameSpaceID_Unknown) {}
nsXULPrototypeAttribute()
: mNameSpaceID(kNameSpaceID_Unknown),
mEventHandler(nsnull)
{
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
gNumAttributes++;
#endif
}
PRInt32 mNameSpaceID;
nsCOMPtr<nsIAtom> mName;
nsString mValue;
void* mEventHandler;
#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
/**
If enough attributes, on average, are event handlers, it pays to keep
mEventHandler here, instead of maintaining a separate mapping in each
nsXULElement associating certain mName values to their mEventHandlers.
Assume we don't need to keep mNameSpaceID along with mName in such an
event-handler-only name-to-function-pointer mapping.
Let
minAttrSize = sizeof(mNameSpaceID) + sizeof(mName) + sizeof(mValue)
mappingSize = sizeof(mName) + sizeof(mEventHandler)
elemOverhead = nElems * sizeof(void*)
Then
nAttrs * minAttrSize + nEventHandlers * mappingSize + elemOverhead
> nAttrs * (minAttrSize + mappingSize - sizeof(mName))
which simplifies to
nEventHandlers * mappingSize + elemOverhead
> nAttrs * (mappingSize - sizeof(mName))
or
nEventHandlers + (nElems * sizeof(void*)) / mappingSize
> nAttrs * (1 - sizeof(mName) / mappingSize)
If nsCOMPtr and all other pointers are the same size, this reduces to
nEventHandlers + nElems / 2 > nAttrs / 2
To measure how many attributes are event handlers, compile XUL source
with XUL_PROTOTYPE_ATTRIBUTE_METERING and watch the counters below.
Plug into the above relation -- if true, it pays to put mEventHandler
in nsXULPrototypeAttribute rather than to keep a separate mapping.
Recent numbers after opening four browser windows:
nElems 3537, nAttrs 2528, nEventHandlers 1042
giving 1042 + 3537/2 > 2528/2 or 2810 > 1264.
As it happens, mEventHandler also makes this struct power-of-2 sized,
8 words on most architectures, which makes for strength-reduced array
index-to-pointer calculations.
*/
static PRUint32 gNumElements;
static PRUint32 gNumAttributes;
static PRUint32 gNumEventHandlers;
#endif
};
@ -249,6 +301,7 @@ class nsXULElement : public nsIStyledContent,
public nsIDOMXULElement,
public nsIDOMEventReceiver,
public nsIScriptObjectOwner,
public nsIScriptEventHandlerOwner,
public nsIJSScriptObject,
public nsIStyleRule,
public nsIChromeEventHandler
@ -389,6 +442,10 @@ public:
NS_IMETHOD GetScriptObject(nsIScriptContext* aContext, void** aScriptObject);
NS_IMETHOD SetScriptObject(void *aScriptObject);
// nsIScriptEventHandlerOwner
NS_IMETHOD GetCompiledEventHandler(nsIAtom *aName, void** aHandler);
NS_IMETHOD SetCompiledEventHandler(nsIAtom *aName, void* aHandler);
// nsIJSScriptObject
virtual PRBool AddProperty(JSContext *aContext, jsval aID, jsval *aVp);
virtual PRBool DeleteProperty(JSContext *aContext, jsval aID, jsval *aVp);