Bug 1574852 - part 94: Move `HTMLEditRules::PinSelectionToNewBlock()` to `HTMLEditor` r=m_kato

Differential Revision: https://phabricator.services.mozilla.com/D44798

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-09-11 09:44:39 +00:00
Родитель 90491197ac
Коммит 821d89d488
3 изменённых файлов: 74 добавлений и 86 удалений

Просмотреть файл

@ -618,14 +618,20 @@ nsresult HTMLEditRules::AfterEditInner() {
break;
}
// if we created a new block, make sure selection lands in it
if (HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement) {
rv = PinSelectionToNewBlock();
// If we created a new block, make sure caret is in it.
if (HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement &&
SelectionRefPtr()->IsCollapsed()) {
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
.EnsureCaretInBlockElement(
MOZ_KnownLive(*HTMLEditorRef()
.TopLevelEditSubActionDataRef()
.mNewBlockElement));
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to pin selection to the new block");
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EnsureSelectionInBlockElement() failed, but ignored");
}
// Adjust selection for insert text, html paste, and delete actions if
@ -1231,12 +1237,11 @@ nsresult HTMLEditRules::GetParagraphState(bool* aMixed, nsAString& outFormat) {
// we might have an empty node list. if so, find selection parent
// and put that on the list
if (arrayOfNodes.IsEmpty()) {
EditorRawDOMPoint selectionStartPoint(
EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
EditorRawDOMPoint atCaret(EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!atCaret.IsSet())) {
return NS_ERROR_FAILURE;
}
arrayOfNodes.AppendElement(*selectionStartPoint.GetContainer());
arrayOfNodes.AppendElement(*atCaret.GetContainer());
}
// remember root node
@ -3361,21 +3366,19 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
const EditorDOMPoint& aSelectionEndPoint) {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
EditorDOMPoint selectionStartPoint(aSelectionStartPoint);
EditorDOMPoint atCaret(aSelectionStartPoint);
EditorDOMPoint selectionEndPoint(aSelectionEndPoint);
// If we're handling D&D, this is called to delete dragging item from the
// tree. In this case, we should move parent blocks if it becomes empty.
if (GetEditAction() == EditAction::eDrop) {
MOZ_ASSERT(selectionStartPoint.GetContainer() ==
selectionEndPoint.GetContainer());
MOZ_ASSERT(selectionStartPoint.Offset() == selectionEndPoint.Offset());
MOZ_ASSERT(atCaret.GetContainer() == selectionEndPoint.GetContainer());
MOZ_ASSERT(atCaret.Offset() == selectionEndPoint.Offset());
{
AutoTrackDOMPoint startTracker(RangeUpdaterRef(), &selectionStartPoint);
AutoTrackDOMPoint startTracker(RangeUpdaterRef(), &atCaret);
AutoTrackDOMPoint endTracker(RangeUpdaterRef(), &selectionEndPoint);
nsresult rv =
DeleteParentBlocksWithTransactionIfEmpty(selectionStartPoint);
nsresult rv = DeleteParentBlocksWithTransactionIfEmpty(atCaret);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -3384,8 +3387,7 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
// If we removed parent blocks, Selection should be collapsed at where
// the most ancestor empty block has been.
if (TopLevelEditSubActionDataRef().mDidDeleteEmptyParentBlocks) {
nsresult rv =
SelectionRefPtr()->Collapse(selectionStartPoint.ToRawRangeBoundary());
nsresult rv = SelectionRefPtr()->Collapse(atCaret.ToRawRangeBoundary());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
@ -3396,11 +3398,11 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
// We might have left only collapsed whitespace in the start/end nodes
{
AutoTrackDOMPoint startTracker(RangeUpdaterRef(), &selectionStartPoint);
AutoTrackDOMPoint startTracker(RangeUpdaterRef(), &atCaret);
AutoTrackDOMPoint endTracker(RangeUpdaterRef(), &selectionEndPoint);
nsresult rv = DeleteNodeIfInvisibleAndEditableTextNode(
MOZ_KnownLive(*selectionStartPoint.GetContainer()));
MOZ_KnownLive(*atCaret.GetContainer()));
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
@ -3417,10 +3419,10 @@ nsresult HTMLEditor::DeleteUnnecessaryNodesAndCollapseSelection(
"remove end node, but ignored");
}
nsresult rv = SelectionRefPtr()->Collapse(
aDirectionAndAmount == nsIEditor::ePrevious
? selectionEndPoint.ToRawRangeBoundary()
: selectionStartPoint.ToRawRangeBoundary());
nsresult rv =
SelectionRefPtr()->Collapse(aDirectionAndAmount == nsIEditor::ePrevious
? selectionEndPoint.ToRawRangeBoundary()
: atCaret.ToRawRangeBoundary());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
@ -5025,12 +5027,11 @@ nsresult HTMLEditor::HandleCSSIndentAtSelectionInternal() {
// just sublist that <li>. This prevents bug 97797.
if (SelectionRefPtr()->IsCollapsed()) {
EditorRawDOMPoint selectionStartPoint(
EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
EditorRawDOMPoint atCaret(EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!atCaret.IsSet())) {
return NS_ERROR_FAILURE;
}
Element* block = GetBlock(*selectionStartPoint.GetContainer());
Element* block = GetBlock(*atCaret.GetContainer());
if (block && HTMLEditUtils::IsListItem(block)) {
arrayOfNodes.AppendElement(*block);
}
@ -9576,37 +9577,27 @@ nsresult HTMLEditor::InsertBRElementToEmptyListItemsAndTableCellsInRange(
return NS_OK;
}
nsresult HTMLEditRules::PinSelectionToNewBlock() {
MOZ_ASSERT(IsEditorDataAvailable());
nsresult HTMLEditor::EnsureCaretInBlockElement(Element& aElement) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(SelectionRefPtr()->IsCollapsed());
if (!SelectionRefPtr()->IsCollapsed()) {
return NS_OK;
}
if (NS_WARN_IF(
!HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement)) {
return NS_ERROR_NULL_POINTER;
}
EditorRawDOMPoint selectionStartPoint(
EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
EditorRawDOMPoint atCaret(EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!atCaret.IsSet())) {
return NS_ERROR_FAILURE;
}
// Use ranges and RangeUtils::CompareNodeToRange() to compare selection
// start to new block.
RefPtr<StaticRange> staticRange = StaticRange::Create(
selectionStartPoint.ToRawRangeBoundary(),
selectionStartPoint.ToRawRangeBoundary(), IgnoreErrors());
RefPtr<StaticRange> staticRange =
StaticRange::Create(atCaret.ToRawRangeBoundary(),
atCaret.ToRawRangeBoundary(), IgnoreErrors());
if (NS_WARN_IF(!staticRange)) {
return NS_ERROR_FAILURE;
}
bool nodeBefore, nodeAfter;
nsresult rv = RangeUtils::CompareNodeToRange(
HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement,
staticRange, &nodeBefore, &nodeAfter);
nsresult rv = RangeUtils::CompareNodeToRange(&aElement, staticRange,
&nodeBefore, &nodeAfter);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -9617,54 +9608,50 @@ nsresult HTMLEditRules::PinSelectionToNewBlock() {
if (nodeBefore) {
// selection is after block. put at end of block.
nsCOMPtr<nsIContent> tmp = HTMLEditorRef().GetLastEditableChild(
*HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement);
if (!tmp) {
tmp = HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement;
nsIContent* lastEditableContent = GetLastEditableChild(aElement);
if (!lastEditableContent) {
lastEditableContent = &aElement;
}
EditorRawDOMPoint endPoint;
if (EditorBase::IsTextNode(tmp) || HTMLEditorRef().IsContainer(tmp)) {
endPoint.SetToEndOf(tmp);
if (EditorBase::IsTextNode(lastEditableContent) ||
IsContainer(lastEditableContent)) {
endPoint.SetToEndOf(lastEditableContent);
} else {
endPoint.Set(tmp);
endPoint.Set(lastEditableContent);
if (NS_WARN_IF(!endPoint.AdvanceOffset())) {
return NS_ERROR_FAILURE;
}
}
ErrorResult error;
SelectionRefPtr()->Collapse(endPoint, error);
if (NS_WARN_IF(!CanHandleEditAction())) {
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
NS_WARNING_ASSERTION(!error.Failed(), "Selection::Collapse() failed");
return error.StealNSResult();
}
// selection is before block. put at start of block.
nsCOMPtr<nsIContent> tmp = HTMLEditorRef().GetFirstEditableChild(
*HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement);
if (!tmp) {
tmp = HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement;
nsIContent* firstEditableContent = GetFirstEditableChild(aElement);
if (!firstEditableContent) {
firstEditableContent = &aElement;
}
EditorRawDOMPoint atStartOfBlock;
if (EditorBase::IsTextNode(tmp) || HTMLEditorRef().IsContainer(tmp)) {
atStartOfBlock.Set(tmp);
if (EditorBase::IsTextNode(firstEditableContent) ||
IsContainer(firstEditableContent)) {
atStartOfBlock.Set(firstEditableContent);
} else {
atStartOfBlock.Set(tmp, 0);
atStartOfBlock.Set(firstEditableContent, 0);
}
ErrorResult error;
SelectionRefPtr()->Collapse(atStartOfBlock, error);
if (NS_WARN_IF(!CanHandleEditAction())) {
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
NS_WARNING_ASSERTION(!error.Failed(), "Selection::Collapse() failed");
return error.StealNSResult();
}
void HTMLEditor::SetSelectionInterlinePosition() {
@ -10320,9 +10307,8 @@ nsresult HTMLEditor::EnsureSelectionInBodyOrDocumentElement() {
return NS_ERROR_FAILURE;
}
EditorRawDOMPoint selectionStartPoint(
EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
EditorRawDOMPoint atCaret(EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!atCaret.IsSet())) {
return NS_ERROR_FAILURE;
}
@ -10334,7 +10320,7 @@ nsresult HTMLEditor::EnsureSelectionInBodyOrDocumentElement() {
// Check that selection start container is inside the <body> element.
// XXXsmaug this code is insane.
nsINode* temp = selectionStartPoint.GetContainer();
nsINode* temp = atCaret.GetContainer();
while (temp && !temp->IsHTMLElement(nsGkAtoms::body)) {
temp = temp->GetParentOrShadowHostNode();
}
@ -10784,14 +10770,14 @@ nsresult HTMLEditor::EnsureHardLineEndsWithLastChildOf(
Element& aRemovingContainerElement) {
MOZ_ASSERT(IsEditActionDataAvailable());
nsIContent* lastEditableChild =
nsIContent* firstEditableContent =
GetLastEditableChild(aRemovingContainerElement);
if (!lastEditableChild) {
if (!firstEditableContent) {
return NS_OK;
}
if (HTMLEditor::NodeIsBlockStatic(*lastEditableChild) ||
lastEditableChild->IsHTMLElement(nsGkAtoms::br)) {
if (HTMLEditor::NodeIsBlockStatic(*firstEditableContent) ||
firstEditableContent->IsHTMLElement(nsGkAtoms::br)) {
return NS_OK;
}

Просмотреть файл

@ -192,12 +192,6 @@ class HTMLEditRules : public TextEditRules {
nsresult GetParagraphFormatNodes(
nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
/**
* PinSelectionToNewBlock() may collapse Selection around mNewNode if it's
* necessary,
*/
MOZ_MUST_USE nsresult PinSelectionToNewBlock();
/**
* DocumentModifiedWorker() is called by DocumentModified() either
* synchronously or asynchronously.

Просмотреть файл

@ -2640,6 +2640,14 @@ class HTMLEditor final : public TextEditor,
*/
void SetSelectionInterlinePosition();
/**
* EnsureSelectionInBlockElement() may move caret into aElement or its
* parent block if caret is outside of them. Don't call this when
* `Selection` is not collapsed.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
EnsureCaretInBlockElement(dom::Element& aElement);
protected: // Called by helper classes.
virtual void OnStartToHandleTopLevelEditSubAction(
EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;