Bug 1460509 - part 58: Make HTMLEditRules::WillAlign() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the editor r?m_kato
MozReview-Commit-ID: L6rjqZABqsI
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -5666,25 +5666,41 @@ HTMLEditRules::WillAlign(const nsAString
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
rv = NormalizeSelection();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
+
+ *aHandled = true;
+ rv = AlignContentsAtSelection(aAlignType);
+ if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
+ NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+nsresult
+HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType)
+{
AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
// Convert the selection ranges into "promoted" selection ranges: This
// basically just expands the range to include the immediate block parent,
// and then further expands to include any ancestors whose children are all
// in the range
- *aHandled = true;
nsTArray<OwningNonNull<nsINode>> nodeArray;
- rv = GetNodesFromSelection(EditAction::align, nodeArray, TouchContent::yes);
+ nsresult rv =
+ GetNodesFromSelection(EditAction::align, nodeArray, TouchContent::yes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// If we don't have any nodes, or we have only a single br, then we are
// creating an empty alignment div. We have to do some different things for
// these.
bool emptyDiv = nodeArray.IsEmpty();
@@ -5761,46 +5777,55 @@ HTMLEditRules::WillAlign(const nsAString
nsCOMPtr<nsIContent> sibling;
if (pointToInsertDiv.GetChild()) {
sibling =
HTMLEditorRef().GetNextHTMLSibling(pointToInsertDiv.GetChild());
}
if (sibling && !IsBlockNode(*sibling)) {
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertDiv);
rv = HTMLEditorRef().DeleteNodeWithTransaction(*brContent);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
}
RefPtr<Element> div =
HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
pointToInsertDiv);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(!div)) {
return NS_ERROR_FAILURE;
}
// Remember our new block for postprocessing
mNewBlock = div;
// Set up the alignment on the div, using HTML or CSS
rv = AlignBlock(*div, aAlignType, ResetAlignOf::OnlyDescendants);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
- *aHandled = true;
// Put in a moz-br so that it won't get deleted
CreateElementResult createMozBrResult =
CreateMozBR(EditorRawDOMPoint(div, 0));
if (NS_WARN_IF(createMozBrResult.Failed())) {
return createMozBrResult.Rv();
}
EditorRawDOMPoint atStartOfDiv(div, 0);
+ // Don't restore the selection
+ selectionRestorer.Abort();
ErrorResult error;
SelectionRef().Collapse(atStartOfDiv, error);
- // Don't restore the selection
- selectionRestorer.Abort();
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ error.SuppressException();
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
// Next we detect all the transitions in the array, where a transition
// means that adjacent nodes in the array don't have the same parent.
@@ -5863,16 +5888,19 @@ HTMLEditRules::WillAlign(const nsAString
rv = RemoveAlignment(*curNode, aAlignType, true);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (useCSS) {
HTMLEditorRef().mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
curNode->AsElement(), nullptr,
nsGkAtoms::align, &aAlignType, false);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
curDiv = nullptr;
continue;
}
if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
// If we don't use CSS, add a content to list element: they have to
// be inside another list, i.e., >= second level of nesting.
// XXX AlignInnerBlocks() handles list item elements and table cells.
// Is it intentional to change alignment of nested other type
@@ -5900,32 +5928,38 @@ HTMLEditRules::WillAlign(const nsAString
SplitNodeResult splitNodeResult =
MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div, atCurNode);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
curDiv =
HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(!curDiv)) {
return NS_ERROR_FAILURE;
}
// Remember our new block for postprocessing
mNewBlock = curDiv;
// Set up the alignment on the div
rv = AlignBlock(*curDiv, aAlignType, ResetAlignOf::OnlyDescendants);
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to align the <div>");
}
// Tuck the node into the end of the active div
rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
*curDiv);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -326,16 +326,26 @@ protected:
const nsAString* aBulletType,
bool* aCancel, bool* aHandled,
const nsAString* aItemType = nullptr);
nsresult WillRemoveList(bool aOrdered, bool* aCancel, bool* aHandled);
nsresult WillIndent(bool* aCancel, bool* aHandled);
nsresult WillCSSIndent(bool* aCancel, bool* aHandled);
nsresult WillHTMLIndent(bool* aCancel, bool* aHandled);
nsresult WillOutdent(bool* aCancel, bool* aHandled);
+
+ /**
+ * Called before aligning contents around Selection. This method actually
+ * sets align attributes to align contents.
+ *
+ * @param aAlignType New align attribute value where the contents
+ * should be aligned to.
+ * @param aCancel Returns true if the operation is canceled.
+ * @param aHandled Returns true if the edit action is handled.
+ */
nsresult WillAlign(const nsAString& aAlignType,
bool* aCancel, bool* aHandled);
/**
* Called before changing absolute positioned element to static positioned.
* This method actually changes the position property of nearest absolute
* positioned element. Therefore, this might cause destroying the HTML
* editor.
@@ -421,16 +431,30 @@ protected:
* @param aNode The node whose contents should be aligned
* to aAlignType.
* @param aAlignType New value of align attribute of <div> which
* is only child of aNode.
*/
MOZ_MUST_USE nsresult
AlignBlockContents(nsINode& aNode, const nsAString& aAlignType);
+ /**
+ * AlignContentsAtSelection() aligns contents around Selection to aAlignType.
+ * This creates AutoSelectionRestorer. Therefore, even if this returns
+ * NS_OK, CanHandleEditAction() may return false if the editor is destroyed
+ * during restoring the Selection. So, every caller needs to check if
+ * CanHandleEditAction() returns true before modifying the DOM tree or
+ * changing Selection.
+ *
+ * @param aAlignType New align attribute value where the contents
+ * should be aligned to.
+ */
+ MOZ_MUST_USE nsresult
+ AlignContentsAtSelection(const nsAString& aAlignType);
+
nsresult AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
nsINode* aNode);
nsresult GetFormatString(nsINode* aNode, nsAString &outFormat);
enum class Lists { no, yes };
enum class Tables { no, yes };
void GetInnerContent(nsINode& aNode,
nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
int32_t* aIndex, Lists aLists = Lists::yes,