RESOLVED FIXED 140999
Removing text node does not remove its associated markers
https://bugs.webkit.org/show_bug.cgi?id=140999
Summary Removing text node does not remove its associated markers
Grzegorz Czajkowski
Reported 2015-01-28 08:12:20 PST
Clearing text on HTMLTextAreaElement and HTMLInputElement does not remove its previous markers. For example, running the following script two times will cause that Document stores two markers (one for current node and one for previous which was remved!). <script> textArea.value = ""; textArea.focus(); document.execCommand("InsertText", false, "welllcome "); </script> document().markers().showMarkers() 2 nodes have markers: 0x7f31b5122dc0 1:[0:9](0) 0x7f31b5122d70 1:[0:9](0) This happens when text to insert is empty and setInnerTextValue("") calls removeChildren() on the previous node. It does not affect user as the absolete node has been removed and its markers not drawn. However does it make sense to store it until document's detach?
Attachments
Patch (2.46 KB, patch)
2015-01-28 08:17 PST, Grzegorz Czajkowski
no flags
test_case_1, input.value = "" does not clear markers (825 bytes, text/html)
2015-01-30 02:57 PST, Grzegorz Czajkowski
no flags
Patch (2.94 KB, patch)
2015-01-30 03:03 PST, Grzegorz Czajkowski
no flags
apply Chris' comments + add async bits (6.69 KB, patch)
2015-02-02 05:50 PST, Grzegorz Czajkowski
no flags
Apply Chris' comments (round 2) and let removeMarkersAync remove specific DocumentMarkers (7.17 KB, patch)
2015-02-03 05:44 PST, Grzegorz Czajkowski
no flags
test_case_2, removing text node does not clear markers (763 bytes, text/html)
2015-02-13 03:19 PST, Grzegorz Czajkowski
no flags
Apply Ryosuke's comments (3.65 KB, patch)
2015-02-13 07:53 PST, Grzegorz Czajkowski
rniwa: review+
Grzegorz Czajkowski
Comment 1 2015-01-28 08:17:49 PST
Darin Adler
Comment 2 2015-01-28 09:04:53 PST
Comment on attachment 245544 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=245544&action=review > Source/WebCore/dom/ContainerNodeAlgorithms.h:242 > + if (is<Text>(node)) > + node.document().markers().removeMarkers(&node); This is in a really hot code path; I don’t think we want to do this in this way. Lets find a way that is more efficient.
Grzegorz Czajkowski
Comment 3 2015-01-28 10:45:04 PST
Comment on attachment 245544 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=245544&action=review >> Source/WebCore/dom/ContainerNodeAlgorithms.h:242 >> + node.document().markers().removeMarkers(&node); > > This is in a really hot code path; I don’t think we want to do this in this way. Lets find a way that is more efficient. Right. I'll try to propose a new approach. Thanks!
Grzegorz Czajkowski
Comment 4 2015-01-29 08:10:59 PST
I was playing with this bug a bit and it turned out that it's reproducible for contenteditable as well, for example: <script> div.focus(); document.execCommand("InsertText", false, "welllcome "); div.innerHTML = ""; // after that we still store markers for "welllcome" in DocumentMakerController </script> Since the root cause is the same (we don't remove markers while children are being removed) I'll suggest removing them somewhere inside ContatinerNode::removeChildren().
Grzegorz Czajkowski
Comment 5 2015-01-30 02:57:51 PST
Created attachment 245704 [details] test_case_1, input.value = "" does not clear markers
Grzegorz Czajkowski
Comment 6 2015-01-30 03:03:41 PST
Chris Dumez
Comment 7 2015-01-30 11:19:46 PST
Comment on attachment 245705 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=245705&action=review > Source/WebCore/dom/ContainerNode.cpp:664 > + if (is<Text>(removedChild)) Special-casing Text here seems weird. Also, I am a bit worried about the performance impact of this change. Wouldn't it be possible to override "virtual void removedFrom(ContainerNode& insertionPoint);" in Text class and call removeMarkers() there instead? It would be much nicer if Text handling was in Text class instead of ContainerNode.
Grzegorz Czajkowski
Comment 8 2015-02-02 03:06:52 PST
Comment on attachment 245705 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=245705&action=review >> Source/WebCore/dom/ContainerNode.cpp:664 >> + if (is<Text>(removedChild)) > > Special-casing Text here seems weird. Also, I am a bit worried about the performance impact of this change. Wouldn't it be possible to override "virtual void removedFrom(ContainerNode& insertionPoint);" in Text class and call removeMarkers() there instead? It would be much nicer if Text handling was in Text class instead of ContainerNode. Moving this to Text::removedFrom(ContainerNode) seems like a good idea. Thanks! Additionally, I'll try to add some async bits to make it non-blocking.
Grzegorz Czajkowski
Comment 9 2015-02-02 05:50:36 PST
Created attachment 245871 [details] apply Chris' comments + add async bits
Chris Dumez
Comment 10 2015-02-02 10:07:30 PST
Comment on attachment 245871 [details] apply Chris' comments + add async bits View in context: https://bugs.webkit.org/attachment.cgi?id=245871&action=review > Source/WebCore/dom/DocumentMarkerController.h:81 > + void willRemoveAllMarkersFrom(Node*); Why doesn't this take a reference? > Source/WebCore/dom/DocumentMarkerController.h:113 > + void removeMarkersFromRemovedNodeTimerFired(); We usually put private methods before the private data members, not after. > Source/WebCore/dom/Text.cpp:200 > + document().markers().willRemoveAllMarkersFrom(this); I am not familiar with these markers and I cannot cannot if doing things asynchronously makes sense here so I'll let someone else review. However, I'll say that willRemoveAllMarkersFrom() naming is confusing as it doesn't tell me that it actually removes the markers asynchronously. We probably want something like removeMarkersAsync(). > Source/WebCore/dom/Text.cpp:201 > + Node::removedFrom(removalPoint); CharacterData:: as this is the parent class. Even if CharacterData doesn't override this function now, it could later. > Source/WebCore/dom/Text.h:53 > + virtual void removedFrom(ContainerNode& removalPoint) override final; Should probably be protected?
Grzegorz Czajkowski
Comment 11 2015-02-02 11:51:11 PST
(In reply to comment #10) > Comment on attachment 245871 [details] > apply Chris' comments + add async bits > > View in context: > https://bugs.webkit.org/attachment.cgi?id=245871&action=review > > > Source/WebCore/dom/DocumentMarkerController.h:81 > > + void willRemoveAllMarkersFrom(Node*); > > Why doesn't this take a reference? > > > Source/WebCore/dom/DocumentMarkerController.h:113 > > + void removeMarkersFromRemovedNodeTimerFired(); > > We usually put private methods before the private data members, not after. > > > Source/WebCore/dom/Text.cpp:200 > > + document().markers().willRemoveAllMarkersFrom(this); > > I am not familiar with these markers and I cannot cannot if doing things > asynchronously makes sense here so I'll let someone else review. However, > I'll say that willRemoveAllMarkersFrom() naming is confusing as it doesn't > tell me that it actually removes the markers asynchronously. We probably > want something like removeMarkersAsync(). > > > Source/WebCore/dom/Text.cpp:201 > > + Node::removedFrom(removalPoint); > > CharacterData:: as this is the parent class. Even if CharacterData doesn't > override this function now, it could later. > > > Source/WebCore/dom/Text.h:53 > > + virtual void removedFrom(ContainerNode& removalPoint) override final; > > Should probably be protected? Thanks for comprehensive review! I'll take all the comments into consideration.
Grzegorz Czajkowski
Comment 12 2015-02-03 05:44:56 PST
Created attachment 245933 [details] Apply Chris' comments (round 2) and let removeMarkersAync remove specific DocumentMarkers
Chris Dumez
Comment 13 2015-02-03 19:13:59 PST
I'll let Darin or Ryosuke review as I am not familiar with these document markers.
Grzegorz Czajkowski
Comment 14 2015-02-04 11:56:09 PST
ping reviewers this is a friendly request for review. Thanks
Ryosuke Niwa
Comment 15 2015-02-12 02:39:27 PST
Comment on attachment 245933 [details] Apply Chris' comments (round 2) and let removeMarkersAync remove specific DocumentMarkers View in context: https://bugs.webkit.org/attachment.cgi?id=245933&action=review > Source/WebCore/dom/Text.cpp:203 > +void Text::removedFrom(ContainerNode& removalPoint) > +{ > + document().markers().removeMarkersAsync(*this); > + CharacterData::removedFrom(removalPoint); > +} > + I don't think it makes sense to add Text::removedFrom just to remove markers. We should be doing this in either one of Document::nodeWillBeRemoved, Document::nodeChildrenWillBeRemoved, or Document::updateRangesAfterChildrenChanged instead. And I don't think making only this one code path async make sense. If anything, we should check hasMarkers() before calling removeMarkers or make all of Document::textInserted, Document::textRemoved, etc… async as well.
Ryosuke Niwa
Comment 16 2015-02-12 02:41:57 PST
Comment on attachment 245933 [details] Apply Chris' comments (round 2) and let removeMarkersAync remove specific DocumentMarkers View in context: https://bugs.webkit.org/attachment.cgi?id=245933&action=review > Source/WebCore/dom/DocumentMarkerController.cpp:474 > + m_removeMarkersTimerData.first = &node; > + m_removeMarkersTimerData.second = markerTypes; I don't think this would work if two different nodes were removed in a single task.
Grzegorz Czajkowski
Comment 17 2015-02-13 03:19:44 PST
Created attachment 246515 [details] test_case_2, removing text node does not clear markers I was playing with this bug a bit and found out that we don't remove markers on div.removeChild(div.firstChild);
Grzegorz Czajkowski
Comment 18 2015-02-13 03:21:00 PST
Comment on attachment 245933 [details] Apply Chris' comments (round 2) and let removeMarkersAync remove specific DocumentMarkers Clear review flag due to Ryosuke's review.
Grzegorz Czajkowski
Comment 19 2015-02-13 07:53:53 PST
Created attachment 246526 [details] Apply Ryosuke's comments
Ryosuke Niwa
Comment 20 2015-02-13 17:04:12 PST
Comment on attachment 246526 [details] Apply Ryosuke's comments View in context: https://bugs.webkit.org/attachment.cgi?id=246526&action=review > Source/WebCore/dom/Document.cpp:3713 > + if (is<Text>(n)) We should check m_markers->hasMarkers() here as well.
Grzegorz Czajkowski
Comment 21 2015-02-15 03:32:46 PST
(In reply to comment #20) > Comment on attachment 246526 [details] > Apply Ryosuke's comments > > View in context: > https://bugs.webkit.org/attachment.cgi?id=246526&action=review > > > Source/WebCore/dom/Document.cpp:3713 > > + if (is<Text>(n)) > > We should check m_markers->hasMarkers() here as well. Happy to see r+, thanks. I think we can skip it here as inside removeMarkers() we already check them, https://trac.webkit.org/browser/trunk/Source/WebCore/dom/DocumentMarkerController.cpp#L454
Grzegorz Czajkowski
Comment 22 2015-02-16 00:26:11 PST
Radar WebKit Bug Importer
Comment 23 2015-02-16 04:54:59 PST
Note You need to log in before you can comment on or make changes to this bug.