Bug 1451672 - part 24: Rename HTMLEditor::CopyLastEditableChildStyles() to HTMLEditor::CopyLastEditableChildStylesWithTransaction() r?m_kato
Like this adds new comments into the method, we should optimize the method
since it may create a lot of transactions and dispatch a lot of mutation
events.
MozReview-Commit-ID: FknJfu6QCjS
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -7593,23 +7593,26 @@ HTMLEditRules::ReturnInListItem(Selectio
ErrorResult error;
aSelection.Collapse(EditorRawDOMPoint(newListItem, 0), error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
- nsCOMPtr<Element> brNode;
- rv = htmlEditor->CopyLastEditableChildStyles(prevItem,
- &aListItem,
- getter_AddRefs(brNode));
- NS_ENSURE_SUCCESS(rv, rv);
- if (brNode) {
- EditorRawDOMPoint atBrNode(brNode);
+ RefPtr<Element> brElement;
+ nsresult rv =
+ htmlEditor->CopyLastEditableChildStylesWithTransaction(
+ *prevItem->AsElement(), aListItem,
+ address_of(brElement));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_FAILURE;
+ }
+ if (brElement) {
+ EditorRawDOMPoint atBrNode(brElement);
if (NS_WARN_IF(!atBrNode.IsSetAndValid())) {
return NS_ERROR_FAILURE;
}
ErrorResult error;
aSelection.Collapse(atBrNode, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -4528,88 +4528,119 @@ HTMLEditor::AreNodesSameType(nsIContent*
}
// If CSS is enabled, we are stricter about span nodes.
return CSSEditUtils::ElementsSameStyle(aNode1->AsElement(),
aNode2->AsElement());
}
nsresult
-HTMLEditor::CopyLastEditableChildStyles(nsINode* aPreviousBlock,
- nsINode* aNewBlock,
- Element** aOutBrNode)
+HTMLEditor::CopyLastEditableChildStylesWithTransaction(
+ Element& aPreviousBlock,
+ Element& aNewBlock,
+ RefPtr<Element>* aNewBrElement)
{
- if (NS_WARN_IF(!aNewBlock)) {
+ if (NS_WARN_IF(!aNewBrElement)) {
return NS_ERROR_INVALID_ARG;
}
- nsCOMPtr<nsINode> newBlock(aNewBlock);
- *aOutBrNode = nullptr;
+ *aNewBrElement = nullptr;
+
+ RefPtr<Element> previousBlock(&aPreviousBlock);
+ RefPtr<Element> newBlock(&aNewBlock);
+
// First, clear out aNewBlock. Contract is that we want only the styles
// from aPreviousBlock.
- for (nsCOMPtr<nsINode> child = aNewBlock->GetFirstChild();
+ for (nsCOMPtr<nsINode> child = newBlock->GetFirstChild();
child;
- child = aNewBlock->GetFirstChild()) {
+ child = newBlock->GetFirstChild()) {
nsresult rv = DeleteNodeWithTransaction(*child);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
- // now find and clone the styles
- nsCOMPtr<nsINode> child = aPreviousBlock;
- nsCOMPtr<nsINode> tmp = aPreviousBlock;
- while (tmp) {
- child = tmp;
- tmp = GetLastEditableChild(*child);
- }
- while (child && TextEditUtils::IsBreak(child)) {
- child = GetPreviousEditableHTMLNode(*child);
- }
- nsCOMPtr<Element> newStyles, deepestStyle;
- nsCOMPtr<nsINode> childNode = child;
- nsCOMPtr<Element> childElement;
- if (childNode) {
- childElement = childNode->IsElement() ? childNode->AsElement()
- : childNode->GetParentElement();
- }
- while (childElement && (childElement != aPreviousBlock)) {
- if (HTMLEditUtils::IsInlineStyle(childElement) ||
- childElement->IsHTMLElement(nsGkAtoms::span)) {
- if (newStyles) {
- newStyles =
- InsertContainerWithTransaction(*newStyles,
- *childElement->NodeInfo()->NameAtom());
- if (NS_WARN_IF(!newStyles)) {
- return NS_ERROR_FAILURE;
- }
- } else {
- EditorRawDOMPoint atStartOfNewBlock(newBlock, 0);
- deepestStyle = newStyles =
- CreateNodeWithTransaction(*childElement->NodeInfo()->NameAtom(),
- atStartOfNewBlock);
- if (NS_WARN_IF(!newStyles)) {
- return NS_ERROR_FAILURE;
- }
+
+ // XXX aNewBlock may be moved or removed. Even in such case, we should
+ // keep cloning the styles?
+
+ // Look for the deepest last editable leaf node in aPreviousBlock.
+ // Then, if found one is a <br> element, look for non-<br> element.
+ nsIContent* deepestEditableContent = nullptr;
+ for (nsCOMPtr<nsIContent> child = previousBlock.get();
+ child;
+ child = GetLastEditableChild(*child)) {
+ deepestEditableContent = child;
+ }
+ while (deepestEditableContent &&
+ TextEditUtils::IsBreak(deepestEditableContent)) {
+ deepestEditableContent =
+ GetPreviousEditableHTMLNode(*deepestEditableContent);
+ }
+ Element* deepestVisibleEditableElement = nullptr;
+ if (deepestEditableContent) {
+ deepestVisibleEditableElement =
+ deepestEditableContent->IsElement() ?
+ deepestEditableContent->AsElement() :
+ deepestEditableContent->GetParentElement();
+ }
+
+ // Clone inline elements to keep current style in the new block.
+ // XXX Looks like that this is really slow if lastEditableDescendant is
+ // far from aPreviousBlock. Probably, we should clone inline containers
+ // from ancestor to descendants without transactions, then, insert it
+ // after that with transaction.
+ RefPtr<Element> lastClonedElement, firstClonsedElement;
+ for (RefPtr<Element> elementInPreviousBlock = deepestVisibleEditableElement;
+ elementInPreviousBlock && elementInPreviousBlock != previousBlock;
+ elementInPreviousBlock = elementInPreviousBlock->GetParentElement()) {
+ if (!HTMLEditUtils::IsInlineStyle(elementInPreviousBlock) &&
+ !elementInPreviousBlock->IsHTMLElement(nsGkAtoms::span)) {
+ continue;
+ }
+ nsAtom* tagName = elementInPreviousBlock->NodeInfo()->NameAtom();
+ // At first time, just create the most descendant inline container element.
+ if (!firstClonsedElement) {
+ EditorRawDOMPoint atStartOfNewBlock(newBlock, 0);
+ firstClonsedElement = lastClonedElement =
+ CreateNodeWithTransaction(*tagName, atStartOfNewBlock);
+ if (NS_WARN_IF(!firstClonsedElement)) {
+ return NS_ERROR_FAILURE;
}
- CloneAttributesWithTransaction(*newStyles, *childElement);
+ // Clone all attributes.
+ // XXX Looks like that this clones id attribute too.
+ CloneAttributesWithTransaction(*lastClonedElement,
+ *elementInPreviousBlock);
+ continue;
}
- childElement = childElement->GetParentElement();
- }
- if (deepestStyle) {
- RefPtr<Selection> selection = GetSelection();
- if (NS_WARN_IF(!selection)) {
+ // Otherwise, inserts new parent inline container to the previous inserted
+ // inline container.
+ lastClonedElement =
+ InsertContainerWithTransaction(*lastClonedElement, *tagName);
+ if (NS_WARN_IF(!lastClonedElement)) {
return NS_ERROR_FAILURE;
}
- RefPtr<Element> brElement =
- InsertBrElementWithTransaction(*selection,
- EditorRawDOMPoint(deepestStyle, 0));
- if (NS_WARN_IF(!brElement)) {
- return NS_ERROR_FAILURE;
- }
- brElement.forget(aOutBrNode);
- }
+ CloneAttributesWithTransaction(*lastClonedElement, *elementInPreviousBlock);
+ }
+
+ if (!firstClonsedElement) {
+ // XXX Even if no inline elements are cloned, shouldn't we create new
+ // <br> element for aNewBlock?
+ return NS_OK;
+ }
+
+ RefPtr<Selection> selection = GetSelection();
+ if (NS_WARN_IF(!selection)) {
+ return NS_ERROR_FAILURE;
+ }
+ RefPtr<Element> brElement =
+ InsertBrElementWithTransaction(*selection,
+ EditorRawDOMPoint(firstClonsedElement, 0));
+ if (NS_WARN_IF(!brElement)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aNewBrElement = brElement.forget();
return NS_OK;
}
nsresult
HTMLEditor::GetElementOrigin(Element& aElement,
int32_t& aX,
int32_t& aY)
{
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -154,20 +154,16 @@ public:
// nsIHTMLAbsPosEditor methods (implemented in HTMLAbsPositionEditor.cpp)
NS_DECL_NSIHTMLABSPOSEDITOR
// nsIHTMLInlineTableEditor methods (implemented in HTMLInlineTableEditor.cpp)
NS_DECL_NSIHTMLINLINETABLEEDITOR
// XXX Following methods are not overriding but defined here...
- nsresult CopyLastEditableChildStyles(nsINode* aPreviousBlock,
- nsINode* aNewBlock,
- Element** aOutBrNode);
-
nsresult LoadHTML(const nsAString& aInputString);
nsresult GetCSSBackgroundColorState(bool* aMixed, nsAString& aOutColor,
bool aBlockLevel);
nsresult GetHTMLBackgroundColorState(bool* aMixed, nsAString& outColor);
// nsIEditorStyleSheets methods
NS_DECL_NSIEDITORSTYLESHEETS
@@ -462,16 +458,33 @@ public:
template<typename PT, typename CT>
EditorDOMPoint
InsertNodeIntoProperAncestorWithTransaction(
nsIContent& aNode,
const EditorDOMPointBase<PT, CT>& aPointToInsert,
SplitAtEdges aSplitAtEdges);
/**
+ * CopyLastEditableChildStyles() clones inline container elements into
+ * aPreviousBlock to aNewBlock to keep using same style in it.
+ *
+ * @param aPreviousBlock The previous block element. All inline
+ * elements which are last sibling of each level
+ * are cloned to aNewBlock.
+ * @param aNewBlock New block container element.
+ * @param aNewBrElement If this method creates a new <br> element for
+ * placeholder, this is set to the new <br>
+ * element.
+ */
+ nsresult
+ CopyLastEditableChildStylesWithTransaction(Element& aPreviousBlock,
+ Element& aNewBlock,
+ RefPtr<Element>* aNewBrElement);
+
+ /**
* Use this to assure that selection is set after attribute nodes when
* trying to collapse selection at begining of a block node
* e.g., when setting at beginning of a table cell
* This will stop at a table, however, since we don't want to
* "drill down" into nested tables.
* @param aSelection Optional. If null, we get current selection.
*/
void CollapseSelectionToDeepestNonTableFirstChild(Selection* aSelection,