WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
98921
MutationRecord addedNodes/removedNodes should never be null
https://bugs.webkit.org/show_bug.cgi?id=98921
Summary
MutationRecord addedNodes/removedNodes should never be null
Erik Arvidsson
Reported
2012-10-10 09:53:54 PDT
We currently return empty NodeLists for addedNodes/removedNodes in several places where we should return null. See
http://dom.spec.whatwg.org/#concept-node-insert
step 7 var div = document.createElement('div'); var observer = new JsMutationObserver(function() {}); observer.observe(div, { childList: true }); var a = document.createTextNode('a'); div.appendChild(a); var records = observer.takeRecords(); assert(records[0].removedNodes === null); There is one case of "removedNodes null" in the spec and two cases of "addedNodes null". Searching is your friend.
Attachments
Patch
(6.99 KB, patch)
2012-10-12 12:17 PDT
,
Adam Klein
no flags
Details
Formatted Diff
Diff
Patch
(8.64 KB, patch)
2012-12-07 13:06 PST
,
Adam Klein
no flags
Details
Formatted Diff
Diff
Patch
(8.56 KB, patch)
2012-12-07 14:28 PST
,
Adam Klein
no flags
Details
Formatted Diff
Diff
Patch for landing
(9.98 KB, patch)
2012-12-07 14:55 PST
,
Adam Klein
no flags
Details
Formatted Diff
Diff
Show Obsolete
(3)
View All
Add attachment
proposed patch, testcase, etc.
Adam Klein
Comment 1
2012-10-10 14:13:15 PDT
I think perhaps the spec should simply be updated to match WebKit and Gecko behavior for 'childList'-typed records. My memory is that the behavior here is intentional to avoid requiring a null-check: consumers of the API can simply walk over addedNodes/removedNodes blindly.
Erik Arvidsson
Comment 2
2012-10-10 14:49:05 PDT
Changing the spec is also acceptable.
Adam Klein
Comment 3
2012-10-11 11:56:48 PDT
Per
https://www.w3.org/Bugs/Public/show_bug.cgi?id=19463
, the spec has been updated. Updating this bug appropriately. The spec now states that addedNodes/removedNodes should always return non-null NodeLists, which will have items in them if appropriate.
Erik Arvidsson
Comment 4
2012-10-12 10:19:15 PDT
So we are broken for attribute and characterData
Adam Klein
Comment 5
2012-10-12 12:17:02 PDT
Created
attachment 168461
[details]
Patch
Erik Arvidsson
Comment 6
2012-10-12 12:22:05 PDT
Doesn't this patch return the same NodeList instance? var div = document.createElement('div'); var observer = new WebKitMutationObserver(function() {}); observer.observe(div, {attributes: true}); div.setAttribute('a', 'A'); var records = observer.takeRecords(); shouldBeTrue("records[0].addedNodes !== records[0].removedNodes");
Adam Klein
Comment 7
2012-10-12 12:25:29 PDT
(In reply to
comment #6
)
> Doesn't this patch return the same NodeList instance? > > var div = document.createElement('div'); > var observer = new WebKitMutationObserver(function() {}); > observer.observe(div, {attributes: true}); > div.setAttribute('a', 'A'); > var records = observer.takeRecords(); > shouldBeTrue("records[0].addedNodes !== records[0].removedNodes");
Yes, it does. Does this strike you as particularly problematic? These node lists are immutable, as are the properties on the records. What's the use case for ===/!== comparisons on MutationRecord.addedNodes/removedNodes?
Erik Arvidsson
Comment 8
2012-10-12 12:29:25 PDT
(In reply to
comment #7
)
> (In reply to
comment #6
) > > Doesn't this patch return the same NodeList instance? > > > > var div = document.createElement('div'); > > var observer = new WebKitMutationObserver(function() {}); > > observer.observe(div, {attributes: true}); > > div.setAttribute('a', 'A'); > > var records = observer.takeRecords(); > > shouldBeTrue("records[0].addedNodes !== records[0].removedNodes"); > > Yes, it does. Does this strike you as particularly problematic? These node lists are immutable, as are the properties on the records. What's the use case for ===/!== comparisons on MutationRecord.addedNodes/removedNodes?
It strikes me as odd. Another problem with this is that you are using static which means the instance will be shared between different domains. How does that work? Will the user get permission errors when the try to access this object? Can you add a test to make sure this works as expected?
Ryosuke Niwa
Comment 9
2012-11-03 18:30:47 PDT
I agree with arv here that reusing the same nod list object is odd. I wonder if we can add some binding level optimization where we can lazy-create zero-length NodeList efficiently...
Adam Klein
Comment 10
2012-11-20 10:51:14 PST
***
Bug 102825
has been marked as a duplicate of this bug. ***
Ryosuke Niwa
Comment 11
2012-12-03 13:37:34 PST
Comment on
attachment 168461
[details]
Patch I don’t think we should be using the same object. The behavior doesn’t match that of Firefox or the specification.
Adam Klein
Comment 12
2012-12-07 11:32:12 PST
Will pick this up again and do something more normal.
Adam Klein
Comment 13
2012-12-07 13:06:25 PST
Created
attachment 178260
[details]
Patch
Adam Klein
Comment 14
2012-12-07 13:06:42 PST
New patch creates new nodelists for each record, ptal.
Ryosuke Niwa
Comment 15
2012-12-07 14:00:05 PST
Comment on
attachment 178260
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=178260&action=review
> Source/WebCore/dom/MutationRecord.cpp:74 > +class EmptyNodeList : public NodeList { > public:
Do we really need to add this class?
> Source/WebCore/dom/MutationRecord.cpp:78 > + nodeList = adoptRef(new EmptyNodeList);
Why can’t we just instantiate StaticNodeList here? We’re creating these objects lazily anyway.
> Source/WebCore/dom/MutationRecord.cpp:110 > +class AttributesRecord : public EmptyChildListRecord { > +public:
It’s odd that AttributesRecord inherits from EmptyChildListRecord since AttributesRecords is NOT a EmptyChildListRecord.
> Source/WebCore/dom/MutationRecord.cpp:127 > +class CharacterDataRecord : public EmptyChildListRecord {
Ditto.
Adam Klein
Comment 16
2012-12-07 14:07:38 PST
Comment on
attachment 178260
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=178260&action=review
>> Source/WebCore/dom/MutationRecord.cpp:74 >> public: > > Do we really need to add this class?
What's the downside? It's about 5 lines of code (the lazy initialization code I'd need anyway).
>> Source/WebCore/dom/MutationRecord.cpp:78 >> + nodeList = adoptRef(new EmptyNodeList); > > Why can’t we just instantiate StaticNodeList here? We’re creating these objects lazily anyway.
See above, I'm not clear on why StaticNodeList is better for this. NodeList is already fully virtualized, so the only extra cost is the size of the code for EmptyNodeList (which I'd bet is pretty darn small).
>> Source/WebCore/dom/MutationRecord.cpp:110 >> +public: > > It’s odd that AttributesRecord inherits from EmptyChildListRecord since AttributesRecords is NOT a EmptyChildListRecord.
Maybe I named it poorly? AttributesRecord and CharacterDataRecord are both records-with-empty-childlists. What else would you say AttributesRecord and CharacterDataRecord have in common?
Ryosuke Niwa
Comment 17
2012-12-07 14:17:21 PST
Comment on
attachment 178260
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=178260&action=review
>>> Source/WebCore/dom/MutationRecord.cpp:74 >>> public: >> >> Do we really need to add this class? > > What's the downside? It's about 5 lines of code (the lazy initialization code I'd need anyway).
We shouldn’t be adding code unless it’s necessary. With that argument, we’ll end up adding all sorts of 5-line code, and soon, we’ll double the number of lines in our codebsae like we did in the past 3 years. I can be convinced that this is a good idea if there is a data that supports adding this class benefit the real world use case.
>>> Source/WebCore/dom/MutationRecord.cpp:110 >>> +public: >> >> It’s odd that AttributesRecord inherits from EmptyChildListRecord since AttributesRecords is NOT a EmptyChildListRecord. > > Maybe I named it poorly? AttributesRecord and CharacterDataRecord are both records-with-empty-childlists. What else would you say AttributesRecord and CharacterDataRecord have in common?
How about renaming it to RecordWithEmptyChildList?
Adam Klein
Comment 18
2012-12-07 14:28:45 PST
Created
attachment 178276
[details]
Patch
Adam Klein
Comment 19
2012-12-07 14:31:20 PST
Comment on
attachment 178260
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=178260&action=review
>>>> Source/WebCore/dom/MutationRecord.cpp:74 >>>> public: >>> >>> Do we really need to add this class? >> >> What's the downside? It's about 5 lines of code (the lazy initialization code I'd need anyway). > > We shouldn’t be adding code unless it’s necessary. With that argument, we’ll end up adding all sorts of 5-line code, and soon, we’ll double the number of lines in our codebsae like we did in the past 3 years. > I can be convinced that this is a good idea if there is a data that supports adding this class benefit the real world use case.
I certainly don't have any data for this, just trying to reduce the size of these objects which are effectively just sentinels by sizeof(Vector). The lazy-initialization may or may not help us if the pattern for code that uses these APIs is to always query addedNodes.length and removedNodes.length. I still think this code was unlikely to cause any real maintenance burden or add to the bloat of the codebase. But I've replaced with StaticNodeList as requested.
Ryosuke Niwa
Comment 20
2012-12-07 14:32:50 PST
Comment on
attachment 178276
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=178276&action=review
> Source/WebCore/dom/MutationRecord.cpp:92 > + Vector<RefPtr<Node> > emptyVector; > + nodeList = StaticNodeList::adopt(emptyVector);
You would add StaticNodeList::create() to avoid swap.
Adam Klein
Comment 21
2012-12-07 14:55:04 PST
Created
attachment 178286
[details]
Patch for landing
WebKit Review Bot
Comment 22
2012-12-07 15:44:46 PST
Comment on
attachment 178286
[details]
Patch for landing Clearing flags on attachment: 178286 Committed
r136996
: <
http://trac.webkit.org/changeset/136996
>
WebKit Review Bot
Comment 23
2012-12-07 15:44:51 PST
All reviewed patches have been landed. Closing bug.
Darin Adler
Comment 24
2012-12-08 13:19:23 PST
Comment on
attachment 178286
[details]
Patch for landing View in context:
https://bugs.webkit.org/attachment.cgi?id=178286&action=review
> Source/WebCore/dom/StaticNodeList.h:-43 > - // Adopts the contents of the nodes vector.
Why remove this comment?
Adam Klein
Comment 25
2012-12-08 14:22:21 PST
(In reply to
comment #24
)
> (From update of
attachment 178286
[details]
) > View in context:
https://bugs.webkit.org/attachment.cgi?id=178286&action=review
> > > Source/WebCore/dom/StaticNodeList.h:-43 > > - // Adopts the contents of the nodes vector. > > Why remove this comment?
I discussed this on IRC with rniwa, sorry it didn't end up on the bug: my thought was that now both the name ("adopt") and implementation (using swap()) are together, the comment seems redundant. rniwa agreed. If you think the comment is still useful I'd be happy to add it back.
Ojan Vafai
Comment 26
2012-12-10 17:00:13 PST
Comment on
attachment 178260
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=178260&action=review
>>>>> Source/WebCore/dom/MutationRecord.cpp:74 >>>>> public: >>>> >>>> Do we really need to add this class? >>> >>> What's the downside? It's about 5 lines of code (the lazy initialization code I'd need anyway). >> >> We shouldn’t be adding code unless it’s necessary. With that argument, we’ll end up adding all sorts of 5-line code, and soon, we’ll double the number of lines in our codebsae like we did in the past 3 years. >> I can be convinced that this is a good idea if there is a data that supports adding this class benefit the real world use case. > > I certainly don't have any data for this, just trying to reduce the size of these objects which are effectively just sentinels by sizeof(Vector). The lazy-initialization may or may not help us if the pattern for code that uses these APIs is to always query addedNodes.length and removedNodes.length. I still think this code was unlikely to cause any real maintenance burden or add to the bloat of the codebase. > > But I've replaced with StaticNodeList as requested.
For the record, I think the StaticNodeList approach that got committed is inferior in every way to the EmptyNodeList approach here. I don't think we need to justify every change with data. The EmptyNodeList approach is clearly avoiding significant memory churn. It wouldn't be hard to come up with an example page where it's problematic. A few lines of extra code here has effectively 0 cost.
Ryosuke Niwa
Comment 27
2012-12-10 17:18:34 PST
(In reply to
comment #26
)
> (From update of
attachment 178260
[details]
) > View in context:
https://bugs.webkit.org/attachment.cgi?id=178260&action=review
>
> For the record, I think the StaticNodeList approach that got committed is inferior in every way to the EmptyNodeList approach here. I don't think we need to justify every change with data. The EmptyNodeList approach is clearly avoiding significant memory churn. It wouldn't be hard to come up with an example page where it's problematic.
I disagree. The biggest problem is nobody would know to use EmptyNodeList when they're looking to use StaticNodeList. It's much better to optimize StaticNodeList::createEmpty not to require memory allocation because then people working on any client of StaticNodeList are likely to discover this optimization.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug