Cannot join <li> elements with typing Backspace at start of a list item
Categories
(Core :: DOM: Editor, defect)
Tracking
()
People
(Reporter: adrian, Unassigned)
References
(Blocks 1 open bug, )
Details
(Keywords: dupeme, testcase, Whiteboard: [h2review-noted])
User Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.107 Safari/535.1 Steps to reproduce: I have the following html loaded into Firefox: <html> <body> <ul contenteditable="true"> <li></li> <li>B</li> </ul> </body> </html> I put the caret into the first bullet and press [BACKSPACE] or [DELETE]. Actual results: For each [BACKSPACE] or [DELETE] a BR is created outside the contenteditable element: After a few strokes I have this html: <html> <body> <br _moz_dirty=""> <br _moz_dirty=""> <br _moz_dirty=""> <br _moz_dirty=""> <br _moz_dirty=""> <ul contenteditable="true"> <li></li> <li>B</li> </ul> </body> </html> The first bullet must be empty to produce this error. If there are any characters in the first bullet it behaves as expected. Expected results: The html outside a contenteditable should not be modified.
Comment 1•12 years ago
|
||
Reproducible back to first contenteditable supporting Version (Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.0.19) Gecko/2010031422 Firefox/3.0.19 ID:2010031422).
Comment 2•11 years ago
|
||
This might be slightly off-topic, but here's a way to show that backspace does not perform as expected within contentEditable=true list items. = Replicate = 1. Visit http://jsfiddle.net/sFSb8/ 2. Position cursor to start of second list item. 3. Press backspace. = Expected Results = The second list item is joined with the first list item. The third list item becomes the second list item, and so on down the line. = Actual Results = Backspace has no effect. = Work Around = 1. Position the cursor to the end of the first line. 2. Press delete (this will join the first line with second line, as expected). 3. Press enter (this will split the first line and second line, resulting in what *looks* like the starting state). 4. Press backspace (now it works, but only for joining the first and second lines, which have been modified). Firefox 26 for Ubuntu.
Updated•6 years ago
|
Comment 3•4 years ago
|
||
I don't see the <br>
elements anymore, but I cannot join the list items.
Comment 4•4 years ago
•
|
||
Items can be joined with Delete
but not with Backspace
. It can join with Backspace
if it has no previous text sibling.
Comment 5•4 years ago
•
|
||
I'm seeing that joining by Delete
calls HandleDeleteNonCollapsedSelection
while joining by Backspace
calls TryToJoinBlocksWithTransaction
, where the latter tries to join <li>
with its parent which is no-op.
xul.dll!mozilla::HTMLEditor::HandleDeleteNonCollapsedSelection(short aDirectionAndAmount, short aStripWrappers, mozilla::HTMLEditor::SelectionWasCollapsed aSelectionWasCollapsed) Line 2984 (c:\Users\Kagami\Documents\GitHub\gecko-dev\editor\libeditor\HTMLEditSubActionHandler.cpp:2984)
xul.dll!mozilla::HTMLEditor::HandleDeleteSelectionInternal(short aDirectionAndAmount, short aStripWrappers) Line 2378 (c:\Users\Kagami\Documents\GitHub\gecko-dev\editor\libeditor\HTMLEditSubActionHandler.cpp:2378)
xul.dll!mozilla::HTMLEditor::TryToJoinBlocksWithTransaction(nsIContent & aLeftContentInBlock, nsIContent & aRightContentInBlock) Line 3454 (c:\Users\Kagami\Documents\GitHub\gecko-dev\editor\libeditor\HTMLEditSubActionHandler.cpp:3454)
xul.dll!mozilla::HTMLEditor::HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(short aDirectionAndAmount, mozilla::dom::Element & aCurrentBlockElement, const mozilla::EditorDOMPointBase<nsCOMPtr<nsINode>,nsCOMPtr<nsIContent> > & aCaretPoint) Line 2960 (c:\Users\Kagami\Documents\GitHub\gecko-dev\editor\libeditor\HTMLEditSubActionHandler.cpp:2960)
xul.dll!mozilla::HTMLEditor::HandleDeleteAroundCollapsedSelection(short aDirectionAndAmount, short aStripWrappers) Line 2461 (c:\Users\Kagami\Documents\GitHub\gecko-dev\editor\libeditor\HTMLEditSubActionHandler.cpp:2461)
xul.dll!mozilla::HTMLEditor::HandleDeleteSelectionInternal(short aDirectionAndAmount, short aStripWrappers) Line 2370 (c:\Users\Kagami\Documents\GitHub\gecko-dev\editor\libeditor\HTMLEditSubActionHandler.cpp:2370)
Edit: That code path difference is because TextEditor::ExtendSelectionForDelete
extends selection for Delete
but not for Backspace
.
Comment 6•4 years ago
|
||
I tried modifying TextEditor::ExtendSelectionForDelete
to always call CharacterExtendForBackspace
for ePrevious
just as it does for eNext
. It fixes this bug, but with several unexpected failures on WPT (and also several new passes). Seems we should insert some new condition to call it, what do you think, Masayuki?
Try: https://treeherder.mozilla.org/#/jobs?repo=try&revision=5ec201f0cbd4bbd862b3b6d73a2fc9f42bf762aa
Comment 7•4 years ago
|
||
(In reply to Kagami :saschanaz from comment #5)
Edit: That code path difference is because
TextEditor::ExtendSelectionForDelete
extends selection forDelete
but not forBackspace
.
(In reply to Kagami :saschanaz from comment #6)
I tried modifying
TextEditor::ExtendSelectionForDelete
to always callCharacterExtendForBackspace
forePrevious
just as it does foreNext
. It fixes this bug, but with several unexpected failures on WPT (and also several new passes). Seems we should insert some new condition to call it, what do you think, Masayuki?Try: https://treeherder.mozilla.org/#/jobs?repo=try&revision=5ec201f0cbd4bbd862b3b6d73a2fc9f42bf762aa
The removing code must be required for various cases. This bug is Backspace
key handling when selection is collapsed and the caret is start of a node. So, I guess that you need to call CharacterExtendForBackspace()
only when insertionPoint.IsStartOfContainer()
is true.
Comment 8•4 years ago
|
||
So, I guess that you need to call
CharacterExtendForBackspace()
only wheninsertionPoint.IsStartOfContainer()
is true.
It works and I guess it's the right fix, but it still causes the same test failures, reviving Bug 200416 for example. Given that a similar bug still exists (Bug 1621592), maybe a fix here should include another fix for that bug.
Comment 9•4 years ago
|
||
Hmm, CharacterExtendForBackspace()
goes into nsIFrame::PeekOffset()
. Its current behavior does not match with editor's requirement (according to the unexpected failures), and changing it may break Selection API too (or may fix its bugs too, though). As far as I've checked, it's useful for editor only when insertionPoint
is middle of a text node.
Perhaps, in eNext
case, editor should stop using CharacterExtendForDelete()
unless selection is collapsed in start or middle of a text node. Then HTMLEditor::HandleDeleteCollapsedSelectionAtCurrentBlockBoundary()
should keep looking for nearest editing content which should be joined with current block.
Comment 10•4 years ago
•
|
||
AFAICT HandleDeleteCollapsedSelectionAtCurrentBlockBoundary
alone tries joining <li>
with the parent <ul>
instead of its sibling, and CharacterExtendForDelete
prevents that by also selecting whitespaces between them. So I think we need CharacterExtendForDelete
here.
Comment 11•4 years ago
|
||
(In reply to Kagami :saschanaz from comment #10)
AFAICT
HandleDeleteCollapsedSelectionAtCurrentBlockBoundary
alone tries joining<li>
with the parent<ul>
instead of its sibling, andCharacterExtendForDelete
prevents that by also selecting whitespaces between them. So I think we needCharacterExtendForDelete
here.
Do you mean that GetPreviousEditableHTMLNode()
and GetNextEditableHTMLNode()
may return block parent here? If so, I don't understand what's going on. They must return next/previous <li>
element or its first/last leaf node.
https://searchfox.org/mozilla-central/rev/278046367dab878316f60f0bd7f740cf73f3c447/editor/libeditor/EditorBase.cpp#3915,3919-3921,3926-3931,3933,3968
And also, TryToJoinBlocksWithTransaction()
must get 2 <li>
elements here:
https://searchfox.org/mozilla-central/rev/278046367dab878316f60f0bd7f740cf73f3c447/editor/libeditor/HTMLEditSubActionHandler.cpp#3463-3464
But we hit here?:
https://searchfox.org/mozilla-central/rev/278046367dab878316f60f0bd7f740cf73f3c447/editor/libeditor/HTMLEditSubActionHandler.cpp#3501-3505
Updated•4 years ago
|
Comment 12•4 years ago
•
|
||
Do you mean that GetPreviousEditableHTMLNode() and GetNextEditableHTMLNode() may return block parent here?
Both returns a text node, one from between two <li>
s and and another from the second <li>
. I'm not sure the former one is really "editable", as it's invisible, but nsINode::IsEditable() thinks it is as it's in contenteditable
.
And also, TryToJoinBlocksWithTransaction() must get 2 <li> elements here:
As the former text node is from the parent <ul>
, the result here is <ul>
and <li>
.
But we hit here?:
Thus, yes.
Comment 13•4 years ago
|
||
(In reply to Kagami :saschanaz from comment #12)
Do you mean that GetPreviousEditableHTMLNode() and GetNextEditableHTMLNode() may return block parent here?
Both returns a text node, one from between two
<li>
s and and another from the second<li>
. I'm not sure the former one is really "editable", as it's invisible, but nsINode::IsEditable() thinks it is as it's incontenteditable
.And also, TryToJoinBlocksWithTransaction() must get 2 <li> elements here:
As the former text node is from the parent
<ul>
, the result here is<ul>
and<li>
.
Ah, that's what the unexpected behavior of the utility methods!
Probably, EditorBase::FindNode()
should keep scanning next leaf node here when it meets a text node which is non-visible text node under <ul>
, <ol>
, <dl>
, <table>
, <thead>
, <tbody>
, <thead>
, <tr>
, etc (or all invisible text nodes? I'm not sure...). I guess that changing IsEditable()
in any class shouldn't be changed because if it returns false
, editor tries to keep it even when its conteainer is being removed.
Updated•4 years ago
|
Comment 14•3 years ago
|
||
Hasn't been active on this one. Still intend to take a look later, but please feel free to take this.
Updated•2 years ago
|
Description
•