Closed
Bug 244249
Opened 20 years ago
Closed 20 years ago
Consider making GetID not allocate
Categories
(Core :: SVG, defect)
Tracking
()
RESOLVED
FIXED
People
(Reporter: bzbarsky, Assigned: jlurz24)
References
Details
(Keywords: perf)
Attachments
(4 files, 7 obsolete files)
GetID is called during style resolution (which will happen a lot on anything being animated via inline style or attribute changes). At the moment, SVG gets a string and allocates an atom every time this is called. Isn't the ID already stored as an atom? If so, can't we just return it? If we fixed this, we could deCOMtaminate nsIStyledContent::GetID also, for a speed boost across the board.
SVG shouldn't be a problem, however generic XML will have to add an extra member in order for this to work
Reporter | ||
Comment 2•20 years ago
|
||
May be worth it, for the speed improvement. I was noticing GetID coming up high in style system profiles of _HTML_, where all it's doing is addref/release....
Comment 3•20 years ago
|
||
Mass reassign of SVG bugs that aren't currently being worked on by Alex to general@svg.bugs. If you think someone should be assigned to your bug you can join the #svg channel on mozilla.org's IRC server ( irc://irc.mozilla.org/svg ) where you can try to convince one of the SVG hackers to work on it. We aren't always there, so if you don't get a response straight away please try again later.
Assignee: alex → general
Okay I've almost got this working, turned out it wasn't as difficult as I thought. I think XUL and HTML should get a nice speed boost by reducing addreffing, and SVG will get a boost from reduced allocation. I haven't quite figured out how to do this for SVG yet, I don't think its storing the id as an eAtom, but it shouldn't be too difficult. The main issue I ran into is the nsXMLElement doesn't really have its own implementation of SetAttr, which is where the attribute needs to parse the atom before storing it.(That's how XUL and HTML do it) nsXMLElement calls nsGenericElement's setAttr, which doesn't take an attribute. So I'm not sure whether to change SetAttr in nsGenericElement, as I think nsXMLElement is the only user of that function, or to move this function into nsXMLElement, but this is more difficult and might result in code duplication. Also if anyone knows of a good testcase that makes use of id in XML, that'd be very helpful.
I think that if you can make the code itself generic enough (for example by comparing the attrname to mNodeInfo->GetIDAttributeAtom()) this could well go in nsGenericElement. A goal of mine is to reduce the codeduplication currently existing in all the SetAttr implementations. (IIRC I didn't do it in my initial attr reworking because XUL is still too different, although that can be remedied now). So don't do anything that will make that harder, but I think you should be able to not do that.
Okay great, thats close to how I did it already. I used GetIDAttributeName(), which in nsGenericElement returns mNodeInfo->GetIDAttributeAtom(). XUL, XML, SVG and HTML now share the same implementation of GetID. Could this be moved to nsGenericElement? (Currently the implementation there returns null) I've got SVG working now too, so I think I've got a complete implementation. I need to clean this up a bit and update to the head, and then I will submit a patch. I still haven't figured out exactly how to test XML, and am open to ideas on how to regression test this overall. Should something like this be perf tested as well?
Reporter | ||
Comment 7•20 years ago
|
||
> Could this be moved to nsGenericElement? (Currently the implementation there > returns null) XUL still doesn't inherit from nsGenericElement. So it'll need to keep its own copy of the implementation (with an XXX comment?). I think combining the rest in nsGenericElement is a good idea. The only reason to maybe not to it is that it requires two vtable calls (to GetID and GetIDAttributeName) for HTML. I think the smaller amount of code is worth it, though. > I still haven't figured out exactly how to test XML You'll need to have an XML file with an inline DTD that declares an attribute to be of type ID, then a CSS stylesheet that uses a #foo selector (which should then match that attribute). > Should something like this be perf tested as well? Once you have a patch, I'll try to write up a testcase that tests our GetID code and profile it with and without the patch.
> XUL still doesn't inherit from nsGenericElement. So it'll need to keep its own > copy of the implementation (with an XXX comment?). Right, I put an XXX comment in for this. Currently the implementation is slightly different, but I'm pretty sure it doesn't need to be. > I think combining the rest in nsGenericElement is a good idea. The only reason > to maybe not to it is that it requires two vtable calls (to GetID and > GetIDAttributeName) for HTML. I think the smaller amount of code is worth it, > though. Okay I combined the code, should make maintenance easier and reduce code size slightly. We'll see what kind of perf effect the extra vtable call has. > You'll need to have an XML file with an inline DTD that declares an attribute to > be of type ID, then a CSS stylesheet that uses a #foo selector (which should > then match that attribute). Did this, and it works. I think this might use the GetId function though, not GetID function. I'll upload a patch briefly...
First shot at this patch. It passes basic functionality tests for XUL, XML, SVG and HTML. I'm not going to set review flags quite yet because I'd like to test it some more first. If anybody wants to have a look though, that'd be great.
Reporter | ||
Comment 10•20 years ago
|
||
If you upload your XML testcase (probably a good idea anyway), I can tell you whether it exercises GetId or GetID.
Assignee | ||
Comment 11•20 years ago
|
||
CSS for XML testcase
Assignee | ||
Comment 12•20 years ago
|
||
ML for testcase...looking closer at bz's comment I now think that I should have used a # selector instead "id=" in my stylesheet(I thought they did the same thing). Could be a useful testcase/turned into a useful testcase anyway I think.
Reporter | ||
Comment 13•20 years ago
|
||
#foo and [id=foo] don't do the same thing in CSS, and don't follow the same codepath in Mozilla. So no, that testcase is not testing GetID (or GetId, for that matter; it's testing GetAttr()). For future reference, you can point the testcase to the CSS attached in bugzilla so it doesn't have to be downloaded to work... ;)
Assignee | ||
Comment 14•20 years ago
|
||
Okay so that testcase is very wrong, my knowledge of CSS isn't so great. With some digging through the spec I managed to figure it out though and correct the testcase, which is good, but it shows the nsXMLElement GetID doesn't work, which is bad(it asserts). Now that I have a working testcase though it shouldn't take long to work out. So ignore that part of the patch for now, I'll have a new one soon.
Assignee | ||
Comment 15•20 years ago
|
||
So I think I've got this one figured out and working. In my new code the ID was getting set as a string for the first instance of an element, and correctly as an atom the second time, causing a nice crash. This was because GetIDAttributeName() was returning null for the first element during SetAttr(), since SetIDAttributeAtom() was getting called after AddAttributes() in the nsXMLContentSink. This was okay before since GetIDAttributeName() was only needed in GetAttr(), after SetIDAttributeAtom(). I moved the call to SetIDAttributeAtom() prior to AddAttributes() in nsXMLContentSink, and now things work as expected. Does this sound like the right thing to do?
Reporter | ||
Comment 16•20 years ago
|
||
Yes, that sounds correct.
You probably wanna handle gracefully if the id-name is changed. This can happen if an element is moved to a different document which has a different DTD. What you probably wanna do is if GetID is called and the attribute is stored as an eString set it as an eAtom and then do what you do now. (Just remember to have aNotify set to false when calling SetAttr so that you don't fire off mutation events). It's probably a good idea to move the SetIDAttributeAtom call though to save a few cycles.
Assignee | ||
Comment 18•20 years ago
|
||
(In reply to comment #17) > You probably wanna handle gracefully if the id-name is changed. Ah yes, currently that situation would cause a crash I think(after an assertion failed.) So it's okay to cast away the constness of GetID in that case? I'll see if I can figure out a testcase for that(I'm not exactly sure how to switch a document's element on the fly).
Assignee | ||
Comment 19•20 years ago
|
||
css for working xml GetID testcase
Attachment #163389 -
Attachment is obsolete: true
Assignee | ||
Comment 20•20 years ago
|
||
ML for working XMLElement GetID testcase. Actually links to the css file in bugzilla this time.
Attachment #163390 -
Attachment is obsolete: true
Reporter | ||
Comment 21•20 years ago
|
||
(In reply to comment #17) > You probably wanna handle gracefully if the id-name is changed. This can > happen if an element is moved to a different document which has a different > DTD. It can? The only places where SetIDAttributeAtom() is called are in the content sink. So at the moment, moving an element to a different document doesn't change the attribute name of the id....
cast away the corectness?
(In reply to comment #21) > It can? The only places where SetIDAttributeAtom() is called are in the content > sink. So at the moment, moving an element to a different document doesn't > change the attribute name of the id.... Moving it to another document will give it a new mNodeInfo, which might have a different id-name. In fact, this might even happen within a single document if you change an elements prefix. I'll try to come up with a testcase.
Reporter | ||
Comment 24•20 years ago
|
||
Oh, right. Never mind me. ;)
This tests an element changing id. Test clicking 'show elements' and 'restyle' before and after clicking 'set prefix'. It's hard to say what's correct behaviour. Using namespaces and DTDs together often gives amigious situations. The most important thing is that we don't crash.
Assignee | ||
Comment 26•20 years ago
|
||
Thanks for the testcase. I'll make sure the code maintains the current behaviour, and certainly doesn't crash.
Assignee | ||
Comment 27•20 years ago
|
||
Works, but I'd like to find a better way.
Attachment #163377 -
Attachment is obsolete: true
Assignee | ||
Comment 28•20 years ago
|
||
So the patch I've just uploaded works with changing namespaces, but its kinda ugly. I checked for a string value in GetID, and then reset it using setAttr. SetAttr required a modification to avoid returning early since the two values, the original and the new were the same. If anyone has any suggestions for an improved approach, or knows of a way to detect the namespace changing(so the atom can be reset), that'd be great.
First off, note that the namespace isn't chaning, just the prefix. It's impossible to change the namespace of an element. You could hook into the prefix getting changed by adding code to the SetPrefix functions, however that would add a lot of clutter and would not cover the case when an element is moved from one document to another, so that's not a good option. I thought that we didn't do the same-value check if aNotify was false, however looking at the code it looks like that isn't the case in nsGenericElement::SetAttr, only in nsGenericHTMLElement::SetAttr (and not always there either due to mutationevents, but i think that's a bug). These annoying inconsitencies is one of the reasons I want to consolidate the SetAttr implementaitons. But I'm digressing. To avoid the entire SetAttr mess you could go streight to the mAttrAndChildArray and call SetAndTakeAttr. You'd first need to create an nsAttrValue though.
Assignee | ||
Comment 30•20 years ago
|
||
Sorry this got lost for a while, this is a cleaner version of the previous patch which parses the value directly into the attribute.
Attachment #163916 -
Attachment is obsolete: true
Attachment #166052 -
Flags: review?(bzbarsky)
Comment on attachment 166052 [details] [diff] [review] GetID_deCOM_v3 >Index: content/base/src/nsGenericElement.cpp Please follow convention in this file and use two-space-indentation. >+nsIAtom* >+nsGenericElement::GetID() const >+{ >+ nsIAtom* IDName = GetIDAttributeName(); >+ if (IDName) { >+ const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(IDName); >+ if (attrVal){ >+ if (attrVal->Type() == nsAttrValue::eAtom) { >+ return attrVal->GetAtomValue(); >+ } >+ // Check if the ID has been stored as a string. >+ // This would occur if the ID attribute name changed after >+ // the ID was parsed. It'd be a lot faster to test for emptystring right away before creating an nsAutoString etc. Especially since that's what'll happen if you set id="" in the markup. Use attr->IsEmptyString(). Put that code above the comment since the comment doesn't apply to that situation. >@@ -3365,18 +3386,30 @@ nsGenericElement::SetAttr(PRInt32 aNames > mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); > > if (aNotify && document) { > document->AttributeWillChange(this, aNamespaceID, aName); > } > > nsresult rv; > if (aNamespaceID == kNameSpaceID_None) { >- rv = mAttrsAndChildren.SetAttr(aName, aValue); >- NS_ENSURE_SUCCESS(rv, rv); >+ if (aName == GetIDAttributeName()) { >+ if (!aValue.Equals(EmptyString())) { Better done as !aValue.IsEmpty(). Also, these two tests should be written as if (aName == GetIDAttributeName() && !aValue.IsEmpty()) { ... Otherwise you won't do anything if someone calls myElem.setAttribute('id','') >Index: content/html/style/src/nsCSSStyleSheet.cpp >@@ -2854,17 +2854,17 @@ RuleProcessorData::RuleProcessorData(nsP > mParentContent = aContent->GetParent(); > > // get the event state > mPresContext->EventStateManager()->GetContentState(aContent, mEventState); > > // get the styledcontent interface and the ID > if (NS_SUCCEEDED(aContent->QueryInterface(NS_GET_IID(nsIStyledContent), (void**)&mStyledContent))) { > NS_ASSERTION(mStyledContent, "Succeeded but returned null"); >- mStyledContent->GetID(&mContentID); >+ mContentID = mStyledContent->GetID(); You need to addref here, otherwise the atom might be deleted and you'll be left with a dangling pointer. Alternativly you could make mContentID into an nsCOMPtr which will handle that for you. >@@ -2905,17 +2905,16 @@ RuleProcessorData::~RuleProcessorData() > { > MOZ_COUNT_DTOR(RuleProcessorData); > > if (mPreviousSiblingData) > mPreviousSiblingData->Destroy(mPresContext); > if (mParentData) > mParentData->Destroy(mPresContext); > >- NS_IF_RELEASE(mContentID); > NS_IF_RELEASE(mStyledContent); And you still need to release here. >Index: content/svg/content/src/nsSVGElement.cpp Again, please follow indentation convention of the file you're editing. >@@ -199,17 +199,22 @@ nsSVGElement::SetAttr(PRInt32 aNamespace > attrValue.SetTo(svg_value); > } > } > else if (aName == nsSVGAtoms::style && aNamespaceID == kNameSpaceID_None) { > nsGenericHTMLElement::ParseStyleAttribute(this, PR_TRUE, aValue, attrValue); > } > else { > // We don't have an nsISVGValue attribute. >- attrValue.SetTo(aValue); >+ if(aName == GetIDAttributeName() && aNamespaceID == kNameSpaceID_None){ >+ attrValue.ParseAtom(aValue); >+ } >+ else { >+ attrValue.SetTo(aValue); >+ } Nit: please write the aName/aNamespaceID test as another |else if|, just as for 'style' above. And use nsSVGAtoms::id directly. Also, have you tested that things still work for SVG. SVG doesn't always store the value as an nsAttrValue directly, but I don't remember if that was the case for the 'id' attribute. With that, r=sicking
Attachment #166052 -
Flags: review?(bzbarsky) → review+
Reporter | ||
Comment 32•20 years ago
|
||
> You need to addref here, otherwise the atom might be deleted and you'll be left
> with a dangling pointer.
Actually, part of the point of this patch is that it allows to NOT addref there
(hence the need to make GetID() not allocate). The ruleprocessordata struct is
a temporary thing allocated on the stack during style resolution; no DOM changes
can happend during the lifetime of a ruleprocessordata struct. So that part of
the patch is, in fact, correct.
Oh, cool. Please add a comment in the .h for that struct saying that the member is weak instead.
Assignee | ||
Comment 34•20 years ago
|
||
Hmm thought I replied to this last night. Odd. Thanks for the quick reviews, I'll whip up another patch later tonight. > Please follow convention in this file and use two-space-indentation. My mistake, I'm a little too used to 4 spaces. I'll fix all these. > Better done as !aValue.IsEmpty(). > Also, these two tests should be written as > if (aName == GetIDAttributeName() && !aValue.IsEmpty()) { ... > Otherwise you won't do anything if someone calls myElem.setAttribute('id','') What is the correct behavior in this situation? Should getting this id later return nsnull or an atom containing the empty string? I'm confused by this comment, which shows up in a couple places: // id="" means that the element has no id, not that it has // an emptystring as the id. > You need to addref here, otherwise the atom might be deleted and you'll be left > with a dangling pointer. I actually spent a while tracing this through to convice myself that this could be a weak reference. I'll add a comment to the header to clarify. > Also, have you tested that things still work for SVG. SVG doesn't always store > the value as an nsAttrValue directly, but I don't remember if that was the case > for the 'id' attribute. I ran through some of the mozilla svg test suite and everything seemed correct, I'll run through it again to be safe.
> > Better done as !aValue.IsEmpty(). > > Also, these two tests should be written as > > if (aName == GetIDAttributeName() && !aValue.IsEmpty()) { ... > > Otherwise you won't do anything if someone calls myElem.setAttribute('id','') > What is the correct behavior in this situation? Should getting this id > later return nsnull or an atom containing the empty string? Well, the current patch makes calling myElem.setAttribute('id','') a no-op, i.e. the old value is left, which clearly is wrong. What you should return is nsnull. Returning nsnull means that the element doesn't have an id (for example, the no id-attribute is set at all), which is desired behaviour after the attribute has been set to "". For markup like: <div id="">hello</div> The following code should display 'null': alert(documeng.getElementById("")); > > Also, have you tested that things still work for SVG. SVG doesn't always store > > the value as an nsAttrValue directly, but I don't remember if that was the case > > for the 'id' attribute. > I ran through some of the mozilla svg test suite and everything seemed > correct, I'll run through it again to be safe. One way of making sure could be to set a breakpoint inside the else if(aName == nsSVGAtoms::id) { and making sure that that code gets run while parsing an svg file.
Assignee | ||
Comment 36•20 years ago
|
||
> What you should return is nsnull. Returning nsnull means that the element > doesn't have an id (for example, the no id-attribute is set at all), which is > desired behaviour after the attribute has been set to "". Of course, I wasn't thinking about the resetting an existing id case. Fixed. > One way of making sure could be to set a breakpoint inside the > else if(aName == nsSVGAtoms::id) { > and making sure that that code gets run while parsing an svg file. Done, it hits it. New patch coming up in a second, thanks again for all the great help.
Assignee | ||
Comment 37•20 years ago
|
||
Fixed all the review comments. There's also a warning fix in nsGenericElement for an unsigned signed comparison in debug only code, warnings annoy me.
Attachment #166052 -
Attachment is obsolete: true
Comment on attachment 166238 [details] [diff] [review] GetID_deCOM_v4 >Index: content/base/src/nsGenericElement.cpp >+nsGenericElement::GetID() const ... >+ // Check if the ID has been stored as a string. >+ // This would occur if the ID attribute name changed after >+ // the ID was parsed. >+ if (attrVal->Type() == nsAttrValue::eString) { >+ nsAutoString idVal; >+ idVal.Assign(attrVal->GetStringValue()); >+ >+ // Create an atom from the value and set it into the attribute list. >+ NS_CONST_CAST(nsAttrValue*, attrVal)->ParseAtom(idVal); >+ return attrVal->GetAtomValue(); I think I'd prefer if you created a new nsAttrValue here and properly called mAttrsAndChildren.SetAndTakeAttr rather then the const-cast evilness. This isn't performance critical in any way since it will hardly ever be ran, so no need to optimize too much. No biggie though, your version should work since id is never a mapped attribute. >Index: content/xml/document/src/nsXMLContentSink.cpp ... >+ // Set the ID attribute atom on the node info object for this node >+ if (aIndex != -1 && NS_SUCCEEDED(result)) { Please add a comment like 'make sure to do this before calling AddAttributes' so that it doesn't accidently get moved down again. (If it does then things will still work but we'd get a performance hit converting string-ids to atoms).
Attachment #166238 -
Flags: review+
Assignee | ||
Comment 39•20 years ago
|
||
> I think I'd prefer if you created a new nsAttrValue here and properly called > mAttrsAndChildren.SetAndTakeAttr rather then the const-cast evilness. This > isn't performance critical in any way since it will hardly ever be ran, so no > need to optimize too much. I'll do whichever approach you think is best, but this will still require const casting the this pointer since its a const function. (Just to be sure I tried it and it was a compiler error.) > Please add a comment like 'make sure to do this before calling AddAttributes' Good idea, done.
Something like: nsAttrValue newVal; newVal.ParseAtom(attrVal->GetStringValue()); nsIAtom* res = newVal->GetAtomValue(); mAttrsAndChildren.SetAndTakeAttr(IDName, newVal); return res; Should work without casts, no?
oooh, now i see, GetID is a const function... Eh, either way then.
Assignee | ||
Comment 42•20 years ago
|
||
I left the code the same because const casting the this pointer was awkard. The patch just adds the comment asked for in the review.
Attachment #166238 -
Attachment is obsolete: true
Attachment #166533 -
Flags: review?(bugmail)
Comment on attachment 166533 [details] [diff] [review] GetID_deCOM_v5 you don't really need to resubit a new patch for a small change like that. Much less ask for a new review.
Attachment #166533 -
Flags: review?(bugmail) → review+
Attachment #166533 -
Flags: superreview?(bzbarsky)
Reporter | ||
Comment 44•20 years ago
|
||
Comment on attachment 166533 [details] [diff] [review] GetID_deCOM_v5 >Index: content/base/src/nsGenericElement.cpp >+nsGenericElement::GetID() const >+ nsAutoString idVal; >+ idVal.Assign(attrVal->GetStringValue()); I think I'd prefer: nsAutoString idVal(attrVal->GetStringValue()); >Index: content/svg/content/src/nsSVGElement.cpp >+ else if(aName == nsSVGAtoms::id && aNamespaceID == Space between "if" and "(", please. sr=bzbarsky with those changes; if you can attach a patch with them, I'll check it in once the tree reopens. Thanks a ton for doing this!
Attachment #166533 -
Flags: superreview?(bzbarsky) → superreview+
Assignee | ||
Comment 45•20 years ago
|
||
Fixed super review comments
Attachment #166533 -
Attachment is obsolete: true
Reporter | ||
Updated•20 years ago
|
Assignee: general → jlurz24
Reporter | ||
Comment 46•20 years ago
|
||
Fix checked in for 1.8a6. This helped out Tdhtml some, and may have helped Tp (hard to tell so far).
Status: NEW → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
You need to log in
before you can comment on or make changes to this bug.
Description
•