Bug 1574852 - part 97: Move HTMLEditRules::PrepareToMakeElementAbsolutePosition() to HTMLEditor r=m_kato

Only caller of it is `WillRemoveAbsolutePosition()` and it always sets
`*aHandled` to true before calling it.  Therefore, it does not need to take
it as an argument.

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-09-12 04:07:30 +00:00
parent 0203cd1dd5
commit 878bbf3cb8
3 changed files with 153 additions and 173 deletions

View File

@ -10902,13 +10902,13 @@ nsresult HTMLEditRules::WillAbsolutePosition(bool* aCancel, bool* aHandled) {
}
}
rv = PrepareToMakeElementAbsolutePosition(
aHandled,
address_of(
HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement));
// PrepareToMakeElementAbsolutePosition() may restore selection with
// AutoSelectionRestorer. Therefore, the editor might have already been
// destroyed now.
rv =
MOZ_KnownLive(HTMLEditorRef())
.MoveSelectedContentsToDivElementToMakeItAbsolutePosition(address_of(
HTMLEditorRef().TopLevelEditSubActionDataRef().mNewBlockElement));
// MoveSelectedContentsToDivElementToMakeItAbsolutePosition() may restore
// selection with AutoSelectionRestorer. Therefore, the editor might have
// already been destroyed now.
if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED;
}
@ -10938,26 +10938,22 @@ nsresult HTMLEditRules::WillAbsolutePosition(bool* aCancel, bool* aHandled) {
return rv;
}
nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
bool* aHandled, RefPtr<Element>* aTargetElement) {
MOZ_ASSERT(IsEditorDataAvailable());
MOZ_ASSERT(aHandled);
nsresult HTMLEditor::MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
RefPtr<Element>* aTargetElement) {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aTargetElement);
AutoSelectionRestorer restoreSelectionLater(HTMLEditorRef());
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTArray<RefPtr<nsRange>, 4> arrayOfRanges;
HTMLEditorRef().GetSelectionRangesExtendedToHardLineStartAndEnd(
GetSelectionRangesExtendedToHardLineStartAndEnd(
arrayOfRanges, EditSubAction::eSetPositionToAbsolute);
// Use these ranges to contruct a list of nodes to act on.
nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
.SplitInlinesAndCollectEditTargetNodes(
arrayOfRanges, arrayOfNodes,
EditSubAction::eSetPositionToAbsolute,
HTMLEditor::CollectNonEditableNodes::Yes);
AutoTArray<OwningNonNull<nsINode>, 64> arrayOfNodes;
nsresult rv = SplitInlinesAndCollectEditTargetNodes(
arrayOfRanges, arrayOfNodes, EditSubAction::eSetPositionToAbsolute,
CollectNonEditableNodes::Yes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -10965,42 +10961,37 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
// If there is no visible and editable nodes in the edit targets, make an
// empty block.
// XXX Isn't this odd if there are only non-editable visible nodes?
if (HTMLEditorRef().IsEmptyOneHardLine(arrayOfNodes)) {
if (IsEmptyOneHardLine(arrayOfNodes)) {
nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) {
return NS_ERROR_FAILURE;
}
EditorDOMPoint atStartOfSelection(firstRange->StartRef());
if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
EditorDOMPoint atCaret(firstRange->StartRef());
if (NS_WARN_IF(!atCaret.IsSet())) {
return NS_ERROR_FAILURE;
}
// Make sure we can put a block here.
SplitNodeResult splitNodeResult =
MOZ_KnownLive(HTMLEditorRef())
.MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
atStartOfSelection);
MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div, atCaret);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
RefPtr<Element> positionedDiv =
MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) {
RefPtr<Element> newDivElement = CreateNodeWithTransaction(
*nsGkAtoms::div, splitNodeResult.SplitPoint());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(!positionedDiv)) {
if (NS_WARN_IF(!newDivElement)) {
return NS_ERROR_FAILURE;
}
// Remember our new block for postprocessing
*aTargetElement = positionedDiv;
// Delete anything that was in the list of nodes
// XXX We don't need to remove items from the array.
while (!arrayOfNodes.IsEmpty()) {
OwningNonNull<nsINode> curNode = arrayOfNodes[0];
rv = MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*curNode);
if (NS_WARN_IF(!CanHandleEditAction())) {
rv = DeleteNodeWithTransaction(*curNode);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -11008,25 +10999,28 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
}
arrayOfNodes.RemoveElementAt(0);
}
// Put selection in new block
*aHandled = true;
// Don't restore the selection
restoreSelectionLater.Abort();
ErrorResult error;
SelectionRefPtr()->Collapse(RawRangeBoundary(positionedDiv, 0), error);
if (NS_WARN_IF(!CanHandleEditAction())) {
SelectionRefPtr()->Collapse(RawRangeBoundary(newDivElement, 0), error);
if (NS_WARN_IF(Destroyed())) {
error.SuppressException();
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
*aTargetElement = std::move(newDivElement);
NS_WARNING_ASSERTION(!error.Failed(), "Selection::Collapse() failed");
return error.StealNSResult();
}
// Okay, now go through all the nodes and put them in a blockquote, or
// whatever is appropriate. Woohoo!
nsCOMPtr<Element> curList, curPositionedDiv, indentedLI;
// `<div>` element to be positioned absolutely. This may have already
// existed or newly created by this method.
RefPtr<Element> targetDivElement;
// Newly created list element for moving selected list item elements into
// targetDivElement. I.e., this is created in the `<div>` element.
RefPtr<Element> createdListElement;
// If we handle a parent list item element, this is set to it. In such case,
// we should handle its children again.
RefPtr<Element> handledListItemElement;
for (OwningNonNull<nsINode>& curNode : arrayOfNodes) {
// Here's where we actually figure out what to do.
EditorDOMPoint atCurNode(curNode);
@ -11035,63 +11029,57 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
}
// Ignore all non-editable nodes. Leave them be.
if (!HTMLEditorRef().IsEditable(curNode)) {
if (!IsEditable(curNode)) {
continue;
}
nsCOMPtr<nsIContent> sibling;
// Some logic for putting list items into nested lists...
// If current node is a child of a list element, we need another list
// element in absolute-positioned `<div>` element to avoid non-selected
// list items are moved into the `<div>` element.
if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
// Check to see if curList is still appropriate. Which it is if curNode
// is still right after it in the same list.
if (curList) {
sibling = HTMLEditorRef().GetPriorHTMLSibling(curNode);
}
if (!curList || (sibling && sibling != curList)) {
nsAtom* containerName =
// If we cannot move current node to created list element, we need a
// list element in the target `<div>` element for the destination.
// Therefore, duplicate same list element into the target `<div>`
// element.
nsIContent* previousEditableContent =
createdListElement ? GetPriorHTMLSibling(curNode) : nullptr;
if (!createdListElement ||
(previousEditableContent &&
previousEditableContent != createdListElement)) {
nsAtom* ULOrOLOrDLTagName =
atCurNode.GetContainer()->NodeInfo()->NameAtom();
// Create a new nested list of correct type.
SplitNodeResult splitNodeResult =
MOZ_KnownLive(HTMLEditorRef())
.MaybeSplitAncestorsForInsertWithTransaction(
MOZ_KnownLive(*containerName), atCurNode);
MaybeSplitAncestorsForInsertWithTransaction(
MOZ_KnownLive(*ULOrOLOrDLTagName), atCurNode);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
if (!curPositionedDiv) {
curPositionedDiv =
MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) {
if (!targetDivElement) {
targetDivElement = CreateNodeWithTransaction(
*nsGkAtoms::div, splitNodeResult.SplitPoint());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
curPositionedDiv,
"Failed to create current positioned div element");
*aTargetElement = curPositionedDiv;
if (NS_WARN_IF(!targetDivElement)) {
return NS_ERROR_FAILURE;
}
}
EditorDOMPoint atEndOfCurPositionedDiv;
atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
curList = MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
atEndOfCurPositionedDiv);
if (NS_WARN_IF(!CanHandleEditAction())) {
createdListElement = CreateNodeWithTransaction(
MOZ_KnownLive(*ULOrOLOrDLTagName),
EditorDOMPoint::AtEndOf(*targetDivElement));
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(!curList)) {
if (NS_WARN_IF(!createdListElement)) {
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 = MOZ_KnownLive(HTMLEditorRef())
.MoveNodeToEndWithTransaction(
MOZ_KnownLive(*curNode->AsContent()), *curList);
if (NS_WARN_IF(!CanHandleEditAction())) {
// Move current node (maybe, assumed as a list item element) into the
// new list element in the target `<div>` element to be positioned
// absolutely.
rv = MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
*createdListElement);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -11100,123 +11088,115 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
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
// have several nodes listed in the array of nodes to act on, that are in
// the same list item. Since we only want to indent that li once, we
// must keep track of the most recent indented list item, and not indent
// it if we find another node to act on that is still inside the same li.
RefPtr<Element> listItem =
curNode->IsContent()
? HTMLEditorRef().GetNearestAncestorListItemElement(
*curNode->AsContent())
: nullptr;
if (listItem) {
if (indentedLI == listItem) {
// Already indented this list item
// If contents in a list item element is selected, we should move current
// node into the target `<div>` element with the list item element itself
// because we want to keep indent level of the contents.
if (RefPtr<Element> listItemElement =
curNode->IsContent()
? GetNearestAncestorListItemElement(*curNode->AsContent())
: nullptr) {
if (handledListItemElement == listItemElement) {
// Current node has already been moved into the `<div>` element.
continue;
}
// Check to see if curList is still appropriate. Which it is if
// curNode is still right after it in the same list.
if (curList) {
sibling = HTMLEditorRef().GetPriorHTMLSibling(listItem);
}
if (!curList || (sibling && sibling != curList)) {
EditorDOMPoint atListItem(listItem);
// If we cannot move the list item element into created list element,
// we need another list element in the target `<div>` element.
nsIContent* previousEditableContent =
createdListElement ? GetPriorHTMLSibling(listItemElement) : nullptr;
if (!createdListElement ||
(previousEditableContent &&
previousEditableContent != createdListElement)) {
EditorDOMPoint atListItem(listItemElement);
if (NS_WARN_IF(!atListItem.IsSet())) {
return NS_ERROR_FAILURE;
}
// XXX If curNode is the listItemElement and not in a list element,
// we duplicate wrong element into the target `<div>` element.
nsAtom* containerName =
atListItem.GetContainer()->NodeInfo()->NameAtom();
// Create a new nested list of correct type
SplitNodeResult splitNodeResult =
MOZ_KnownLive(HTMLEditorRef())
.MaybeSplitAncestorsForInsertWithTransaction(
MOZ_KnownLive(*containerName), atListItem);
MaybeSplitAncestorsForInsertWithTransaction(
MOZ_KnownLive(*containerName), atListItem);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
if (!curPositionedDiv) {
curPositionedDiv = MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(
*nsGkAtoms::div,
EditorDOMPoint(atListItem.GetContainer()));
if (NS_WARN_IF(!CanHandleEditAction())) {
if (!targetDivElement) {
targetDivElement = CreateNodeWithTransaction(
*nsGkAtoms::div, EditorDOMPoint(atListItem.GetContainer()));
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(
curPositionedDiv,
"Failed to create current positioned div element");
*aTargetElement = curPositionedDiv;
if (NS_WARN_IF(!targetDivElement)) {
return NS_ERROR_FAILURE;
}
}
EditorDOMPoint atEndOfCurPositionedDiv;
atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
curList = MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
atEndOfCurPositionedDiv);
if (NS_WARN_IF(!CanHandleEditAction())) {
// XXX So, createdListElement may be set to a non-list element.
createdListElement = CreateNodeWithTransaction(
MOZ_KnownLive(*containerName),
EditorDOMPoint::AtEndOf(*targetDivElement));
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(!curList)) {
if (NS_WARN_IF(!createdListElement)) {
return NS_ERROR_FAILURE;
}
}
rv = MOZ_KnownLive(HTMLEditorRef())
.MoveNodeToEndWithTransaction(*listItem, *curList);
if (NS_WARN_IF(!CanHandleEditAction())) {
// Move current list item element into the createdListElement (could be
// non-list element due to the above bug) in a candidate `<div>` element
// to be positioned absolutely.
rv = MoveNodeToEndWithTransaction(*listItemElement, *createdListElement);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Remember we indented this li
indentedLI = listItem;
handledListItemElement = std::move(listItemElement);
continue;
}
// Need to make a div to put things in if we haven't already
if (!curPositionedDiv) {
if (!targetDivElement) {
// If we meet a `<div>` element, use it as the absolute-position
// container.
// XXX This looks odd. If there are 2 or more `<div>` elements are
// selected, first found `<div>` element will have all other
// selected nodes.
if (curNode->IsHTMLElement(nsGkAtoms::div)) {
curPositionedDiv = curNode->AsElement();
*aTargetElement = curPositionedDiv;
curList = nullptr;
targetDivElement = curNode->AsElement();
MOZ_ASSERT(!createdListElement);
MOZ_ASSERT(!handledListItemElement);
continue;
}
// Otherwise, create new `<div>` element to be positioned absolutely
// and to contain all selected nodes.
SplitNodeResult splitNodeResult =
MOZ_KnownLive(HTMLEditorRef())
.MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
atCurNode);
MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
atCurNode);
if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv();
}
curPositionedDiv = MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(
*nsGkAtoms::div, splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) {
targetDivElement = CreateNodeWithTransaction(
*nsGkAtoms::div, splitNodeResult.SplitPoint());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(!curPositionedDiv)) {
if (NS_WARN_IF(!targetDivElement)) {
return NS_ERROR_FAILURE;
}
// Remember our new block for postprocessing
*aTargetElement = curPositionedDiv;
// curPositionedDiv is now the correct thing to put curNode in
}
// Tuck the node into the end of the active blockquote
rv = MOZ_KnownLive(HTMLEditorRef())
.MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
*curPositionedDiv);
if (NS_WARN_IF(!CanHandleEditAction())) {
rv = MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
*targetDivElement);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Forget curList, if any
curList = nullptr;
// Forget createdListElement, if any
createdListElement = nullptr;
}
*aTargetElement = std::move(targetDivElement);
return NS_OK;
}

View File

@ -151,21 +151,6 @@ class HTMLEditRules : public TextEditRules {
MOZ_CAN_RUN_SCRIPT
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_CAN_RUN_SCRIPT
MOZ_MUST_USE nsresult PrepareToMakeElementAbsolutePosition(
bool* aHandled, RefPtr<Element>* aTargetElement);
nsresult AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
nsINode* aNode);
nsresult GetFormatString(nsINode* aNode, nsAString& outFormat);

View File

@ -2655,6 +2655,21 @@ class HTMLEditor final : public TextEditor,
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
OnEndHandlingTopLevelEditSubActionInternal();
/**
* MoveSelectedContentsToDivElementToMakeItAbsolutePosition() looks for
* a `<div>` element in selection first. If not, creates new `<div>`
* element. Then, move all selected contents into the target `<div>`
* element.
* Note that this creates AutoSelectionRestorer. Therefore, callers need
* to check whether we have been destroyed even when this returns NS_OK.
*
* @param aTargetElement Returns target `<div>` element which should be
* changed to absolute positioned.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
MoveSelectedContentsToDivElementToMakeItAbsolutePosition(
RefPtr<Element>* aTargetElement);
protected: // Called by helper classes.
virtual void OnStartToHandleTopLevelEditSubAction(
EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;