Closed
Bug 344986
Opened 19 years ago
Closed 19 years ago
Simplify nsEventListenerManager
Categories
(Core :: DOM: Events, enhancement)
Tracking
()
RESOLVED
FIXED
People
(Reporter: smaug, Assigned: smaug)
References
Details
Attachments
(5 files, 7 obsolete files)
|
95.73 KB,
patch
|
sicking
:
review+
|
Details | Diff | Splinter Review |
|
95.41 KB,
patch
|
sicking
:
review+
jst
:
superreview+
|
Details | Diff | Splinter Review |
|
95.28 KB,
patch
|
Details | Diff | Splinter Review | |
|
95.74 KB,
patch
|
sicking
:
review+
sicking
:
superreview+
|
Details | Diff | Splinter Review |
|
10.61 KB,
patch
|
dbaron
:
review+
dbaron
:
superreview+
|
Details | Diff | Splinter Review |
I think nsEventListenerManager could be a bit simpler.
Current it has several kinds of lists for event listeners (and handling all those
lists makes the code to look quite ugly), but probably those could be merged
to one, since usually there aren't that many listeners in one node.
I hope that doesn't regress performance too badly.
| Assignee | ||
Comment 1•19 years ago
|
||
This makes nsListenerStruct bigger, but removes some members from nsEventListenerManager. Fixes bug 276846.
Comment on attachment 229564 [details] [diff] [review]
Something like this
Couple of driveby comments just so I don't forget
>Index: widget/public/nsGUIEvent.h
>@@ -409,17 +410,17 @@ public:
...
> // Additional type info for user defined events
>- nsHashKey* userType;
>+ nsCOMPtr<nsIAtom> userType;
Why all the whitespace?
>Index: content/events/public/nsMutationEvent.h
> #define NS_MUTATION_ATTRMODIFIED (NS_MUTATION_START+5)
> #define NS_MUTATION_CHARACTERDATAMODIFIED (NS_MUTATION_START+6)
>+#define NS_MUTATION_END (NS_MUTATION_START+6)
Shouldn't this be +7?
| Assignee | ||
Comment 4•19 years ago
|
||
This is getting closer to something for a review.
Fixes bunch of bugs too: bug 276846, usage of nsIDOMMutationListener,
usage of nsIDOMLoadListener with bfcache...
Removes ~450 lines, though many lines are macro defs.
Assignee: events → Olli.Pettay
Status: NEW → ASSIGNED
| Assignee | ||
Comment 5•19 years ago
|
||
This should be ready at least for comments.
I run dhtml tests 4 times with and without this patch and 3 times results
were better with the patch.
The patch adds caching members to ELM, so that there is no need to search for
listeners every time.
| Assignee | ||
Comment 6•19 years ago
|
||
Comment on attachment 229665 [details] [diff] [review]
ready for comments
(also binary size decreases a bit with the patch)
I think this could be ready for a review. Imo simplifies ELM (though someone might think something else..) and makes the code a bit more manageable. We really don't need that many macro definitions for all kinds of bits.
I think NS_MUTATION_END is right, it points to the last mutation event.
nsEvent::userType has that many spaces so that it aligns with something, in this case with nsEvent::target.
nsListenerStruct is used so that if mEventType != 0 listener was registered using an event type, otherwise it was registered using IID.
Attachment #229665 -
Flags: review?(bugmail)
| Assignee | ||
Updated•19 years ago
|
Attachment #229637 -
Attachment is obsolete: true
| Assignee | ||
Updated•19 years ago
|
Attachment #229564 -
Attachment is obsolete: true
Smaug: could you give a short overview of the changes in this patch? To aid reviewing.
| Assignee | ||
Comment 8•19 years ago
|
||
Reminder for myself, give the overview of the patch!
| Assignee | ||
Comment 9•19 years ago
|
||
Finally some comments.
So this patch removes NS_EVENT_BITS_* and combines all event listeners to one
list. nsListenerStruct is also changed so that it contains the event type, possible an atom for user defined events, or if a listener interface is used
for listener registration it contains a pointer to the EventTypeData.
::HandleEvent is the method which is called most often. sLatestEvent* were added to cache some search result so that there is no need to loop sEventTypes
every time. Similar kind of optimization is mNoListenerForEvent, which is set
whenever event is tryed to be handled but there is no event listener for that.
This way ::HandleRelease can return early in most cases.
(HandleRelease is after called normally 4 times during each event dispatch)
Comment on attachment 229665 [details] [diff] [review]
ready for comments
>Index: content/events/src/nsEventListenerManager.cpp
>+#define EVENT_TYPE_EQUALS( ls, type, userType ) \
>+ (ls->mEventType && ls->mEventType == type && \
>+ (ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType))
Should this be
#define EVENT_TYPE_EQUALS( ls, type, userType ) \
(ls->mEventType == NS_USER_DEFINED_EVENT ? ls->mTypeAtom == userType : ls->mEventType == type)
?
>+nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
>+ nsListenerStruct* ls = nsnull;
>+ for (PRInt32 i = 0; i < mListeners->Count(); i++) {
>+ ls = NS_STATIC_CAST(nsListenerStruct*, mListeners->ElementAt(i));
>+ nsRefPtr<nsIDOMEventListener> listener = ls->mListener.Get();
Use nsCOMPtr, it'll give you some extra features that you'll probably want. Or does that assert?
Note to self, i'm on nsEventListenerManager::AddEventListener
Comment on attachment 229665 [details] [diff] [review]
ready for comments
>+nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
>+ PRUint32 aType,
>+ nsIAtom* aTypeAtom,
>+ const EventTypeData* aTypeData,
> PRInt32 aFlags,
> nsIDOMEventGroup* aEvtGrp)
> {
> NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
>+ NS_ENSURE_TRUE((aType || aTypeData), NS_ERROR_FAILURE);
No need for the extra parens.
>+ if ((aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) ||
>+ (ls->mTypeData && ls->mTypeData->iid &&
>+ ls->mTypeData->iid->Equals(NS_GET_IID(nsIDOMMutationListener)))) {
>+ mMayHaveMutationListeners = PR_TRUE;
>+ // Go from our target to the nearest enclosing DOM window.
>+ nsCOMPtr<nsPIDOMWindow> window;
>+ nsCOMPtr<nsIDocument> document;
>+ nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
>+ if (node){
>+ document = node->GetOwnerDoc();
>+ }
>+ if (document) {
>+ window = document->GetInnerWindow();
>+ } else {
>+ window = do_QueryInterface(mTarget);
>+ }
Nit: you could put the |if (document)| check inside the |if (node)| check since that's the only time it can pass.
Add a space before the '{' in |if (node){|
> nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
>+ for (PRInt32 i = 0; i < mListeners->Count(); ++i) {
Cache the result of the ->Count() call (this may be a good thing in other loops).
>+ ls = NS_STATIC_CAST(nsListenerStruct*, mListeners->ElementAt(i));
>+ nsRefPtr<nsIDOMEventListener> listener = ls->mListener.Get();
>+ if (listener == aListener &&
>+ ls->mGroupFlags == group &&
>+ ((ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) &&
>+ (EVENT_TYPE_EQUALS(ls, aType, aUserType) ||
>+ (!(ls->mEventType) &&
>+ EVENT_TYPE_DATA_EQUALS(ls->mTypeData, aTypeData)))) {
>+ mListeners->RemoveElement((void*)ls);
RemoveElementAt(i) would be faster.
>+nsEventListenerManager::GetIdentifierForEvent(nsIAtom* aEvent)
>+{
>+ if (aEvent == nsLayoutAtoms::onmousedown)
>+ return NS_MOUSE_LEFT_BUTTON_DOWN;
>+ if (aEvent == nsLayoutAtoms::onmouseup)
>+ return NS_MOUSE_LEFT_BUTTON_UP;
...
It'd probably be faster (due to instruction cache) and cleaner to turn this into a table and a loop. Or it could be a hash even if it's called often enough.
>+nsEventListenerManager::HasMutationListeners(PRBool* aListener)
>+{
>+ *aListener = PR_FALSE;
>+ if (mMayHaveMutationListeners && mListeners) {
>+ PRInt32 count = mListeners->Count();
>+ for (PRInt32 i = 0; i < count; ++i) {
>+ nsListenerStruct* ls = NS_STATIC_CAST(nsListenerStruct*,
>+ mListeners->FastElementAt(i));
>+ if (ls &&
>+ (ls->mEventType >= NS_MUTATION_START &&
>+ ls->mEventType <= NS_MUTATION_END) ||
>+ (ls->mTypeData && ls->mTypeData->iid &&
>+ ls->mTypeData->iid->Equals(NS_GET_IID(nsIDOMMutationListener)))) {
>+ *aListener = PR_TRUE;
Add a break or return?
r=me with that.
Attachment #229665 -
Flags: review?(bugmail) → review+
| Assignee | ||
Comment 12•19 years ago
|
||
Comment on attachment 237346 [details] [diff] [review]
+comments, using hashtable
>+#define NS_SET_EVENT_ID(_event, _id) \
>+ success = (success && (gEventIdTable->Put(nsLayoutAtoms::on##_event, _id)))
You could make that sucess &&= gEventIdTable...
Attachment #237346 -
Flags: review+
| Assignee | ||
Comment 14•19 years ago
|
||
Comment on attachment 237346 [details] [diff] [review]
+comments, using hashtable
Jst, could you look at this?
Or at least say what you think about this kind of change.
Attachment #237346 -
Flags: superreview?(jst)
Comment 15•19 years ago
|
||
Comment on attachment 237346 [details] [diff] [review]
+comments, using hashtable
I'm all for a change like this! Some comments below...
- In nsEventListenerManager::GetTypeDataForEvent(nsIAtom *aEvent):
Both the method name and the argument name suggest that the argment that should be passed to this method is an event, where as it really is just the name of an event. Maybe rename this to GetTypeDataForEventName() and name the argument aName?
- In nsEventListenerManager::AddEventListener()
+ if (!mListeners) {
+ mListeners = new nsVoidArray();
+ NS_ENSURE_TRUE(mListeners, NS_ERROR_OUT_OF_MEMORY);
}
I wonder what the most common case is here. I'd guess that it's really uncommon for an event listener manager to have *no* listeners in it. If that's true then we could make nsEventListenerManager::mListeners be a nsVoidArray instead of a pointer to one. Fewer mallocs. And also, what's the most common number of event listeners? If it's 1, then it might even make sense to use the type nsSmallVoidArray. To make this change we'd need to instrument this code a bit (and I'd be fine with doing that in a separate bug, of course).
+static void
+InitializeEventIdTable() {
That opening brace really needs to be on a line of its own.
- In nsEventListenerManager::AddEventListenerByType():
{
- PRInt32 subType;
- EventArrayType arrayType;
nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aType);
Something to consider for another bug, but wouldn't it make more sense to no include the "on" in the atoms here? It's cheaper to strip out the on where we have a string starting with "on" than it it to always add it before atomizing etc.
In nsEventListenerManager.h, in the definition of nsListenerStruct:
+ PRUint32 mEventType;
+ nsCOMPtr<nsIAtom> mTypeAtom;
+ PRUint16 mFlags;
+ PRUint16 mGroupFlags;
+ PRBool mHandlerIsString;
+ const EventTypeData* mTypeData;
If we *really* care, we could eliminate the separate mHandlerIsString and use the low bit of either mTypeAtom (in which case it couldn't be an nsCOMPtr any more) or maybe mTypeData, unless we can fit it into one of the flags memgers, that is...
sr=jst
Attachment #237346 -
Flags: superreview?(jst) → superreview+
Since an nsVoidArray is just 4 bytes these days there's no point in using nsVoidArray* for this, even if it's common to have no listeners.
Using nsSmallVoidArray seems like a really good idea if there's often just 1 listener, but that'd require some more instrumentation. nsSmallVoidArray only has overhead in cykles these days, not in footprint, so it's likely a good idea.
| Assignee | ||
Comment 17•19 years ago
|
||
| Assignee | ||
Comment 18•19 years ago
|
||
Checked in, but this may have caused tp to go up a bit.
I'll look at the problem tomorrow and back out if necessary.
| Assignee | ||
Comment 19•19 years ago
|
||
I did some jproffing (IIRC, bz did too). I think the problem
is in ::HandleEvent. I'll post a new just slightly modified patch which reduces
iterations of mListeners and does some other small optimizations.
It should help, but whether it fixes the whole tp regression... dunno,
have to test with tbox.
| Assignee | ||
Comment 20•19 years ago
|
||
Quite small changes to ELM::HandleEvent (and only to that method).
The main change is to move (useTypeInterface || useGenericInterface) to be before mListeners.IndexOf(ls) != -1. No need to test whether listener is
actually still in mListeners, if the listener won't handle the event anyway.
Having (useTypeInterface || useGenericInterface) earlier saves also few other
things...
Using jprof and the performance testcase darin once posted to the newsgroup (tp2?) this seems to bring ::HandleEvent back to what it is originally.
Attachment #238091 -
Flags: superreview?(bugmail)
Attachment #238091 -
Flags: review?(bugmail)
| Assignee | ||
Comment 21•19 years ago
|
||
Comment on attachment 238091 [details] [diff] [review]
fixes to ::HandleEvent
There is some regression related to keyevents and menubar. Will re-ask review once I've solved that problem.
Attachment #238091 -
Flags: superreview?(bugmail)
Attachment #238091 -
Flags: review?(bugmail)
| Assignee | ||
Comment 22•19 years ago
|
||
The only change to the previous patch is in ::HandleEvent
PRBool useTypeInterface =
+ ((ls->mEventType == NS_EVENT_TYPE_NULL) ||
+ ls->mEventType == aEvent->message) &&
EVENT_TYPE_DATA_EQUALS(ls->mTypeData, typeData);
Attachment #238091 -
Attachment is obsolete: true
Attachment #239481 -
Flags: superreview?(bugmail)
Attachment #239481 -
Flags: review?(bugmail)
| Assignee | ||
Comment 23•19 years ago
|
||
Comment on attachment 239481 [details] [diff] [review]
v5
argh, there is still something wrong. The old ELM is really way too complicated and undocumented.
Attachment #239481 -
Flags: superreview?(bugmail)
Attachment #239481 -
Flags: review?(bugmail)
| Assignee | ||
Comment 24•19 years ago
|
||
The problem I saw was that when using event type specific listeners (for example nsIDOMLoadListener) it doesn't matter what event type is actually used for registration ("load", "unload"); the listener should
be in the listener list only once, even if registered many times using
different event type (event type in the same category).
Attachment #239481 -
Attachment is obsolete: true
Attachment #239675 -
Flags: superreview?(bugmail)
Attachment #239675 -
Flags: review?(bugmail)
| Assignee | ||
Comment 25•19 years ago
|
||
The previous patch was missing mListenerRemoved = PR_TRUE;
in RemoveScriptEventListener.
Attachment #239675 -
Attachment is obsolete: true
Attachment #240222 -
Flags: superreview?(bugmail)
Attachment #240222 -
Flags: review?(bugmail)
Attachment #239675 -
Flags: superreview?(bugmail)
Attachment #239675 -
Flags: review?(bugmail)
| Assignee | ||
Comment 26•19 years ago
|
||
btw, I've run jprof with dhtml and tp2 tests and the latest patch shouldn't cause performance regressions.
Comment on attachment 240222 [details] [diff] [review]
v8
looks good. r/sr=sicking
Attachment #240222 -
Flags: superreview?(bugmail)
Attachment #240222 -
Flags: superreview+
Attachment #240222 -
Flags: review?(bugmail)
Attachment #240222 -
Flags: review+
This was actually a significant codesize increase -- not what I was expecting from the line count decrease. All of the increase on prometheus-vm can be attributed to the size of a single function: InitializeEventIdTable. Could that instead be written using an array and a loop?
| Assignee | ||
Comment 29•19 years ago
|
||
(In reply to comment #28)
> This was actually a significant codesize increase -- not what I was expecting
> from the line count decrease. All of the increase on prometheus-vm can be
> attributed to the size of a single function: InitializeEventIdTable. Could
> that instead be written using an array and a loop?
>
I was surprised to see the codesize increase.
I'll make a patch to change eventIdTable.
| Assignee | ||
Comment 30•19 years ago
|
||
Attachment #241435 -
Flags: superreview?(bugmail)
Attachment #241435 -
Flags: review?(bugmail)
Comment on attachment 241435 [details] [diff] [review]
reduces codesize a bit
>+ if (!gEventIdTable || !gEventIdTable->Init(NS_ARRAY_LENGTH(eventIdArray)))
You probably want int(NS_ARRAY_LENGTH(eventIdArray) / 0.75) + 1. See the comments in pldhash.h about PL_DHashTableSetAlphaBounds.
I also wonder whether you're better off using nsIAtom** and making the array static (although that does often require relocation at library load time -- although maybe less so these days with prelinking, etc.).
| Assignee | ||
Comment 32•19 years ago
|
||
Attachment #241435 -
Attachment is obsolete: true
Attachment #241712 -
Flags: superreview?(dbaron)
Attachment #241712 -
Flags: review?(dbaron)
Attachment #241435 -
Flags: superreview?(bugmail)
Attachment #241435 -
Flags: review?(bugmail)
Comment on attachment 241712 [details] [diff] [review]
+comments
r+sr=dbaron. Might not be a bad idea to ask brendan (via email?) whether there ought to be a macro in pldhash.h for the 0.75 so we don't have to write it here as a magic number...
Attachment #241712 -
Flags: superreview?(dbaron)
Attachment #241712 -
Flags: superreview+
Attachment #241712 -
Flags: review?(dbaron)
Attachment #241712 -
Flags: review+
Yeah, a macro that takes the number of entries and returns an appropriate size seems like a good idea.
| Assignee | ||
Comment 35•19 years ago
|
||
Comment on attachment 241712 [details] [diff] [review]
+comments
Checked in, filed Bug 356116 for the macro.
| Assignee | ||
Comment 36•19 years ago
|
||
Zdiff:-3280
Status: ASSIGNED → RESOLVED
Closed: 19 years ago
Resolution: --- → FIXED
You need to log in
before you can comment on or make changes to this bug.
Description
•