Bug 216327

Summary: Text replacements at the beginning of a second line are replaced too early
Product: WebKit Reporter: Megan Gardner <megan_gardner>
Component: New BugsAssignee: Megan Gardner <megan_gardner>
Status: RESOLVED FIXED    
Severity: Normal CC: darin, ews-watchlist, mifenton, thorton, webkit-bug-importer, wenson_hsieh
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on:    
Bug Blocks: 216450    
Attachments:
Description Flags
Patch
none
Patch
none
Patch
none
Patch
none
Patch
none
Patch
darin: review+
Patch none

Description Megan Gardner 2020-09-09 15:09:01 PDT
Overlapping text replacments at the beginning of a line are replaced too early.
Comment 1 Megan Gardner 2020-09-09 15:19:16 PDT
Created attachment 408371 [details]
Patch
Comment 2 Megan Gardner 2020-09-09 18:59:00 PDT
Created attachment 408395 [details]
Patch
Comment 3 Megan Gardner 2020-09-09 19:00:27 PDT
Created attachment 408396 [details]
Patch
Comment 4 Darin Adler 2020-09-09 20:09:16 PDT
Comment on attachment 408396 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=408396&action=review

> Source/WebCore/ChangeLog:12
> +        In the changes in r258871, using SimpleRanges instead of Range causing some side effects
> +        when the replacements at the beginning of lines. The ranges that we are counting are backwards
> +        and the return characters are being counted instead of being ignored. There is almost 
> +        certainly a better fix than this, but this patch restores the original logic that
> +        was present when Range was being used, until a better fix can be worked out. 

I appreciate you fixing this regression I caused, and I agree that we can do an expedient change to help buy us time to do a better one.

I’m guessing that the "real" fix might be to clamp the replacement range inside the paragraph when we create the TextCheckingHelper object.

> Source/WebCore/editing/TextCheckingHelper.cpp:223
> -uint64_t TextCheckingParagraph::automaticReplacementStart() const
> +uint64_t TextCheckingParagraph::automaticReplacementStart(bool oldBehaviour) const
>  {
> -    if (!m_automaticReplacementStart)
> -        m_automaticReplacementStart = characterCount({ paragraphRange().start, m_automaticReplacementRange.start });
> +    if (!m_automaticReplacementStart) {
> +
> +        auto ordering = documentOrder(paragraphRange().start, m_automaticReplacementRange.start);
> +        if (oldBehaviour && is_gt(ordering))
> +            m_automaticReplacementStart = characterCount({ m_automaticReplacementRange.start, m_automaticReplacementRange.start });
> +        else
> +            m_automaticReplacementStart = characterCount({ paragraphRange().start, m_automaticReplacementRange.start });
> +        
> +    }
>      return *m_automaticReplacementStart;
>  }

This isn’t OK, because the result is cached, so calling this once with oldBehavior true has a side effect on future calls. So if you want to make this two separate functions for now, that’s fine, but we need to either not cache the results from the "old behavior" versions (probably the right way to go) or cache the results separately.

> Source/WebCore/editing/TextCheckingHelper.cpp:235
> -uint64_t TextCheckingParagraph::automaticReplacementLength() const
> +uint64_t TextCheckingParagraph::automaticReplacementLength(bool oldBehaviour) const
>  {
> -    if (!m_automaticReplacementLength)
> -        m_automaticReplacementLength = characterCount(m_automaticReplacementRange);
> +    if (!m_automaticReplacementLength) {
> +        auto ordering = documentOrder(paragraphRange().start, m_automaticReplacementRange.start);
> +        if (oldBehaviour && is_gt(ordering))
> +            m_automaticReplacementLength = characterCount({ paragraphRange().start, m_automaticReplacementRange.end });
> +        else
> +            m_automaticReplacementLength = characterCount(m_automaticReplacementRange);
> +    }
>      return *m_automaticReplacementLength;
>  }

