diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp
index cf1b56c61447..ab9d06929fd3 100644
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -700,6 +700,52 @@ nsresult HTMLEditRules::AfterEditInner() {
return NS_OK;
}
+EditActionResult HTMLEditor::CanHandleHTMLEditSubAction() const {
+ MOZ_ASSERT(IsEditActionDataAvailable());
+
+ if (NS_WARN_IF(Destroyed())) {
+ return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
+ }
+
+ // If there is not selection ranges, we should ignore the result.
+ if (!SelectionRefPtr()->RangeCount()) {
+ return EditActionCanceled();
+ }
+
+ nsRange* range = SelectionRefPtr()->GetRangeAt(0);
+ nsINode* selStartNode = range->GetStartContainer();
+ if (NS_WARN_IF(!selStartNode)) {
+ return EditActionResult(NS_ERROR_FAILURE);
+ }
+
+ if (!IsModifiableNode(*selStartNode)) {
+ return EditActionCanceled();
+ }
+
+ nsINode* selEndNode = range->GetEndContainer();
+ if (NS_WARN_IF(!selEndNode)) {
+ return EditActionResult(NS_ERROR_FAILURE);
+ }
+
+ if (selStartNode == selEndNode) {
+ return EditActionIgnored();
+ }
+
+ if (!IsModifiableNode(*selEndNode)) {
+ return EditActionCanceled();
+ }
+
+ nsINode* commonAncestor = range->GetCommonAncestor();
+ if (NS_WARN_IF(!commonAncestor)) {
+ return EditActionResult(NS_ERROR_FAILURE);
+ }
+ if (!IsModifiableNode(*commonAncestor)) {
+ return EditActionCanceled();
+ }
+
+ return EditActionIgnored();
+}
+
nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
bool* aHandled) {
if (NS_WARN_IF(!CanHandleEditAction())) {
@@ -720,43 +766,20 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
AutoSafeEditorData setData(*this, *mHTMLEditor);
- // Nothing to do if there's no selection to act on
- if (NS_WARN_IF(!SelectionRefPtr()->RangeCount())) {
- return NS_OK;
+ EditActionResult result = HTMLEditorRef().CanHandleHTMLEditSubAction();
+ if (NS_WARN_IF(result.Failed())) {
+ return result.Rv();
}
-
- RefPtr range = SelectionRefPtr()->GetRangeAt(0);
- nsCOMPtr selStartNode = range->GetStartContainer();
- if (NS_WARN_IF(!selStartNode)) {
- return NS_ERROR_FAILURE;
- }
-
- if (!HTMLEditorRef().IsModifiableNode(*selStartNode)) {
+ if (result.Canceled()) {
+ if (!SelectionRefPtr()->RangeCount()) {
+ // Special case. If there is no selection range, we do nothing here,
+ // but return as not handled nor canceled.
+ return NS_OK;
+ }
*aCancel = true;
return NS_OK;
}
- nsCOMPtr selEndNode = range->GetEndContainer();
- if (NS_WARN_IF(!selEndNode)) {
- return NS_ERROR_FAILURE;
- }
-
- if (selStartNode != selEndNode) {
- if (!HTMLEditorRef().IsModifiableNode(*selEndNode)) {
- *aCancel = true;
- return NS_OK;
- }
-
- nsINode* commonAncestor = range->GetCommonAncestor();
- if (NS_WARN_IF(!commonAncestor)) {
- return NS_ERROR_FAILURE;
- }
- if (!HTMLEditorRef().IsModifiableNode(*commonAncestor)) {
- *aCancel = true;
- return NS_OK;
- }
- }
-
switch (aInfo.mEditSubAction) {
case EditSubAction::eInsertText:
case EditSubAction::eInsertTextComingFromIME:
@@ -791,8 +814,6 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
return WillRemoveAbsolutePosition(aCancel, aHandled);
case EditSubAction::eSetOrClearAlignment:
return WillAlign(*aInfo.alignType, aCancel, aHandled);
- case EditSubAction::eCreateOrRemoveBlock:
- return WillMakeBasicBlock(*aInfo.blockType, aCancel, aHandled);
case EditSubAction::eRemoveList: {
nsresult rv = WillRemoveList(aCancel, aHandled);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
@@ -820,6 +841,7 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
return WillRelativeChangeZIndex(-1, aCancel, aHandled);
case EditSubAction::eIncreaseZIndex:
return WillRelativeChangeZIndex(1, aCancel, aHandled);
+ case EditSubAction::eCreateOrRemoveBlock:
case EditSubAction::eInsertHTMLSource:
case EditSubAction::eUndo:
case EditSubAction::eRedo:
@@ -1848,11 +1870,11 @@ EditActionResult HTMLEditRules::WillInsertParagraphSeparator() {
// Insert a new block first
MOZ_ASSERT(separator == ParagraphSeparator::div ||
separator == ParagraphSeparator::p);
- // FormatBlockContainer() creates AutoSelectionRestorer.
+ // FormatBlockContainerWithTransaction() creates AutoSelectionRestorer.
// Therefore, even if it returns NS_OK, editor might have been destroyed
// at restoring Selection.
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
- .FormatBlockContainer(
+ .FormatBlockContainerWithTransaction(
MOZ_KnownLive(ParagraphSeparatorElement(separator)));
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
NS_WARN_IF(!CanHandleEditAction())) {
@@ -1860,8 +1882,9 @@ EditActionResult HTMLEditRules::WillInsertParagraphSeparator() {
}
// We warn on failure, but don't handle it, because it might be harmless.
// Instead we just check that a new block was actually created.
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
- "HTMLEditor::FormatBlockContainer() failed");
+ NS_WARNING_ASSERTION(
+ NS_SUCCEEDED(rv),
+ "HTMLEditor::FormatBlockContainerWithTransaction() failed");
firstRange = SelectionRefPtr()->GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) {
@@ -1890,10 +1913,10 @@ EditActionResult HTMLEditRules::WillInsertParagraphSeparator() {
// Now, mNewBlockElement is last created block element for wrapping inline
// elements around the caret position and AfterEditInner() will move
// caret into it. However, it may be different from block parent of
- // the caret position. E.g., FormatBlockContainer() may wrap following
- // inline elements of a
element which is next sibling of container
- // of the caret. So, we need to adjust mNewBlockElement here for avoiding
- // jumping caret to odd position.
+ // the caret position. E.g., FormatBlockContainerWithTransaction() may
+ // wrap following inline elements of a
element which is next sibling
+ // of container of the caret. So, we need to adjust mNewBlockElement here
+ // for avoiding jumping caret to odd position.
HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement =
blockParent;
}
@@ -4535,37 +4558,7 @@ nsresult HTMLEditRules::WillMakeDefListItem(const nsAString* aItemType,
return NS_OK;
}
-nsresult HTMLEditRules::WillMakeBasicBlock(const nsAString& aBlockType,
- bool* aCancel, bool* aHandled) {
- MOZ_ASSERT(IsEditorDataAvailable());
- MOZ_ASSERT(aCancel && aHandled);
-
- OwningNonNull blockType = NS_Atomize(aBlockType);
-
- // FYI: Ignore cancel result of WillInsert().
- nsresult rv = MOZ_KnownLive(HTMLEditorRef()).WillInsert();
- if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
- return NS_ERROR_EDITOR_DESTROYED;
- }
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
-
- *aCancel = false;
- *aHandled = true;
-
- // FormatBlockContainer() creates AutoSelectionRestorer.
- // Therefore, even if it returns NS_OK, editor might have been destroyed
- // at restoring Selection.
- rv = MOZ_KnownLive(HTMLEditorRef()).FormatBlockContainer(blockType);
- if (NS_WARN_IF(!CanHandleEditAction())) {
- return NS_ERROR_EDITOR_DESTROYED;
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
-}
-
-nsresult HTMLEditor::FormatBlockContainer(nsAtom& blockType) {
+nsresult HTMLEditor::FormatBlockContainerWithTransaction(nsAtom& blockType) {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
if (!SelectionRefPtr()->IsCollapsed()) {
diff --git a/editor/libeditor/HTMLEditRules.h b/editor/libeditor/HTMLEditRules.h
index 781bac3e113f..0c0ddafdb0d1 100644
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -366,18 +366,6 @@ class HTMLEditRules : public TextEditRules {
bool aEntireList, bool* aCancel,
bool* aHandled);
- /**
- * WillMakeBasicBlock() called before changing block style around Selection.
- * This method actually does something with calling FormatBlockContainer().
- *
- * @param aBlockType Necessary block style as string.
- * @param aCancel Returns true if the operation is canceled.
- * @param aHandled Returns true if the edit action is handled.
- */
- MOZ_CAN_RUN_SCRIPT
- MOZ_MUST_USE nsresult WillMakeBasicBlock(const nsAString& aBlockType,
- bool* aCancel, bool* aHandled);
-
/**
* Called after creating a basic block, indenting, outdenting or aligning
* contents. This method inserts a padding
element for empty last line
diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp
index 75a63440a1b5..ea44054ea6f3 100644
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1798,9 +1798,9 @@ nsresult HTMLEditor::SetParagraphFormatAsAction(
"MakeDefinitionListItemWithTransaction() failed");
return EditorBase::ToGenericNSResult(rv);
}
- nsresult rv = InsertBasicBlockWithTransaction(*tagName);
+ nsresult rv = FormatBlockContainerAsSubAction(*tagName);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
- "InsertBasicBlockWithTransaction() failed");
+ "FormatBlockContainerAsSubAction() failed");
return EditorBase::ToGenericNSResult(rv);
}
@@ -2264,7 +2264,7 @@ nsresult HTMLEditor::MakeDefinitionListItemWithTransaction(nsAtom& aTagName) {
return NS_OK;
}
-nsresult HTMLEditor::InsertBasicBlockWithTransaction(nsAtom& aTagName) {
+nsresult HTMLEditor::FormatBlockContainerAsSubAction(nsAtom& aTagName) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (!mRules) {
@@ -2276,75 +2276,37 @@ nsresult HTMLEditor::InsertBasicBlockWithTransaction(nsAtom& aTagName) {
// Protect the edit rules object from dying
RefPtr rules(mRules);
- bool cancel, handled;
-
AutoPlaceholderBatch treatAsOneTransaction(*this);
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eCreateOrRemoveBlock, nsIEditor::eNext);
- nsDependentAtomString tagName(&aTagName);
- EditSubActionInfo subActionInfo(EditSubAction::eCreateOrRemoveBlock);
- subActionInfo.blockType = &tagName;
- nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
- if (cancel || NS_FAILED(rv)) {
+ EditActionResult result = CanHandleHTMLEditSubAction();
+ if (result.Canceled() || NS_WARN_IF(result.Failed())) {
+ return result.Rv();
+ }
+
+ // FYI: Ignore cancel result of WillInsert() since we've already checked
+ // whether we can or cannot edit current selection range.
+ nsresult rv = WillInsert();
+ if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed, but ignored");
+
+ // FormatBlockContainerWithTransaction() creates AutoSelectionRestorer.
+ // Therefore, even if it returns NS_OK, editor might have been destroyed
+ // at restoring Selection.
+ rv = FormatBlockContainerWithTransaction(aTagName);
+ if (NS_WARN_IF(Destroyed())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- if (!handled && SelectionRefPtr()->IsCollapsed()) {
- nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
- if (NS_WARN_IF(!firstRange)) {
- return NS_ERROR_FAILURE;
- }
-
- EditorDOMPoint atStartOfSelection(firstRange->StartRef());
- if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
- NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
- return NS_ERROR_FAILURE;
- }
-
- // Have to find a place to put the block.
- EditorDOMPoint pointToInsertBlock(atStartOfSelection);
-
- while (!CanContainTag(*pointToInsertBlock.GetContainer(), aTagName)) {
- pointToInsertBlock.Set(pointToInsertBlock.GetContainer());
- if (NS_WARN_IF(!pointToInsertBlock.IsSet()) ||
- NS_WARN_IF(!pointToInsertBlock.GetContainerAsContent())) {
- return NS_ERROR_FAILURE;
- }
- }
-
- if (pointToInsertBlock.GetContainer() !=
- atStartOfSelection.GetContainer()) {
- // We need to split up to the child of the point to insert a block.
- SplitNodeResult splitBlockResult = SplitNodeDeepWithTransaction(
- MOZ_KnownLive(*pointToInsertBlock.GetChild()), atStartOfSelection,
- SplitAtEdges::eAllowToCreateEmptyContainer);
- if (NS_WARN_IF(splitBlockResult.Failed())) {
- return splitBlockResult.Rv();
- }
- pointToInsertBlock = splitBlockResult.SplitPoint();
- if (NS_WARN_IF(!pointToInsertBlock.IsSet())) {
- return NS_ERROR_FAILURE;
- }
- }
-
- // Create a block and insert it before the right node if we split some
- // parents of start of selection above, or just start of selection
- // otherwise.
- RefPtr newBlock =
- CreateNodeWithTransaction(aTagName, pointToInsertBlock);
- if (NS_WARN_IF(!newBlock)) {
- return NS_ERROR_FAILURE;
- }
-
- // reposition selection to inside the block
- ErrorResult error;
- SelectionRefPtr()->Collapse(RawRangeBoundary(newBlock, 0), error);
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- }
-
+ nsDependentAtomString tagName(&aTagName);
+ EditSubActionInfo subActionInfo(EditSubAction::eCreateOrRemoveBlock);
+ subActionInfo.blockType = &tagName;
rv = rules->DidDoAction(subActionInfo, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h
index 5cdc89ea26cf..01087be45b44 100644
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -1108,6 +1108,17 @@ class HTMLEditor final : public TextEditor,
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult OnModifyDocument();
protected: // edit sub-action handler
+ /**
+ * CanHandleHTMLEditSubAction() checks whether there is at least one
+ * selection range or not, and whether the first range is editable.
+ * If it's not editable, `Canceled()` of the result returns true.
+ * If `Selection` is in odd situation, returns an error.
+ *
+ * XXX I think that `IsSelectionEditable()` is better name, but it's already
+ * in `EditorBase`...
+ */
+ EditActionResult CanHandleHTMLEditSubAction() const;
+
/**
* Called before inserting something into the editor.
* This method may removes mPaddingBRElementForEmptyEditor if there is.
@@ -1696,8 +1707,9 @@ class HTMLEditor final : public TextEditor,
nsTArray>& aNodeArray, nsAtom& aBlockTag);
/**
- * FormatBlockContainer() is implementation of "formatBlock" command of
- * `Document.execCommand()`. This applies block style or removes it.
+ * FormatBlockContainerWithTransaction() is implementation of "formatBlock"
+ * command of `Document.execCommand()`. This applies block style or removes
+ * it.
* NOTE: This creates AutoSelectionRestorer. Therefore, even when this
* return NS_OK, editor may have been destroyed.
*
@@ -1711,7 +1723,7 @@ class HTMLEditor final : public TextEditor,
* called.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
- FormatBlockContainer(nsAtom& aBlockType);
+ FormatBlockContainerWithTransaction(nsAtom& aBlockType);
/**
* InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() determines if
@@ -2855,14 +2867,14 @@ class HTMLEditor final : public TextEditor,
nsresult MakeDefinitionListItemWithTransaction(nsAtom& aTagName);
/**
- * InsertBasicBlockWithTransaction() inserts a block element whose name
- * is aTagName at selection.
+ * FormatBlockContainerAsSubAction() inserts a block element whose name
+ * is aTagName at selection. If selection is not collapsed and aTagName is
+ * nsGkAtoms::normal or nsGkAtoms::_empty, this removes block containers.
*
* @param aTagName A block level element name. Must NOT be
* nsGkAtoms::dt nor nsGkAtoms::dd.
*/
- MOZ_CAN_RUN_SCRIPT
- nsresult InsertBasicBlockWithTransaction(nsAtom& aTagName);
+ MOZ_CAN_RUN_SCRIPT nsresult FormatBlockContainerAsSubAction(nsAtom& aTagName);
/**
* Increase/decrease the font size of selection.