Caret position gets lost (moved to end) when textarea frame is reconstructed, after deleting the first character
Categories
(Core :: DOM: Selection, defect, P2)
Tracking
()
People
(Reporter: dholbert, Assigned: saschanaz, NeedInfo)
References
(Regression)
Details
(Keywords: regression, webcompat:platform-bug)
User Story
user-impact-score:300
Attachments
(5 files)
|
659 bytes,
text/html
|
Details | |
|
260.06 KB,
video/mp4
|
Details | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-beta+
|
Details | Review |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-esr140+
|
Details | Review |
STR:
- Load attached testcase.
- Click the very start of the textarea, and press "Delete" on your keyboard to delete that first character.
(Alternately, you can click just after that first character and press Backspace; that has the same outcome for me.) - Watch the caret (vertical bar) very carefully, when the textarea changes to cyan.
ACTUAL RESULTS:
Caret jumps to the end of the textarea.
EXPECTED RESULTS:
Caret should not jump.
If I repeat the STR but I delete a character in the middle of the text, then the caret does not jump. So there's something special about the situation when the caret is at the very start here.
(I'm not sure offhand if this belongs in layout or DOM:Editor; I don't know offhand where we save the caret/selection state to restore after frame reconstruction. Starting in DOM:Editor but feel free to reclassify as-appropriate.)
| Reporter | ||
Comment 1•20 days ago
|
||
Comment 2•20 days ago
|
||
DOM: Selection?
But also we should try to do better than reframing for white-space changes...
| Reporter | ||
Updated•20 days ago
|
| Reporter | ||
Comment 3•20 days ago
|
||
(In reply to Emilio Cobos Álvarez [:emilio] from comment #2)
But also we should try to do better than reframing for white-space changes...
Fair point. That's somewhat orthogonal to the caret-position-getting-lost-when-we-do-have-to-reframe [for whatever reason], though, so I spun off bug 1993374 on that.
| Reporter | ||
Updated•20 days ago
|
| Reporter | ||
Comment 4•20 days ago
|
||
Regression range:
INFO: Last good revision: 7a6d6b986a1ed263db6d19ecbd56e2f4210870cf (2020-12-10)
INFO: First bad revision: 1130661c79c222fb1acd29b7ec5dc5202cdd0d2d (2020-12-11)
INFO: Pushlog:
https://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=7a6d6b986a1ed263db6d19ecbd56e2f4210870cf&tochange=1130661c79c222fb1acd29b7ec5dc5202cdd0d2d
Suspected regressor (only one to mention selection):
https://hg-edge.mozilla.org/mozilla-central/rev/2a7e04756c232eb775140500ef2d370fa608eb50
Kagami Sascha Rosylight — Bug 1681615 - Detect selection changes through the setters r=masayuki
saschanaz, could you take a look here? This is causing a papercut on google.com (see bug 1985376).
Comment 5•20 days ago
|
||
Set release status flags based on info from the regressing bug 1681615
Updated•19 days ago
|
| Assignee | ||
Comment 6•19 days ago
|
||
xul.dll!mozilla::TextControlState::SelectionProperties::SetIsDirty() Line 382 (d:\firefox\obj-aarch64-pc-windows-msvc\dist\include\mozilla\TextControlState.h:382)
xul.dll!mozilla::TextControlState::SetValueWithoutTextEditor(mozilla::AutoTextControlHandlingState & aHandlingSetValue) Line 2975 (d:\firefox\dom\html\TextControlState.cpp:2975)
xul.dll!mozilla::TextControlState::SetValue(const nsTSubstring<char16_t> & aValue, const nsTSubstring<char16_t> * aOldValue, const mozilla::EnumSet<mozilla::TextControlState::ValueSetterOption,unsigned int> & aOptions) Line 2658 (d:\firefox\dom\html\TextControlState.cpp:2658)
xul.dll!mozilla::TextControlState::SetValue(const nsTSubstring<char16_t> & aValue, const mozilla::EnumSet<mozilla::TextControlState::ValueSetterOption,unsigned int> & aOptions) Line 288 (d:\firefox\obj-aarch64-pc-windows-msvc\dist\include\mozilla\TextControlState.h:288)
xul.dll!mozilla::TextControlState::UnbindFromFrame(nsTextControlFrame * aFrame) Line 2441 (d:\firefox\dom\html\TextControlState.cpp:2441)
xul.dll!mozilla::dom::HTMLTextAreaElement::UnbindFromFrame(nsTextControlFrame * aFrame) Line 218 (d:\firefox\dom\html\HTMLTextAreaElement.cpp:218)
xul.dll!nsTextControlFrame::Destroy(mozilla::FrameDestroyContext & aContext) Line 130 (d:\firefox\layout\forms\nsTextControlFrame.cpp:130)
xul.dll!nsBlockFrame::DoRemoveFrame(mozilla::FrameDestroyContext & aContext, nsIFrame * aDeletedFrame, unsigned int aFlags) Line 7285 (d:\firefox\layout\generic\nsBlockFrame.cpp:7285)
xul.dll!nsBlockFrame::RemoveFrame(mozilla::FrameDestroyContext & aContext, mozilla::FrameChildListID aListID, nsIFrame * aOldFrame) Line 6438 (d:\firefox\layout\generic\nsBlockFrame.cpp:6438)
xul.dll!nsFrameManager::RemoveFrame(mozilla::FrameDestroyContext & aContext, mozilla::FrameChildListID aListID, nsIFrame * aOldFrame) Line 123 (d:\firefox\layout\base\nsFrameManager.cpp:123)
xul.dll!nsCSSFrameConstructor::ContentWillBeRemoved(nsIContent * aChild, nsCSSFrameConstructor::RemoveFlags aFlags) Line 7560 (d:\firefox\layout\base\nsCSSFrameConstructor.cpp:7560)
xul.dll!nsCSSFrameConstructor::RecreateFramesForContent(nsIContent * aContent, nsCSSFrameConstructor::InsertionKind aInsertionKind) Line 8476 (d:\firefox\layout\base\nsCSSFrameConstructor.cpp:8476)
xul.dll!mozilla::RestyleManager::ProcessRestyledFrames(nsStyleChangeList & aChangeList) Line 1687 (d:\firefox\layout\style\RestyleManager.cpp:1687)
xul.dll!mozilla::RestyleManager::DoProcessPendingRestyles(mozilla::ServoTraversalFlags aFlags) Line 3301 (d:\firefox\layout\style\RestyleManager.cpp:3301)
xul.dll!mozilla::RestyleManager::ProcessPendingRestyles() Line 3390 (d:\firefox\layout\style\RestyleManager.cpp:3390)
xul.dll!mozilla::PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) Line 4622 (d:\firefox\layout\base\PresShell.cpp:4622)
xul.dll!mozilla::PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aType) Line 1512 (d:\firefox\obj-aarch64-pc-windows-msvc\dist\include\mozilla\PresShell.h:1512)
xul.dll!mozilla::dom::Document::DetermineProximityToViewportAndNotifyResizeObservers() Line 18576 (d:\firefox\dom\base\Document.cpp:18576)
xul.dll!nsRefreshDriver::Tick::<lambda>(mozilla::dom::Document & aDoc) Line 2515 (d:\firefox\layout\base\nsRefreshDriver.cpp:2515)
xul.dll!nsRefreshDriver::RunRenderingPhase<`lambda at D:\firefox\layout\base\nsRefreshDriver.cpp:2512:7'>::<lambda>() Line 1316 (d:\firefox\layout\base\nsRefreshDriver.cpp:1316)
The layout change calls TextControlState::UnbindFromFrame and then ultimately TextControlState::SetValueWithoutTextEditor, which internally branches based on if (!mValue.Equals(aHandlingSetValue.GetSettingValue())).
If I tap arrow-up key instead of delete in the repro, mValue doesn't change, which makes it enter the no-change branch, where it calls SetIsDirty. And later RestoreSelectionState::Run() checks the dirty state and calls mFrame->SetSelectionRange.
What's different with delete is that mValue does change, which makes in enter another branch, which does not set the mIsDirty bit because literally no selection change happened. That makes mFrame->SetSelectionRange not being called, which surprisingly... puts the cursor at the end, instead of the start? Why?
Updated•19 days ago
|
Comment 7•18 days ago
|
||
Setting the cached selection as dirty when reframing (from UnbindFrame or so) makes sense to me fwiw. The DOM that's selected is going away after all. Does that work?
As for the start vs. end not sure, but we're recreating the anonymous dom so I wouldn't be surprised the handling gets a bit funky.
| Reporter | ||
Comment 8•18 days ago
|
||
(In reply to Kagami Rosylight [:saschanaz] (they/them) from comment #6)
That makes
mFrame->SetSelectionRangenot being called, which surprisingly... puts the cursor at the end, instead of the start? Why?
(In reply to Emilio Cobos Álvarez [:emilio] from comment #7)
As for the start vs. end not sure, but we're recreating the anonymous dom so I wouldn't be surprised the handling gets a bit funky.
As a literal/mechanical answer for "why does the cursor get placed at the end": I observed in a pernosco trace for the Google webcompat bug that it's because we reach this line:
https://searchfox.org/firefox-main/rev/ab5bf2401717970560597083d6ac72915bde2d89/editor/libeditor/EditorBase.cpp#3704
CollapseSelectionTo(EditorRawDOMPoint(&aTextNode, aString.Length()), error);
(That's creating a point at position aString.Length(), i.e. at the end of the string.)
That might not be precisely what you were asking, but I'm just noting it as a fact/breadcrumb in case it's useful.
| Reporter | ||
Comment 9•18 days ago
|
||
(It looks like we reach that line in a "good" pernosco run, too, though, so maybe it's just a question of how we use the resulting selection... not sure.)
In any case, if it's useful, here's a pernosco trace with this bug's reduced testcase, showing calls to nsCaret::CaretPositionFor, and focused on the first such call where we're requesting the position at the end rather than the start (aSelection->mOffset is 4 rather than 0):
https://pernos.co/debug/NNSdaVADx6L57dQ7Xt-GqQ/index.html#f{m[BTWK,CwmS_,t[2Q,ReY_,f{e[BTWK,CwmK_,s{ac7hLQAAA,bDfg,uGvWnIw,oGwZmew___/
Yep, EditorBase (TextEditor instance) collapse Selection to the end when a value is set. Then, TextControlState will adjust Selection if it's required. To make it run, TextControlState::UnbindFromFrame needs to mark the cached selection dirty because it needs to set new independent Selection to the same as cached last selection before the previous frame is destroyed.
I wonder, we need to manage whether we need to restore Selection and whether the selection has been changed with different flags?
| Assignee | ||
Comment 11•14 days ago
|
||
Thanks! For now let's see whether that works.
| Assignee | ||
Updated•14 days ago
|
Comment 12•14 days ago
|
||
Set release status flags based on info from the regressing bug 1681615
| Assignee | ||
Comment 13•14 days ago
|
||
Comment 14•13 days ago
|
||
Comment 16•13 days ago
|
||
Comment 17•13 days ago
|
||
Backed out for causing wpt failures @selection-after-whitespace-change.html.
| Assignee | ||
Comment 18•13 days ago
|
||
We ignore Home in macOS, even for webdriver? Huh.
(In reply to Kagami Rosylight [:saschanaz] (they/them) from comment #18)
We ignore Home in macOS, even for webdriver? Huh.
Ah, IIRC, Home, End keys on macOS are initialized by NativeKeyBindings and we don't have shortcut key definition. Let me check it...
Shortcut keys are not defined for Home and End on macOS and Linux.
https://searchfox.org/firefox-main/rev/04f343bf04ce512276a27788ef21f06e995b9496/dom/events/ShortcutKeyDefinitions.cpp#110-131
Hmm, but NativeKeyBindings initialize that for tests...
https://searchfox.org/firefox-main/rev/04f343bf04ce512276a27788ef21f06e995b9496/widget/cocoa/NativeKeyBindings.mm#528-549
Ah, they are just scroll to start/end instead of moving caret.
https://searchfox.org/firefox-main/rev/04f343bf04ce512276a27788ef21f06e995b9496/widget/cocoa/NativeKeyBindings.mm#181-182
| Assignee | ||
Comment 23•13 days ago
|
||
On Chrome it works though, is it a potential webcompat issue by itself? 🤔
Yeah, could be, but our strategy is that we should prefer the same behavior as the native apps on the platform. So, we should change when the users complain about the same behavior as the native apps.
| Assignee | ||
Comment 26•12 days ago
|
||
Safari only scrolls it. Chrome does the same, but if it's single-line text control then it falls back to the line start/end navigation.
Comment 27•12 days ago
|
||
Comment 29•11 days ago
|
||
| bugherder | ||
Comment 31•11 days ago
|
||
The patch landed in nightly and beta is affected.
:saschanaz, is this bug important enough to require an uplift?
- If yes, please nominate the patch for beta approval.
- See https://wiki.mozilla.org/Release_Management/Requesting_an_Uplift for documentation on how to request an uplift.
- If no, please set
status-firefox145towontfix.
For more information, please visit BugBot documentation.
Comment 32•11 days ago
|
||
firefox-beta Uplift Approval Request
- User impact if declined: Users of google.com get unexpected text cursor behavior
- Code covered by automated testing: yes
- Fix verified in Nightly: yes
- Needs manual QE test: yes
- Steps to reproduce for manual QE testing: See comment #0
- Risk associated with taking this patch: low
- Explanation of risk level: Limited behavior change in unbound frame case
- String changes made/needed: No
- Is Android affected?: yes
| Assignee | ||
Comment 33•11 days ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D268527
Comment 34•11 days ago
|
||
firefox-esr140 Uplift Approval Request
- User impact if declined: Unexpected text cursor behavior in google.com
- Code covered by automated testing: yes
- Fix verified in Nightly: yes
- Needs manual QE test: yes
- Steps to reproduce for manual QE testing: See comment #0
- Risk associated with taking this patch: low
- Explanation of risk level: Limited behavior change in unbound frame case
- String changes made/needed: No
- Is Android affected?: yes
| Assignee | ||
Comment 35•11 days ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D268527
| Assignee | ||
Updated•11 days ago
|
Updated•8 days ago
|
Comment 36•8 days ago
|
||
Verified fixed using Nightly 146.0a1 (20251019211057) on Windows 10, MacOS 15 and Ubuntu 24.04.
Updated•8 days ago
|
Updated•7 days ago
|
Updated•7 days ago
|
Comment 37•7 days ago
|
||
| uplift | ||
Updated•10 hours ago
|
Comment 39•10 hours ago
|
||
Kagami, your patch does not apply cleanly to the ESR140 branch, could you update it please? Thanks
Description
•