Same issue.
Comment 5 Wenson Hsieh 2020-09-10 09:38:26 PDT
<rdar://problem/68170353>
Comment 6 Megan Gardner 2020-09-10 10:29:48 PDT
Created attachment 408456 [details]
Patch
Comment 7 Megan Gardner 2020-09-10 13:48:39 PDT
Created attachment 408474 [details]
Patch
Comment 8 Megan Gardner 2020-09-10 14:35:18 PDT
Created attachment 408476 [details]
Patch
Comment 9 Darin Adler 2020-09-10 15:14:26 PDT
Comment on attachment 408476 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=408476&action=review

review+ assuming tests pass on all Mac platform EWS testers (I wasn’t patient enough)

> Source/WebCore/editing/Editor.cpp:2828
> -        } else if (resultEndLocation <= automaticReplacementEndLocation && resultEndLocation >= paragraph.automaticReplacementStart()
> +        } else if (automaticReplacementStartLocation <= resultEndLocation && resultEndLocation <= automaticReplacementEndLocation

Not sure why it’s important to reorder the two checks, here. Maybe we could have just added "true" on this line and then not done the other refactoring above. Just added true here, and added true above, without introducing the new local variable.

> Source/WebCore/editing/TextCheckingHelper.cpp:211
> +uint64_t TextCheckingParagraph::automaticReplacementStart(bool oldBehaviour) const

Hope this really is temporary: Boolean not explained at all in the header, spelling here is British English. Not clear what "old" means.

Likely none of that matters if we quickly find a more straightforward fix.

> Source/WebCore/editing/TextCheckingHelper.cpp:214
> +        return characterCount({ m_automaticReplacementRange.start, m_automaticReplacementRange.start });

This is just a way to write:

   return 0;

> LayoutTests/platform/ios/TestExpectations:130
>  # Requires support for testing text replacement
>  editing/spelling/text-replacement-after-typing-to-word.html [ Skip ]
> +editing/spelling/text-replacement-first-word-second-line.html [ Skip ]

Seems like these should be (eventually) moved into a directory so we can disable/enable them all at once if we add text replacement to a platform. I imagine we might want more than 2 tests, even.
Comment 10 Megan Gardner 2020-09-10 16:02:37 PDT
Comment on attachment 408476 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=408476&action=review

>> Source/WebCore/editing/Editor.cpp:2828
>> +        } else if (automaticReplacementStartLocation <= resultEndLocation && resultEndLocation <= automaticReplacementEndLocation
> 
> Not sure why it’s important to reorder the two checks, here. Maybe we could have just added "true" on this line and then not done the other refactoring above. Just added true here, and added true above, without introducing the new local variable.

I think this is significantly easier to read, both with the variable and the reordering.

>> Source/WebCore/editing/TextCheckingHelper.cpp:211
>> +uint64_t TextCheckingParagraph::automaticReplacementStart(bool oldBehaviour) const
> 
> Hope this really is temporary: Boolean not explained at all in the header, spelling here is British English. Not clear what "old" means.
> 
> Likely none of that matters if we quickly find a more straightforward fix.

I also hope we can find a better fix quickly.

>> Source/WebCore/editing/TextCheckingHelper.cpp:214
>> +        return characterCount({ m_automaticReplacementRange.start, m_automaticReplacementRange.start });
> 
> This is just a way to write:
> 
>    return 0;

of course, no need to actually call this.

>> LayoutTests/platform/ios/TestExpectations:130
>> +editing/spelling/text-replacement-first-word-second-line.html [ Skip ]
> 
> Seems like these should be (eventually) moved into a directory so we can disable/enable them all at once if we add text replacement to a platform. I imagine we might want more than 2 tests, even.

I will keep it in mind for later.
Comment 11 Megan Gardner 2020-09-10 16:03:36 PDT
Created attachment 408490 [details]
Patch
Comment 12 Darin Adler 2020-09-10 16:44:09 PDT
(In reply to Megan Gardner from comment #10)
> > Not sure why it’s important to reorder the two checks, here. Maybe we could have just added "true" on this line and then not done the other refactoring above. Just added true here, and added true above, without introducing the new local variable.
> 
> I think this is significantly easier to read, both with the variable and the
> reordering.

Reordering, OK. Variable, I don’t see how, it’s the same words, just removes the ().
Comment 13 EWS 2020-09-10 23:11:00 PDT
Committed r266909: <https://trac.webkit.org/changeset/266909>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 408490 [details].