REOPENED 30946
Extra layout on keypress after a space (problem with rebalanceWhitespaceAt in InsertTextCommand)
https://bugs.webkit.org/show_bug.cgi?id=30946
Summary Extra layout on keypress after a space (problem with rebalanceWhitespaceAt in...
Justin Garcia
Reported 2009-10-29 23:04:57 PDT
When a insert a character after a space, we do two layouts: insertTextIntoNode(textNode, offset, text); => setNeedsLayout rebalanceWhitespaceAt(startPosition); => replaceTextInNode => updateLayout then rebalanceWhitespaceAt(startPosition); => replaceTextInNode => setText => setNeedsLayout setEndingSelection(Selection(endingSelection().end(), endingSelection().affinity()))) => updateLayout I think we have two ways to go here: Make the decision about what we need to turn adjacent spaces into before we do any DOM operations (since this requires a fresh layout), and do whitespace modifications with all of the other DOM operations that the command performs. Turn all spaces into non-breaking spaces (I believe we can do this because of the non-breaking-space-mode:space we have on editable regions) and then turn them into patterns at serialization time. ojan Vafai <ojan@chromium.org>
Attachments
patch (21.47 KB, patch)
2009-11-18 12:52 PST, Justin Garcia
darin: review-
patch (27.92 KB, patch)
2009-11-20 16:08 PST, Justin Garcia
no flags
Patch2 (28.82 KB, patch)
2010-02-25 10:11 PST, Enrica Casucci
no flags
Patch3 (28.82 KB, patch)
2010-02-25 10:27 PST, Enrica Casucci
no flags
Justin Garcia
Comment 1 2009-10-29 23:20:03 PDT
Justin Garcia
Comment 2 2009-11-18 12:52:20 PST
Darin Adler
Comment 3 2009-11-19 11:51:37 PST
Comment on attachment 43452 [details] patch Looks pretty good. I have a few thoughts. I am concerned about copying the function named isWhitespace in two different source files. Can we put this into PlatformString.h or StringImpl.h instead? What's the right name for it to distinguish it from other functions of similar name? > +void InsertTextCommand::insertTextIntoNodeAndRebalanceWhitespace(const String& textToInsert, PassRefPtr<Text> node, unsigned offset) This function does not take ownership of the text node, so the argument type should be Text*, not PassRefPtr<Text>. > + unsigned whitespaceStart, whitespaceEnd; > + if (!extentOfWhitespaceAt(node.get(), offset, whitespaceStart, whitespaceEnd)) { > + > + VisiblePosition visiblePosition(Position(node.get(), offset)); > + String rebalancedText = stringWithRebalancedWhitespace(textToInsert, isStartOfParagraph(visiblePosition), isEndOfParagraph(visiblePosition)); > + > + insertTextIntoNode(node.get(), offset, rebalancedText); > + return; > + } There should not be a blank line after the if statement. > + > + ASSERT(whitespaceEnd >= whitespaceStart); > + unsigned length = whitespaceEnd - whitespaceStart + 1; Why the +1 here? It seems like the "end" value may be inclusive, which seems like a mistake. I can't tell by reading this. > + void insertTextIntoNodeAndRebalanceWhitespace(const String& text, PassRefPtr<Text> node, unsigned offset); As I said above, the type of the node should be Text*. The names "text" and "node" for the arguments are justified here because of the clash of type names with roles of the arguments. The argument names help make it clear. > +// Finds the extent of whitespace around a particular offset, as long as it is in a node with collapsable whitespace. The word "collapsible" is misspelled here. The comment could be clearer on what "as long as" means. There are multiple modes of collapsible whitespace. For example, pre-wrap collapses other whitespace, but not "\n". To handle those correctly, I think RenderStyle::isCollapsibleWhiteSpace needs to be used instead of a local isWhiteSpace function. > + if (node->length() == 0) > + return false; Do we really need a special case for empty text nodes? Is this a common case that needs performance optimization? Does the code below fail for empty text nodes? I'd like to see an assertion about offset being <= node->length() and also get a sense of whether callers really guarantee that or not. > + if (offset == text.length() || !isWhitespace(text[offset])) { Because of how String indexing works, I don't think you need the "offset == text.length" check here. Getting a character past the end returns 0, and that will return false from isWhitespace. > + if (offset == 0 || !isWhitespace(text[offset - 1])) > + // If neither text[offset] nor text[offset - 1] are some form of whitespace, no rebalancing is necessary. > + return false; Need braces around this. > + while (start > 0 && isWhitespace(text[start - 1])) > + start--; > + > + for (unsigned i = end + 1; i < text.length(); i++) { > + if (!isWhitespace(text[i])) > + break; > + end = i; > + } I don't understand why these loops are structured differently, since they both do the same thing. It would be slightly nicer to make them match. I don't understand how this function can possibly do the right thing if there are adjacent text nodes that also have whitespace in them. review- because at least some of my comments seem worth doing, especially the use of RenderStyle::isColllapsibleWhiteSpace I'd like to see more test cases -- I worry that we are making performance faster but correctness suffer in various edge cases.
Justin Garcia
Comment 4 2009-11-20 16:08:40 PST
Created attachment 43622 [details] patch > I am concerned about copying the function named isWhitespace in two different > source files. Fixed, it didn't need to be in InsertTextCommand at all. > Can we put this into PlatformString.h or StringImpl.h instead? > What's the right name for it to distinguish it from other functions of similar > name? It's a strange function, I'd rather leave it in htmlediting.cpp for now. I'm not sure that it needs to check for tabs and newlines, since those will be removed as insignificant whitespace before we get to whitespace rebalancing. But this is part of the old code that I'd like to address after. > > +void InsertTextCommand::insertTextIntoNodeAndRebalanceWhitespace(const String& textToInsert, PassRefPtr<Text> node, unsigned offset) > > This function does not take ownership of the text node, so the argument type > should be Text*, not PassRefPtr<Text>. OK. But when does a function take ownership of the node? insertTextIntoNode(...) takes in a PassRefPtr<Node>, even though it seems like it only calls a function that takes ownership of the node (the create function). > There should not be a blank line after the if statement. FIxed. > > + ASSERT(whitespaceEnd >= whitespaceStart); > > + unsigned length = whitespaceEnd - whitespaceStart + 1; > > Why the +1 here? It seems like the "end" value may be inclusive, which seems > like a mistake. whitespaceEnd was strangely the offset *of* the last whitespace character, not the offset after the last whitespace character, thus the plus 1. Fixed this. > > +// Finds the extent of whitespace around a particular offset, as long as it is in a node with collapsable whitespace. > > The word "collapsible" is misspelled here. > > The comment could be clearer on what "as long as" means. > > There are multiple modes of collapsible whitespace. For example, pre-wrap > collapses other whitespace, but not "\n". To handle those correctly, I think > RenderStyle::isCollapsibleWhiteSpace needs to be used instead of a local > isWhiteSpace function. The comment now reads: "Find the extent of whitespace around a particular offset. If whitespace characters (' ') are preserved, then we don't do whitespace rebalancing and this function returns false." and the function name is "extentOfWhitespaceForRebalancingAt". > Do we really need a special case for empty text nodes? Is this a common case > that needs performance optimization? Does the code below fail for empty text > nodes? We don't need it, I'll remove it. > I'd like to see an assertion about offset being <= node->length() and also get > a sense of whether callers really guarantee that or not. They can't. They get their offsets from VisiblePositions, which can technically have invalid offsets. > > + if (offset == text.length() || !isWhitespace(text[offset])) { > > Because of how String indexing works, I don't think you need the "offset == > text.length" check here. Getting a character past the end returns 0, and that > will return false from isWhitespace. Seems strange to rely on this detail, and no index checking would look odd since it looks like straight array indexing at first glance. > > + while (start > 0 && isWhitespace(text[start - 1])) > > + start--; > > + > > + for (unsigned i = end + 1; i < text.length(); i++) { > > + if (!isWhitespace(text[i])) > > + break; > > + end = i; > > + } > > I don't understand why these loops are structured differently, since they both > do the same thing. It would be slightly nicer to make them match. Fixed. > I'd like to see more test cases -- I worry that we are making performance > faster but correctness suffer in various edge cases. Added a few test cases that insert content in and around multiple spaces.
Enrica Casucci
Comment 5 2010-02-25 10:11:12 PST
Created attachment 49506 [details] Patch2 I took the updated patch from Justin that addresses Darin's comments and merge it.
WebKit Review Bot
Comment 6 2010-02-25 10:15:44 PST
Attachment 49506 [details] did not pass style-queue: Failed to run "WebKitTools/Scripts/check-webkit-style" exit_code: 1 WebCore/editing/htmlediting.cpp:447: Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons. [readability/comparison_to_zero] [5] Total errors found: 1 in 28 files If any of these errors are false positives, please file a bug against check-webkit-style.
Enrica Casucci
Comment 7 2010-02-25 10:27:36 PST
Created attachment 49510 [details] Patch3 Fixed style issue.
Enrica Casucci
Comment 8 2010-02-25 17:01:26 PST
Committed revision 55263.
Csaba Osztrogonác
Comment 10 2010-02-26 00:43:28 PST
I had to roll out r55263 to make buildbots and commit bot happier: http://trac.webkit.org/changeset/55271 .
Csaba Osztrogonác
Comment 11 2010-02-26 03:24:06 PST
Enrica Casucci
Comment 12 2010-02-26 09:38:31 PST
(In reply to comment #11) > The patch broke fast/events/key-events-in-input-text.html too: > > http://build.webkit.org/results/SnowLeopard%20Intel%20Release%20%28Tests%29/r55270%20%286022%29/fast/events/key-events-in-input-text-pretty-diff.html I'm aware of the problem and I'm preparing a patch to fix the failing tests.
Eric Seidel (no email)
Comment 13 2010-05-17 01:46:19 PDT
I'm unsure what the status of this bug is. It looks like this got landed and then rolled out. Presumably it was landed again? Should this bug be closed?
Ryosuke Niwa
Comment 14 2011-01-17 11:23:14 PST
(In reply to comment #13) > I'm unsure what the status of this bug is. It looks like this got landed and then rolled out. Presumably it was landed again? Should this bug be closed? I don't think it was landed again as far as http://trac.webkit.org/browser/trunk/Source/WebCore/editing/CompositeEditCommand.cpp?annotate=blame&rev=75835 tells me.
Darin Adler
Comment 15 2011-06-18 11:26:09 PDT
Comment on attachment 49510 [details] Patch3 Bug is still open. Need to find a way to fix this.
Note You need to log in before you can comment on or make changes to this bug.