Bug 1460509 - part 17: Make HTMLEditRules::WillAbsolutePosition() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the HTML editor r?m_kato
MozReview-Commit-ID: EGqb0hNpNnc
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -9964,30 +9964,54 @@ HTMLEditRules::WillAbsolutePosition(bool
mNewBlock = focusElement;
return NS_OK;
}
nsresult rv = NormalizeSelection();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
+
+ rv = PrepareToMakeElementAbsolutePosition(aHandled, address_of(mNewBlock));
+ // PrepareToMakeElementAbsolutePosition() may restore selection with
+ // AutoSelectionRestorer. Therefore, the editor might have already been
+ // destroyed now.
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+nsresult
+HTMLEditRules::PrepareToMakeElementAbsolutePosition(
+ bool* aHandled,
+ RefPtr<Element>* aTargetElement)
+{
+ MOZ_ASSERT(IsEditorDataAvailable());
+
+ MOZ_ASSERT(aHandled);
+ MOZ_ASSERT(aTargetElement);
+
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.
nsTArray<RefPtr<nsRange>> arrayOfRanges;
GetPromotedRanges(arrayOfRanges, EditAction::setAbsolutePosition);
// Use these ranges to contruct a list of nodes to act on.
nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
- rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
- EditAction::setAbsolutePosition);
+ nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
+ EditAction::setAbsolutePosition);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// If nothing visible in list, make an empty block
if (ListIsEmptyLine(arrayOfNodes)) {
nsRange* firstRange = SelectionRef().GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) {
@@ -10004,36 +10028,46 @@ HTMLEditRules::WillAbsolutePosition(bool
MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
atStartOfSelection);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
RefPtr<Element> positionedDiv =
HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(!positionedDiv)) {
return NS_ERROR_FAILURE;
}
// Remember our new block for postprocessing
- mNewBlock = positionedDiv;
+ *aTargetElement = positionedDiv;
// Delete anything that was in the list of nodes
while (!arrayOfNodes.IsEmpty()) {
OwningNonNull<nsINode> curNode = arrayOfNodes[0];
rv = HTMLEditorRef().DeleteNodeWithTransaction(*curNode);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
arrayOfNodes.RemoveElementAt(0);
}
// Put selection in new block
*aHandled = true;
+ // Don't restore the selection
+ selectionRestorer.Abort();
ErrorResult error;
SelectionRef().Collapse(RawRangeBoundary(positionedDiv, 0), 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;
}
// Okay, now go through all the nodes and put them in a blockquote, or
// whatever is appropriate. Woohoo!
@@ -10069,34 +10103,43 @@ HTMLEditRules::WillAbsolutePosition(bool
atCurNode);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
if (!curPositionedDiv) {
curPositionedDiv =
HTMLEditorRef().CreateNodeWithTransaction(
*nsGkAtoms::div, splitNodeResult.SplitPoint());
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
NS_WARNING_ASSERTION(curPositionedDiv,
"Failed to create current positioned div element");
- mNewBlock = curPositionedDiv;
+ *aTargetElement = curPositionedDiv;
}
EditorRawDOMPoint atEndOfCurPositionedDiv;
atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
curList =
HTMLEditorRef().CreateNodeWithTransaction(*containerName,
atEndOfCurPositionedDiv);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(!curList)) {
return NS_ERROR_FAILURE;
}
// curList is now the correct thing to put curNode in. Remember our
// new block for postprocessing.
}
// Tuck the node into the end of the active list
rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
*curList);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
continue;
}
// Not a list item, use blockquote? If we are inside a list item, we
// don't want to blockquote, we want to sublist the list item. We may
@@ -10130,66 +10173,81 @@ HTMLEditRules::WillAbsolutePosition(bool
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
if (!curPositionedDiv) {
EditorRawDOMPoint atListItemParent(atListItem.GetContainer());
curPositionedDiv =
HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
atListItemParent);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
NS_WARNING_ASSERTION(curPositionedDiv,
"Failed to create current positioned div element");
- mNewBlock = curPositionedDiv;
+ *aTargetElement = curPositionedDiv;
}
EditorRawDOMPoint atEndOfCurPositionedDiv;
atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
curList =
HTMLEditorRef().CreateNodeWithTransaction(*containerName,
atEndOfCurPositionedDiv);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(!curList)) {
return NS_ERROR_FAILURE;
}
}
rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*listItem, *curList);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Remember we indented this li
indentedLI = listItem;
continue;
}
// Need to make a div to put things in if we haven't already
if (!curPositionedDiv) {
if (curNode->IsHTMLElement(nsGkAtoms::div)) {
curPositionedDiv = curNode->AsElement();
- mNewBlock = curPositionedDiv;
+ *aTargetElement = curPositionedDiv;
curList = nullptr;
continue;
}
SplitNodeResult splitNodeResult =
MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
atCurNode);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
curPositionedDiv =
HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(!curPositionedDiv)) {
return NS_ERROR_FAILURE;
}
// Remember our new block for postprocessing
- mNewBlock = curPositionedDiv;
+ *aTargetElement = curPositionedDiv;
// curPositionedDiv is now the correct thing to put curNode in
}
// Tuck the node into the end of the active blockquote
rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
*curPositionedDiv);
+ if (NS_WARN_IF(!CanHandleEditAction())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Forget curList, if any
curList = nullptr;
}
return NS_OK;
}
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -253,17 +253,16 @@ protected:
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);
nsresult WillAlign(const nsAString& aAlignType,
bool* aCancel, bool* aHandled);
- nsresult WillAbsolutePosition(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.
*
* @param aCancel Returns true if the operation is canceled.
@@ -288,16 +287,42 @@ protected:
nsresult WillMakeDefListItem(const nsAString* aBlockType, bool aEntireList,
bool* aCancel, bool* aHandled);
nsresult WillMakeBasicBlock(const nsAString& aBlockType,
bool* aCancel, bool* aHandled);
nsresult MakeBasicBlock(nsAtom& aBlockType);
nsresult DidMakeBasicBlock(RulesInfo* aInfo, nsresult aResult);
/**
+ * Called before changing an element to absolute positioned.
+ * This method only prepares the operation since DidAbsolutePosition() will
+ * change it actually later. mNewBlock is set to the target element and
+ * if necessary, some ancestor nodes of selection may be split.
+ *
+ * @param aCancel Returns true if the operation is canceled.
+ * @param aHandled Returns true if the edit action is handled.
+ */
+ MOZ_MUST_USE nsresult WillAbsolutePosition(bool* aCancel, bool* aHandled);
+
+ /**
+ * PrepareToMakeElementAbsolutePosition() is helper method of
+ * WillAbsolutePosition() since in some cases, needs to restore selection
+ * with AutoSelectionRestorer. So, all callers have to check if
+ * CanHandleEditAction() still returns true after a call of this method.
+ * XXX Should be documented outline of this method.
+ *
+ * @param aHandled Returns true if the edit action is handled.
+ * @param aTargetElement Returns target element which should be
+ * changed to absolute positioned.
+ */
+ MOZ_MUST_USE nsresult
+ PrepareToMakeElementAbsolutePosition(bool* aHandled,
+ RefPtr<Element>* aTargetElement);
+
+ /**
* Called if nobody handles the edit action to make an element absolute
* positioned.
* This method actually changes the element which is computed by
* WillAbsolutePosition() to absolute positioned.
* Therefore, this might cause destroying the HTML editor.
*/
MOZ_MUST_USE nsresult DidAbsolutePosition();
@@ -591,17 +616,17 @@ protected:
bool mListenerEnabled;
bool mReturnInEmptyLIKillsList;
bool mDidDeleteSelection;
bool mDidRangedDelete;
bool mRestoreContentEditableCount;
RefPtr<nsRange> mUtilRange;
// Need to remember an int across willJoin/didJoin...
uint32_t mJoinOffset;
- nsCOMPtr<Element> mNewBlock;
+ RefPtr<Element> mNewBlock;
RefPtr<RangeItem> mRangeItem;
// XXX In strict speaking, mCachedStyles isn't enough to cache inline styles
// because inline style can be specified with "style" attribute and/or
// CSS in <style> elements or CSS files. So, we need to look for better
// implementation about this.
StyleCache mCachedStyles[SIZE_STYLE_TABLE];
};