--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -1414,51 +1414,53 @@ EditorBase::SetSpellcheckUserOverride(bo
{
mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
return SyncRealTimeSpell();
}
already_AddRefed<Element>
EditorBase::CreateNode(nsAtom* aTag,
- EditorRawDOMPoint& aPointToInsert)
+ const EditorRawDOMPoint& aPointToInsert)
{
MOZ_ASSERT(aTag);
MOZ_ASSERT(aPointToInsert.IsSetAndValid());
+ EditorRawDOMPoint pointToInsert(aPointToInsert);
+
// XXX We need offset at new node for mRangeUpdater. Therefore, we need
// to compute the offset now but this is expensive. So, if it's possible,
// we need to redesign mRangeUpdater as avoiding using indices.
- int32_t offset = static_cast<int32_t>(aPointToInsert.Offset());
+ int32_t offset = static_cast<int32_t>(pointToInsert.Offset());
AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
{
AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
listener->WillCreateNode(nsDependentAtomString(aTag),
- GetAsDOMNode(aPointToInsert.GetChildAtOffset()));
+ GetAsDOMNode(pointToInsert.GetChildAtOffset()));
}
}
nsCOMPtr<Element> ret;
RefPtr<CreateElementTransaction> transaction =
- CreateTxnForCreateElement(*aTag, aPointToInsert);
+ CreateTxnForCreateElement(*aTag, pointToInsert);
nsresult rv = DoTransaction(transaction);
if (NS_SUCCEEDED(rv)) {
ret = transaction->GetNewNode();
MOZ_ASSERT(ret);
// Now, aPointToInsert may be invalid. I.e., ChildAtOffset() keeps
// referring the next sibling of new node but Offset() refers the
// new node. Let's make refer the new node.
- aPointToInsert.Set(ret);
- }
-
- mRangeUpdater.SelAdjCreateNode(aPointToInsert.Container(), offset);
+ pointToInsert.Set(ret);
+ }
+
+ mRangeUpdater.SelAdjCreateNode(pointToInsert.Container(), offset);
{
AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
listener->DidCreateNode(nsDependentAtomString(aTag),
GetAsDOMNode(ret), rv);
}
}
@@ -4037,134 +4039,97 @@ EditorBase::IsPreformatted(nsIDOMNode* a
}
const nsStyleText* styleText = elementStyle->StyleText();
*aResult = styleText->WhiteSpaceIsSignificant();
return NS_OK;
}
-
-/**
- * This splits a node "deeply", splitting children as appropriate. The place
- * to split is represented by a DOM point at {splitPointParent,
- * splitPointOffset}. That DOM point must be inside aNode, which is the node
- * to split. We return the offset in the parent of aNode where the split
- * terminates - where you would want to insert a new element, for instance, if
- * that's why you were splitting the node.
- *
- * -1 is returned on failure, in unlikely cases like the selection being
- * unavailable or cloning the node failing. Make sure not to use the returned
- * offset for anything without checking that it's valid! If you're not using
- * the offset, it's okay to ignore the return value.
- */
-int32_t
-EditorBase::SplitNodeDeep(nsIContent& aNode,
- nsIContent& aSplitPointParent,
- int32_t aSplitPointOffset,
- SplitAtEdges aSplitAtEdges,
- nsIContent** aOutLeftNode,
- nsIContent** aOutRightNode,
- nsCOMPtr<nsIContent>* ioChildAtSplitPointOffset)
-{
- MOZ_ASSERT(&aSplitPointParent == &aNode ||
- EditorUtils::IsDescendantOf(aSplitPointParent, aNode));
-
- int32_t offset =
- std::min(std::max(aSplitPointOffset, 0),
- static_cast<int32_t>(aSplitPointParent.Length()));
- EditorDOMPoint atStartOfRightNode(&aSplitPointParent, offset);
- if (NS_WARN_IF(!atStartOfRightNode.IsSet())) {
- return -1;
- }
- MOZ_ASSERT(atStartOfRightNode.IsSetAndValid());
-
- nsCOMPtr<nsIContent> leftNode, rightNode;
+SplitNodeResult
+EditorBase::SplitNodeDeep(nsIContent& aMostAncestorToSplit,
+ const EditorRawDOMPoint& aStartOfDeepestRightNode,
+ SplitAtEdges aSplitAtEdges)
+{
+ MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
+ MOZ_ASSERT(aStartOfDeepestRightNode.Container() == &aMostAncestorToSplit ||
+ EditorUtils::IsDescendantOf(*aStartOfDeepestRightNode.Container(),
+ aMostAncestorToSplit));
+
+ if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
+ return SplitNodeResult(NS_ERROR_INVALID_ARG);
+ }
+
+ nsCOMPtr<nsIContent> newLeftNodeOfMostAncestor;
+ EditorDOMPoint atStartOfRightNode(aStartOfDeepestRightNode);
while (true) {
- // If we meet an orphan node before meeting aNode, we need to stop
- // splitting. This is a bug of the caller.
- if (NS_WARN_IF(atStartOfRightNode.Container() != &aNode &&
+ // If we meet an orphan node before meeting aMostAncestorToSplit, we need
+ // to stop splitting. This is a bug of the caller.
+ if (NS_WARN_IF(atStartOfRightNode.Container() != &aMostAncestorToSplit &&
!atStartOfRightNode.Container()->GetParent())) {
- return -1;
+ return SplitNodeResult(NS_ERROR_FAILURE);
}
// Need to insert rules code call here to do things like not split a list
// if you are after the last <li> or before the first, etc. For now we
// just have some smarts about unneccessarily splitting text nodes, which
// should be universal enough to put straight in this EditorBase routine.
if (NS_WARN_IF(!atStartOfRightNode.Container()->IsContent())) {
- return -1;
+ return SplitNodeResult(NS_ERROR_FAILURE);
}
nsIContent* currentRightNode = atStartOfRightNode.Container()->AsContent();
// If the split point is middle of the node or the node is not a text node
// and we're allowed to create empty element node, split it.
if ((aSplitAtEdges == SplitAtEdges::eAllowToCreateEmptyContainer &&
!atStartOfRightNode.Container()->GetAsText()) ||
(!atStartOfRightNode.IsStartOfContainer() &&
!atStartOfRightNode.IsEndOfContainer())) {
ErrorResult error;
- rightNode = currentRightNode;
- leftNode = SplitNode(atStartOfRightNode.AsRaw(), error);
+ nsCOMPtr<nsIContent> newLeftNode =
+ SplitNode(atStartOfRightNode.AsRaw(), error);
if (NS_WARN_IF(error.Failed())) {
- error.SuppressException();
- return -1;
+ return SplitNodeResult(NS_ERROR_FAILURE);
+ }
+
+ if (currentRightNode == &aMostAncestorToSplit) {
+ // Actually, we split aMostAncestorToSplit.
+ return SplitNodeResult(newLeftNode, &aMostAncestorToSplit);
}
// Then, try to split its parent before current node.
atStartOfRightNode.Set(currentRightNode);
}
// If the split point is end of the node and it is a text node or we're not
// allowed to create empty container node, try to split its parent after it.
else if (!atStartOfRightNode.IsStartOfContainer()) {
- // XXX Making current node which wasn't split treated as new left node
- // here. However, rightNode still may keep referring a descendant
- // of the leftNode, which was split. This must be odd behavior for
- // the callers.
- // Perhaps, we should set rightNode to currentRightNode?
- leftNode = currentRightNode;
+ if (currentRightNode == &aMostAncestorToSplit) {
+ return SplitNodeResult(&aMostAncestorToSplit, nullptr);
+ }
// Try to split its parent after current node.
atStartOfRightNode.Set(currentRightNode);
DebugOnly<bool> advanced = atStartOfRightNode.AdvanceOffset();
NS_WARNING_ASSERTION(advanced,
"Failed to advance offset after current node");
}
// If the split point is start of the node and it is a text node or we're
// not allowed to create empty container node, try to split its parent.
else {
- // XXX Making current node which wasn't split treated as exiting right
- // node here. However, leftNode still may keep referring a
- // descendant of rightNode, which was created at splitting. This
- // must be odd behavior for the callers.
- // Perhaps, we should set leftNode to nullptr?
- rightNode = currentRightNode;
+ if (currentRightNode == &aMostAncestorToSplit) {
+ return SplitNodeResult(nullptr, &aMostAncestorToSplit);
+ }
// Try to split its parent before current node.
atStartOfRightNode.Set(currentRightNode);
}
-
- if (currentRightNode == &aNode) {
- // we split all the way up to (and including) aNode; we're done
- break;
- }
- }
-
- if (aOutLeftNode) {
- leftNode.forget(aOutLeftNode);
- }
- if (aOutRightNode) {
- rightNode.forget(aOutRightNode);
- }
- if (ioChildAtSplitPointOffset) {
- *ioChildAtSplitPointOffset = atStartOfRightNode.GetChildAtOffset();
- }
-
- return atStartOfRightNode.Offset();
+ }
+
+ return SplitNodeResult(NS_ERROR_FAILURE);
}
/**
* This joins two like nodes "deeply", joining children as appropriate.
* Returns the point of the join, or (nullptr, -1) in case of error.
*/
EditorDOMPoint
EditorBase::JoinNodeDeep(nsIContent& aLeftNode,
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -117,16 +117,17 @@ class EditAggregateTransaction;
class EditTransactionBase;
class ErrorResult;
class HTMLEditor;
class InsertNodeTransaction;
class InsertTextTransaction;
class JoinNodeTransaction;
class PlaceholderTransaction;
class RemoveStyleSheetTransaction;
+class SplitNodeResult;
class SplitNodeTransaction;
class TextComposition;
class TextEditor;
namespace dom {
class DataTransfer;
class Element;
class EventTarget;
@@ -220,17 +221,16 @@ enum class SplitAtEdges
// it won't be split.
eDoNotCreateEmptyContainer,
// EditorBase::SplitNodeDeep() always splits containers even if the split
// point is at edge of a container. E.g., if split point is start of an
// inline element, empty inline element is created as a new left node.
eAllowToCreateEmptyContainer
};
-
/**
* Implementation of an editor object. it will be the controller/focal point
* for the main editor services. i.e. the GUIManager, publishing, transaction
* manager, event interfaces. the idea for the event interfaces is to have them
* delegate the actual commands to the editor independent of the XPFE
* implementation.
*/
class EditorBase : public nsIEditor
@@ -474,17 +474,17 @@ protected:
* @param aPointToInsert The insertion point of new element. If this refers
* end of the container or after, the transaction
* will append the element to the container.
* Otherwise, will insert the element before the
* child node referred by this.
* @return The created new element node.
*/
already_AddRefed<Element> CreateNode(nsAtom* aTag,
- EditorRawDOMPoint& aPointToInsert);
+ const EditorRawDOMPoint& aPointToInsert);
/**
* Create a transaction for inserting aNode as a child of aParent.
*/
already_AddRefed<InsertNodeTransaction>
CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent,
int32_t aOffset);
@@ -1142,23 +1142,38 @@ public:
/**
* When you are using AppendNodeToSelectionAsRange(), call this first to
* start a new selection.
*/
nsresult ClearSelection();
nsresult IsPreformatted(nsIDOMNode* aNode, bool* aResult);
- int32_t SplitNodeDeep(nsIContent& aNode, nsIContent& aSplitPointParent,
- int32_t aSplitPointOffset,
- SplitAtEdges aSplitAtEdges,
- nsIContent** outLeftNode = nullptr,
- nsIContent** outRightNode = nullptr,
- nsCOMPtr<nsIContent>* ioChildAtSplitPointOffset =
- nullptr);
+ /**
+ * SplitNodeDeep() splits aMostAncestorToSplit deeply.
+ *
+ * @param aMostAncestorToSplit The most ancestor node which should be
+ * split.
+ * @param aStartOfDeepestRightNode The start point of deepest right node.
+ * This point must be descendant of
+ * aMostAncestorToSplit.
+ * @param aSplitAtEdges Whether the caller allows this to
+ * create empty container element when
+ * split point is start or end of an
+ * element.
+ * @return SplitPoint() returns split point in
+ * aMostAncestorToSplit. The point must
+ * be good to insert something if the
+ * caller want to do it.
+ */
+ SplitNodeResult
+ SplitNodeDeep(nsIContent& aMostAncestorToSplit,
+ const EditorRawDOMPoint& aDeepestStartOfRightNode,
+ SplitAtEdges aSplitAtEdges);
+
EditorDOMPoint JoinNodeDeep(nsIContent& aLeftNode,
nsIContent& aRightNode);
nsresult GetString(const nsAString& name, nsAString& value);
void BeginUpdateViewBatch();
virtual nsresult EndUpdateViewBatch();
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -137,16 +137,124 @@ EditActionHandled(nsresult aRv = NS_OK)
*/
inline EditActionResult
EditActionCanceled(nsresult aRv = NS_OK)
{
return EditActionResult(aRv, true, true);
}
/***************************************************************************
+ * SplitNodeResult is a simple class for EditorBase::SplitNodeDeep().
+ * This makes the callers' code easier to read.
+ */
+class MOZ_STACK_CLASS SplitNodeResult final
+{
+public:
+ bool Succeeded() const { return NS_SUCCEEDED(mRv); }
+ bool Failed() const { return NS_FAILED(mRv); }
+ nsresult Rv() const { return mRv; }
+
+ /**
+ * DidSplit() returns true if a node was actually split.
+ */
+ bool DidSplit() const
+ {
+ return mPreviousNode && mNextNode;
+ }
+
+ /**
+ * GetLeftNode() simply returns the left node which was created at splitting.
+ * This returns nullptr if the node wasn't split.
+ */
+ nsIContent* GetLeftNode() const
+ {
+ return mPreviousNode && mNextNode ? mPreviousNode.get() : nullptr;
+ }
+
+ /**
+ * GetRightNode() simply returns the right node which was split.
+ * This won't return nullptr unless failed to split due to invalid arguments.
+ */
+ nsIContent* GetRightNode() const
+ {
+ return mPreviousNode && !mNextNode ? mPreviousNode : mNextNode;
+ }
+
+ /**
+ * GetPreviousNode() returns previous node at the split point.
+ */
+ nsIContent* GetPreviousNode() const { return mPreviousNode; }
+
+ /**
+ * GetNextNode() returns next node at the split point.
+ */
+ nsIContent* GetNextNode() const { return mNextNode; }
+
+ /**
+ * SplitPoint() returns the split point in the container.
+ * This is useful when callers insert an element at split point with
+ * EditorBase::CreateNode() or something similar methods.
+ *
+ * Note that the result is EditorRawDOMPoint but the nodes are grabbed
+ * by this instance. Therefore, the life time of both container node
+ * and child node are guaranteed while using the result temporarily.
+ */
+ EditorRawDOMPoint SplitPoint() const
+ {
+ if (Failed()) {
+ return EditorRawDOMPoint();
+ }
+ if (!mPreviousNode) {
+ return EditorRawDOMPoint(mNextNode);
+ }
+ EditorRawDOMPoint point(mPreviousNode);
+ DebugOnly<bool> advanced = point.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset to after previous node");
+ return point;
+ }
+
+ /**
+ * This constructor shouldn't be used by anybody except methods which
+ * use this as result when it succeeds.
+ *
+ * @param aPreviousNodeOfSplitPoint Previous node immediately before
+ * split point.
+ * @param aNextNodeOfSplitPoint Next node immediately after split
+ * point.
+ */
+ SplitNodeResult(nsIContent* aPreviousNodeOfSplitPoint,
+ nsIContent* aNextNodeOfSplitPoint)
+ : mPreviousNode(aPreviousNodeOfSplitPoint)
+ , mNextNode(aNextNodeOfSplitPoint)
+ , mRv(NS_OK)
+ {
+ MOZ_DIAGNOSTIC_ASSERT(mPreviousNode || mNextNode);
+ }
+
+ /**
+ * This constructor shouldn't be used by anybody except methods which
+ * use this as error result when it fails.
+ */
+ explicit SplitNodeResult(nsresult aRv)
+ : mRv(aRv)
+ {
+ MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv));
+ }
+
+private:
+ nsCOMPtr<nsIContent> mPreviousNode;
+ nsCOMPtr<nsIContent> mNextNode;
+
+ nsresult mRv;
+
+ SplitNodeResult() = delete;
+};
+
+/***************************************************************************
* stack based helper class for batching a collection of transactions inside a
* placeholder transaction.
*/
class MOZ_RAII AutoPlaceholderBatch final
{
private:
RefPtr<EditorBase> mEditorBase;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1851,22 +1851,25 @@ HTMLEditRules::StandardBreakImpl(nsINode
if (wsType & WSType::block) {
bBeforeBlock = true;
}
nsCOMPtr<nsIDOMNode> linkDOMNode;
if (htmlEditor->IsInLink(GetAsDOMNode(node), address_of(linkDOMNode))) {
// Split the link
nsCOMPtr<Element> linkNode = do_QueryInterface(linkDOMNode);
NS_ENSURE_STATE(linkNode || !linkDOMNode);
- nsCOMPtr<nsINode> linkParent = linkNode->GetParentNode();
- aOffset =
- htmlEditor->SplitNodeDeep(*linkNode, *node->AsContent(), aOffset,
+ SplitNodeResult splitLinkNodeResult =
+ htmlEditor->SplitNodeDeep(*linkNode, EditorRawDOMPoint(node, aOffset),
SplitAtEdges::eDoNotCreateEmptyContainer);
- NS_ENSURE_STATE(aOffset != -1);
- node = linkParent;
+ if (NS_WARN_IF(splitLinkNodeResult.Failed())) {
+ return splitLinkNodeResult.Rv();
+ }
+ EditorRawDOMPoint splitPoint(splitLinkNodeResult.SplitPoint());
+ node = splitPoint.Container();
+ aOffset = splitPoint.Offset();
}
brNode = wsObj.InsertBreak(address_of(node), &aOffset, nsIEditor::eNone);
NS_ENSURE_TRUE(brNode, NS_ERROR_FAILURE);
}
node = brNode->GetParentNode();
NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
if (bAfterBlock && bBeforeBlock) {
// We just placed a br between block boundaries. This is the one case
@@ -1962,73 +1965,94 @@ HTMLEditRules::SplitMailCites(Selection*
NS_ENSURE_STATE(mHTMLEditor);
selNode = mHTMLEditor->GetNodeLocation(visNode, &selOffset);
++selOffset;
}
}
NS_ENSURE_STATE(mHTMLEditor);
NS_ENSURE_STATE(selNode->IsContent());
- int32_t newOffset =
- mHTMLEditor->SplitNodeDeep(*citeNode, *selNode->AsContent(), selOffset,
- SplitAtEdges::eDoNotCreateEmptyContainer,
- getter_AddRefs(leftCite),
- getter_AddRefs(rightCite));
- NS_ENSURE_STATE(newOffset != -1);
-
- // Add an invisible <br> to the end of the left part if it was a <span> of
- // style="display: block". This is important, since when serialising the
- // cite to plain text, the span which caused the visual break is discarded.
- // So the added <br> will guarantee that the serialiser will insert a
- // break where the user saw one.
- if (leftCite &&
- leftCite->IsHTMLElement(nsGkAtoms::span) &&
- leftCite->GetPrimaryFrame()->IsFrameOfType(nsIFrame::eBlockFrame)) {
- nsCOMPtr<nsINode> lastChild = leftCite->GetLastChild();
- if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
- // We ignore the result here.
- nsCOMPtr<Element> invisBR =
- mHTMLEditor->CreateBR(leftCite, leftCite->Length());
- }
- }
-
- selNode = citeNode->GetParentNode();
+ SplitNodeResult splitCiteNodeResult =
+ mHTMLEditor->SplitNodeDeep(*citeNode,
+ EditorRawDOMPoint(selNode, selOffset),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (NS_WARN_IF(splitCiteNodeResult.Failed())) {
+ return splitCiteNodeResult.Rv();
+ }
+
+ // Add an invisible <br> to the end of current cite node (If new left cite
+ // has not been created, we're at the end of it. Otherwise, we're still at
+ // the right node) if it was a <span> of style="display: block". This is
+ // important, since when serializing the cite to plain text, the span which
+ // caused the visual break is discarded. So the added <br> will guarantee
+ // that the serializer will insert a break where the user saw one.
+ nsIContent* preveiousNodeOfSplitPoint =
+ splitCiteNodeResult.GetPreviousNode();
+ if (preveiousNodeOfSplitPoint &&
+ preveiousNodeOfSplitPoint->IsHTMLElement(nsGkAtoms::span) &&
+ preveiousNodeOfSplitPoint->GetPrimaryFrame()->
+ IsFrameOfType(nsIFrame::eBlockFrame)) {
+ nsCOMPtr<nsINode> lastChild =
+ preveiousNodeOfSplitPoint->GetLastChild();
+ if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
+ // We ignore the result here.
+ nsCOMPtr<Element> invisBR =
+ mHTMLEditor->CreateBR(preveiousNodeOfSplitPoint,
+ preveiousNodeOfSplitPoint->Length());
+ }
+ }
+
+ // In most cases, <br> should be inserted after current cite. However, if
+ // left cite hasn't been created because the split point was start of the
+ // cite node, <br> should be inserted before the current cite.
+ EditorRawDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
NS_ENSURE_STATE(mHTMLEditor);
- nsCOMPtr<Element> brNode = mHTMLEditor->CreateBR(selNode, newOffset);
+ nsCOMPtr<Element> brNode =
+ mHTMLEditor->CreateBR(pointToInsertBrNode.Container(),
+ pointToInsertBrNode.Offset());
NS_ENSURE_STATE(brNode);
-
- // want selection before the break, and on same line
+ // Now, offset of pointToInsertBrNode is invalid. Let's clear it.
+ pointToInsertBrNode.Clear();
+
+ // Want selection before the break, and on same line.
+ EditorRawDOMPoint atBrNode(brNode);
aSelection->SetInterlinePosition(true);
- rv = aSelection->Collapse(selNode, newOffset);
- NS_ENSURE_SUCCESS(rv, rv);
+ ErrorResult error;
+ aSelection->Collapse(atBrNode, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+
+ selNode = atBrNode.Container();
+ selOffset = atBrNode.Offset();
// if citeNode wasn't a block, we might also want another break before it.
// We need to examine the content both before the br we just added and also
// just after it. If we don't have another br or block boundary adjacent,
// then we will need a 2nd br added to achieve blank line that user expects.
if (IsInlineNode(*citeNode)) {
NS_ENSURE_STATE(mHTMLEditor);
- WSRunObject wsObj(mHTMLEditor, selNode, newOffset);
+ WSRunObject wsObj(mHTMLEditor, selNode, selOffset);
nsCOMPtr<nsINode> visNode;
int32_t visOffset=0;
WSType wsType;
- wsObj.PriorVisibleNode(selNode, newOffset, address_of(visNode),
+ wsObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
&visOffset, &wsType);
if (wsType == WSType::normalWS || wsType == WSType::text ||
wsType == WSType::special) {
NS_ENSURE_STATE(mHTMLEditor);
- WSRunObject wsObjAfterBR(mHTMLEditor, selNode, newOffset+1);
- wsObjAfterBR.NextVisibleNode(selNode, newOffset + 1,
+ WSRunObject wsObjAfterBR(mHTMLEditor, selNode, selOffset + 1);
+ wsObjAfterBR.NextVisibleNode(selNode, selOffset + 1,
address_of(visNode), &visOffset, &wsType);
if (wsType == WSType::normalWS || wsType == WSType::text ||
wsType == WSType::special ||
// In case we're at the very end.
wsType == WSType::thisBlock) {
NS_ENSURE_STATE(mHTMLEditor);
- brNode = mHTMLEditor->CreateBR(selNode, newOffset);
+ brNode = mHTMLEditor->CreateBR(selNode, selOffset);
NS_ENSURE_STATE(brNode);
}
}
}
// delete any empty cites
bool bEmptyCite = false;
if (leftCite) {
@@ -2041,25 +2065,25 @@ HTMLEditRules::SplitMailCites(Selection*
NS_ENSURE_STATE(mHTMLEditor);
rv = mHTMLEditor->DeleteNode(leftCite);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
}
- if (rightCite) {
+ if (citeNode) {
NS_ENSURE_STATE(mHTMLEditor);
- rv = mHTMLEditor->IsEmptyNode(rightCite, &bEmptyCite, true, false);
+ rv = mHTMLEditor->IsEmptyNode(citeNode, &bEmptyCite, true, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (bEmptyCite) {
NS_ENSURE_STATE(mHTMLEditor);
- rv = mHTMLEditor->DeleteNode(rightCite);
+ rv = mHTMLEditor->DeleteNode(citeNode);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
}
*aHandled = true;
}
return NS_OK;
@@ -3825,77 +3849,88 @@ HTMLEditRules::MakeBasicBlock(Selection&
aSelection.GetRangeAt(0)->GetChildAtStartOffset();
int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
if (&blockType == nsGkAtoms::normal ||
&blockType == nsGkAtoms::_empty) {
// We are removing blocks (going to "body text")
NS_ENSURE_TRUE(htmlEditor->GetBlock(container), NS_ERROR_NULL_POINTER);
OwningNonNull<Element> curBlock = *htmlEditor->GetBlock(container);
- if (HTMLEditUtils::IsFormatNode(curBlock)) {
- // If the first editable node after selection is a br, consume it.
- // Otherwise it gets pushed into a following block after the split,
- // which is visually bad.
- nsCOMPtr<nsIContent> brNode =
- htmlEditor->GetNextEditableHTMLNode(
- EditorRawDOMPoint(container, child, offset));
- if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
- rv = htmlEditor->DeleteNode(brNode);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Do the splits!
- offset =
- htmlEditor->SplitNodeDeep(curBlock, *container->AsContent(), offset,
- SplitAtEdges::eDoNotCreateEmptyContainer);
- NS_ENSURE_STATE(offset != -1);
- // Put a br at the split point
- brNode = htmlEditor->CreateBR(curBlock->GetParentNode(), offset);
- NS_ENSURE_STATE(brNode);
- // Put selection at the split point
- rv = aSelection.Collapse(curBlock->GetParentNode(), offset);
- // Don't restore the selection
- selectionRestorer.Abort();
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // Else nothing to do!
- } else {
- // We are making a block. Consume a br, if needed.
+ if (!HTMLEditUtils::IsFormatNode(curBlock)) {
+ return NS_OK;
+ }
+
+ // If the first editable node after selection is a br, consume it.
+ // Otherwise it gets pushed into a following block after the split,
+ // which is visually bad.
nsCOMPtr<nsIContent> brNode =
- htmlEditor->GetNextEditableHTMLNodeInBlock(
+ htmlEditor->GetNextEditableHTMLNode(
EditorRawDOMPoint(container, child, offset));
if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
rv = htmlEditor->DeleteNode(brNode);
NS_ENSURE_SUCCESS(rv, rv);
- // We don't need to act on this node any more
- arrayOfNodes.RemoveElement(brNode);
- // XXX We need to recompute child here because SplitAsNeeded() and
- // EditorBase::SplitNodeDeep() don't compute child in some cases.
- child = container->GetChildAt(offset);
- }
- // Make sure we can put a block here
- rv = SplitAsNeeded(blockType, container, offset, address_of(child));
- NS_ENSURE_SUCCESS(rv, rv);
- EditorRawDOMPoint atChild(container, child, offset);
- RefPtr<Element> block = htmlEditor->CreateNode(&blockType, atChild);
- NS_ENSURE_STATE(block);
- // Remember our new block for postprocessing
- mNewBlock = block;
- // Delete anything that was in the list of nodes
- while (!arrayOfNodes.IsEmpty()) {
- OwningNonNull<nsINode> curNode = arrayOfNodes[0];
- rv = htmlEditor->DeleteNode(curNode);
- NS_ENSURE_SUCCESS(rv, rv);
- arrayOfNodes.RemoveElementAt(0);
- }
- // Put selection in new block
- rv = aSelection.Collapse(block, 0);
+ }
+ // Do the splits!
+ SplitNodeResult splitNodeResult =
+ htmlEditor->SplitNodeDeep(curBlock,
+ EditorRawDOMPoint(container, offset),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (NS_WARN_IF(splitNodeResult.Failed())) {
+ return splitNodeResult.Rv();
+ }
+ EditorRawDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
+ // Put a br at the split point
+ brNode = htmlEditor->CreateBR(pointToInsertBrNode.Container(),
+ pointToInsertBrNode.Offset());
+ NS_ENSURE_STATE(brNode);
+ // Put selection at the split point
+ EditorRawDOMPoint atBrNode(brNode);
+ ErrorResult error;
+ aSelection.Collapse(atBrNode, error);
// Don't restore the selection
selectionRestorer.Abort();
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+ return NS_OK;
+ }
+
+ // We are making a block. Consume a br, if needed.
+ nsCOMPtr<nsIContent> brNode =
+ htmlEditor->GetNextEditableHTMLNodeInBlock(
+ EditorRawDOMPoint(container, child, offset));
+ if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
+ rv = htmlEditor->DeleteNode(brNode);
NS_ENSURE_SUCCESS(rv, rv);
- }
+ // We don't need to act on this node any more
+ arrayOfNodes.RemoveElement(brNode);
+ // XXX We need to recompute child here because SplitAsNeeded() don't
+ // compute child in some cases.
+ child = container->GetChildAt(offset);
+ }
+ // Make sure we can put a block here
+ rv = SplitAsNeeded(blockType, container, offset, address_of(child));
+ NS_ENSURE_SUCCESS(rv, rv);
+ EditorRawDOMPoint atChild(container, child, offset);
+ RefPtr<Element> block = htmlEditor->CreateNode(&blockType, atChild);
+ NS_ENSURE_STATE(block);
+ // Remember our new block for postprocessing
+ mNewBlock = block;
+ // Delete anything that was in the list of nodes
+ while (!arrayOfNodes.IsEmpty()) {
+ OwningNonNull<nsINode> curNode = arrayOfNodes[0];
+ rv = htmlEditor->DeleteNode(curNode);
+ NS_ENSURE_SUCCESS(rv, rv);
+ arrayOfNodes.RemoveElementAt(0);
+ }
+ // Put selection in new block
+ rv = aSelection.Collapse(block, 0);
+ // Don't restore the selection
+ selectionRestorer.Abort();
+ NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// Okay, now go through all the nodes and make the right kind of blocks, or
// whatever is approriate. Woohoo! Note: blockquote is handled a little
// differently.
if (&blockType == nsGkAtoms::blockquote) {
rv = MakeBlockquote(arrayOfNodes);
NS_ENSURE_SUCCESS(rv, rv);
@@ -4706,42 +4741,47 @@ HTMLEditRules::SplitBlock(Element& aBloc
nsIContent** aOutMiddleNode)
{
// aStartChild and aEndChild must be exclusive descendants of aBlock
MOZ_ASSERT(EditorUtils::IsDescendantOf(aStartChild, aBlock) &&
EditorUtils::IsDescendantOf(aEndChild, aBlock));
NS_ENSURE_TRUE_VOID(mHTMLEditor);
RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
- // Get split point location
- OwningNonNull<nsIContent> startParent = *aStartChild.GetParent();
- int32_t startOffset = startParent->IndexOf(&aStartChild);
-
- // Do the splits!
- nsCOMPtr<nsIContent> newMiddleNode1;
- htmlEditor->SplitNodeDeep(aBlock, startParent, startOffset,
- SplitAtEdges::eDoNotCreateEmptyContainer,
- aOutLeftNode, getter_AddRefs(newMiddleNode1));
-
- // Get split point location
- OwningNonNull<nsIContent> endParent = *aEndChild.GetParent();
- // +1 because we want to be after the child
- int32_t endOffset = 1 + endParent->IndexOf(&aEndChild);
-
- // Do the splits!
- nsCOMPtr<nsIContent> newMiddleNode2;
- htmlEditor->SplitNodeDeep(aBlock, endParent, endOffset,
- SplitAtEdges::eDoNotCreateEmptyContainer,
- getter_AddRefs(newMiddleNode2), aOutRightNode);
+ // Split at the start.
+ SplitNodeResult splitAtStartResult =
+ htmlEditor->SplitNodeDeep(aBlock, EditorRawDOMPoint(&aStartChild),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ NS_WARNING_ASSERTION(splitAtStartResult.Succeeded(),
+ "Failed to split aBlock at start");
+
+ // Split at after the end
+ EditorRawDOMPoint atAfterEnd(&aEndChild);
+ DebugOnly<bool> advanced = atAfterEnd.AdvanceOffset();
+ NS_WARNING_ASSERTION(advanced,
+ "Failed to advance offset after the end node");
+ SplitNodeResult splitAtEndResult =
+ htmlEditor->SplitNodeDeep(aBlock, atAfterEnd,
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ NS_WARNING_ASSERTION(splitAtEndResult.Succeeded(),
+ "Failed to split aBlock at after end");
+
+ if (aOutLeftNode) {
+ NS_IF_ADDREF(*aOutLeftNode = splitAtStartResult.GetPreviousNode());
+ }
+
+ if (aOutRightNode) {
+ NS_IF_ADDREF(*aOutRightNode = splitAtEndResult.GetNextNode());
+ }
if (aOutMiddleNode) {
- if (newMiddleNode2) {
- newMiddleNode2.forget(aOutMiddleNode);
+ if (splitAtEndResult.GetPreviousNode()) {
+ NS_IF_ADDREF(*aOutMiddleNode = splitAtEndResult.GetPreviousNode());
} else {
- newMiddleNode1.forget(aOutMiddleNode);
+ NS_IF_ADDREF(*aOutMiddleNode = splitAtStartResult.GetNextNode());
}
}
}
nsresult
HTMLEditRules::OutdentPartOfBlock(Element& aBlock,
nsIContent& aStartChild,
nsIContent& aEndChild,
@@ -4846,23 +4886,29 @@ HTMLEditRules::CreateStyleForInsertText(
// then process setting any styles
int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize();
item = Move(mHTMLEditor->mTypeInState->TakeSetProperty());
if (item || relFontSize) {
// we have at least one style to add; make a new text node to insert style
// nodes above.
if (RefPtr<Text> text = node->GetAsText()) {
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_FAILURE;
+ }
// if we are in a text node, split it
- NS_ENSURE_STATE(mHTMLEditor);
- offset =
- mHTMLEditor->SplitNodeDeep(*text, *text, offset,
+ SplitNodeResult splitTextNodeResult =
+ mHTMLEditor->SplitNodeDeep(*text, EditorRawDOMPoint(text, offset),
SplitAtEdges::eAllowToCreateEmptyContainer);
- NS_ENSURE_STATE(offset != -1);
- node = node->GetParentNode();
+ if (NS_WARN_IF(splitTextNodeResult.Failed())) {
+ return splitTextNodeResult.Rv();
+ }
+ EditorRawDOMPoint splitPoint(splitTextNodeResult.SplitPoint());
+ node = splitPoint.Container();
+ offset = splitPoint.Offset();
}
if (!mHTMLEditor->IsContainer(node)) {
return NS_OK;
}
OwningNonNull<Text> newNode =
EditorBase::CreateTextNode(aDoc, EmptyString());
NS_ENSURE_STATE(mHTMLEditor);
nsresult rv = mHTMLEditor->InsertNode(*newNode, *node, offset);
@@ -6392,51 +6438,70 @@ HTMLEditRules::GetParagraphFormatNodes(
}
}
return NS_OK;
}
nsresult
HTMLEditRules::BustUpInlinesAtRangeEndpoints(RangeItem& item)
{
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
bool isCollapsed = item.mStartContainer == item.mEndContainer &&
item.mStartOffset == item.mEndOffset;
nsCOMPtr<nsIContent> endInline = GetHighestInlineParent(*item.mEndContainer);
-
- // if we have inline parents above range endpoints, split them
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // XXX Oh, then, if the range is collapsed, we don't need to call
+ // GetHighestInlineParent(), isn't it?
if (endInline && !isCollapsed) {
- nsCOMPtr<nsINode> resultEndNode = endInline->GetParentNode();
- NS_ENSURE_STATE(mHTMLEditor);
- // item.mEndContainer must be content if endInline isn't null
- int32_t resultEndOffset =
- mHTMLEditor->SplitNodeDeep(*endInline, *item.mEndContainer->AsContent(),
- item.mEndOffset,
- SplitAtEdges::eDoNotCreateEmptyContainer);
- NS_ENSURE_TRUE(resultEndOffset != -1, NS_ERROR_FAILURE);
- // reset range
- item.mEndContainer = resultEndNode;
- item.mEndOffset = resultEndOffset;
+ RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+ SplitNodeResult splitEndInlineResult =
+ htmlEditor->SplitNodeDeep(*endInline,
+ EditorRawDOMPoint(item.mEndContainer,
+ item.mEndOffset),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (NS_WARN_IF(splitEndInlineResult.Failed())) {
+ return splitEndInlineResult.Rv();
+ }
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_FAILURE;
+ }
+ EditorRawDOMPoint splitPointAtEnd(splitEndInlineResult.SplitPoint());
+ item.mEndContainer = splitPointAtEnd.Container();
+ item.mEndOffset = splitPointAtEnd.Offset();
}
nsCOMPtr<nsIContent> startInline =
GetHighestInlineParent(*item.mStartContainer);
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_FAILURE;
+ }
if (startInline) {
- nsCOMPtr<nsINode> resultStartNode = startInline->GetParentNode();
- NS_ENSURE_STATE(mHTMLEditor);
- int32_t resultStartOffset =
- mHTMLEditor->SplitNodeDeep(*startInline,
- *item.mStartContainer->AsContent(),
- item.mStartOffset,
- SplitAtEdges::eDoNotCreateEmptyContainer);
- NS_ENSURE_TRUE(resultStartOffset != -1, NS_ERROR_FAILURE);
- // reset range
- item.mStartContainer = resultStartNode;
- item.mStartOffset = resultStartOffset;
+ RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+ SplitNodeResult splitStartInlineResult =
+ htmlEditor->SplitNodeDeep(*startInline,
+ EditorRawDOMPoint(item.mStartContainer,
+ item.mStartOffset),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ if (NS_WARN_IF(splitStartInlineResult.Failed())) {
+ return splitStartInlineResult.Rv();
+ }
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_FAILURE;
+ }
+ EditorRawDOMPoint splitPointAtStart(splitStartInlineResult.SplitPoint());
+ item.mStartContainer = splitPointAtStart.Container();
+ item.mStartOffset = splitPointAtStart.Offset();
}
return NS_OK;
}
nsresult
HTMLEditRules::BustUpInlinesAtBRs(
nsIContent& aNode,
@@ -6452,55 +6517,52 @@ HTMLEditRules::BustUpInlinesAtBRs(
iter.AppendList(functor, arrayOfBreaks);
// If there aren't any breaks, just put inNode itself in the array
if (arrayOfBreaks.IsEmpty()) {
aOutArrayOfNodes.AppendElement(aNode);
return NS_OK;
}
- // Else we need to bust up inNode along all the breaks
- nsCOMPtr<nsINode> inlineParentNode = aNode.GetParentNode();
- nsCOMPtr<nsIContent> splitDeepNode = &aNode;
- nsCOMPtr<nsIContent> leftNode, rightNode;
-
- for (uint32_t i = 0; i < arrayOfBreaks.Length(); i++) {
- OwningNonNull<Element> breakNode = *arrayOfBreaks[i]->AsElement();
- NS_ENSURE_TRUE(splitDeepNode, NS_ERROR_NULL_POINTER);
- NS_ENSURE_TRUE(breakNode->GetParent(), NS_ERROR_NULL_POINTER);
- OwningNonNull<nsIContent> splitParentNode = *breakNode->GetParent();
- int32_t splitOffset = splitParentNode->IndexOf(breakNode);
-
- int32_t resultOffset =
- htmlEditor->SplitNodeDeep(*splitDeepNode, splitParentNode, splitOffset,
- SplitAtEdges::eAllowToCreateEmptyContainer,
- getter_AddRefs(leftNode),
- getter_AddRefs(rightNode));
- NS_ENSURE_STATE(resultOffset != -1);
-
- // Put left node in node list
- if (leftNode) {
+ // Else we need to bust up aNode along all the breaks
+ nsCOMPtr<nsIContent> nextNode = &aNode;
+ for (OwningNonNull<nsINode>& brNode : arrayOfBreaks) {
+ EditorRawDOMPoint atBrNode(brNode);
+ if (NS_WARN_IF(!atBrNode.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
+ SplitNodeResult splitNodeResult =
+ htmlEditor->SplitNodeDeep(*nextNode, atBrNode,
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ if (NS_WARN_IF(splitNodeResult.Failed())) {
+ return splitNodeResult.Rv();
+ }
+
+ // Put previous node at the split point.
+ if (splitNodeResult.GetPreviousNode()) {
// Might not be a left node. A break might have been at the very
// beginning of inline container, in which case SplitNodeDeep would not
// actually split anything
- aOutArrayOfNodes.AppendElement(*leftNode);
- }
+ aOutArrayOfNodes.AppendElement(*splitNodeResult.GetPreviousNode());
+ }
+
// Move break outside of container and also put in node list
+ EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode());
nsresult rv =
- htmlEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
+ htmlEditor->MoveNode(brNode->AsContent(), atNextNode.Container(),
+ atNextNode.Offset());
NS_ENSURE_SUCCESS(rv, rv);
- aOutArrayOfNodes.AppendElement(*breakNode);
-
- // Now rightNode becomes the new node to split
- splitDeepNode = rightNode;
- }
- // Now tack on remaining rightNode, if any, to the list
- if (rightNode) {
- aOutArrayOfNodes.AppendElement(*rightNode);
- }
+ aOutArrayOfNodes.AppendElement(*brNode);
+
+ nextNode = splitNodeResult.GetNextNode();
+ }
+
+ // Now tack on remaining next node.
+ aOutArrayOfNodes.AppendElement(*nextNode);
+
return NS_OK;
}
nsIContent*
HTMLEditRules::GetHighestInlineParent(nsINode& aNode)
{
if (!aNode.IsContent() || IsBlockNode(aNode)) {
return nullptr;
@@ -6674,25 +6736,33 @@ HTMLEditRules::ReturnInHeader(Selection&
int32_t offset = headerParent ? headerParent->IndexOf(&aHeader) : -1;
// Get ws code to adjust any ws
nsCOMPtr<nsINode> node = &aNode;
nsresult rv = WSRunObject::PrepareToSplitAcrossBlocks(htmlEditor,
address_of(node),
&aOffset);
NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(!node->IsContent())) {
+ return NS_ERROR_FAILURE;
+ }
// Split the header
- NS_ENSURE_STATE(node->IsContent());
- htmlEditor->SplitNodeDeep(aHeader, *node->AsContent(), aOffset,
- SplitAtEdges::eAllowToCreateEmptyContainer);
-
- // If the left-hand heading is empty, put a mozbr in it
+ ErrorResult error;
+ SplitNodeResult splitHeaderResult =
+ htmlEditor->SplitNodeDeep(aHeader, EditorRawDOMPoint(node, aOffset),
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ NS_WARNING_ASSERTION(splitHeaderResult.Succeeded(),
+ "Failed to split aHeader");
+
+ // If the previous heading of split point is empty, put a mozbr into it.
nsCOMPtr<nsIContent> prevItem = htmlEditor->GetPriorHTMLSibling(&aHeader);
- if (prevItem && HTMLEditUtils::IsHeader(*prevItem)) {
+ if (prevItem) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ HTMLEditUtils::IsHeader(*prevItem));
bool isEmptyNode;
rv = htmlEditor->IsEmptyNode(prevItem, &isEmptyNode);
NS_ENSURE_SUCCESS(rv, rv);
if (isEmptyNode) {
rv = CreateMozBR(prevItem->AsDOMNode(), 0);
NS_ENSURE_SUCCESS(rv, rv);
}
}
@@ -6884,65 +6954,77 @@ HTMLEditRules::SplitParagraph(Selection&
if (NS_WARN_IF(!mHTMLEditor)) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<HTMLEditor> htmlEditor = mHTMLEditor;
// split para
// get ws code to adjust any ws
- nsCOMPtr<nsIContent> leftPara, rightPara;
nsCOMPtr<nsINode> selNode = aStartOfRightNode.Container();
int32_t selOffset = aStartOfRightNode.Offset();
nsresult rv =
WSRunObject::PrepareToSplitAcrossBlocks(htmlEditor,
address_of(selNode), &selOffset);
- // XXX When it fails, why do we need to return selection node? (Why can the
- // caller trust the result even when it returns error?)
NS_ENSURE_SUCCESS(rv, rv);
- // split the paragraph
- NS_ENSURE_STATE(selNode->IsContent());
- int32_t offset =
- htmlEditor->SplitNodeDeep(aParentDivOrP, *selNode->AsContent(), selOffset,
- SplitAtEdges::eAllowToCreateEmptyContainer,
- getter_AddRefs(leftPara),
- getter_AddRefs(rightPara));
- if (NS_WARN_IF(offset == -1)) {
+ if (NS_WARN_IF(!selNode->IsContent())) {
return NS_ERROR_FAILURE;
}
+
+ // Split the paragraph.
+ SplitNodeResult splitDivOrPResult =
+ htmlEditor->SplitNodeDeep(aParentDivOrP,
+ EditorRawDOMPoint(selNode, selOffset),
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ if (NS_WARN_IF(splitDivOrPResult.Failed())) {
+ return splitDivOrPResult.Rv();
+ }
+ if (NS_WARN_IF(!splitDivOrPResult.DidSplit())) {
+ return NS_ERROR_FAILURE;
+ }
+
// Get rid of the break, if it is visible (otherwise it may be needed to
// prevent an empty p).
if (aNextBRNode && htmlEditor->IsVisibleBRElement(aNextBRNode)) {
rv = htmlEditor->DeleteNode(aNextBRNode);
NS_ENSURE_SUCCESS(rv, rv);
}
- // remove ID attribute on the paragraph we just created
- rv = htmlEditor->RemoveAttribute(rightPara->AsElement(), nsGkAtoms::id);
+ // Remove ID attribute on the paragraph from the existing right node.
+ rv = htmlEditor->RemoveAttribute(aParentDivOrP.AsElement(), nsGkAtoms::id);
NS_ENSURE_SUCCESS(rv, rv);
// We need to ensure to both paragraphs visible even if they are empty.
// However, moz-<br> element isn't useful in this case because moz-<br>
// elements will be ignored by PlaintextSerializer. Additionally,
// moz-<br> will be exposed as <br> with Element.innerHTML. Therefore,
// we can use normal <br> elements for placeholder in this case.
// Note that Chromium also behaves so.
- rv = InsertBRIfNeeded(*leftPara);
+ rv = InsertBRIfNeeded(*splitDivOrPResult.GetPreviousNode());
NS_ENSURE_SUCCESS(rv, rv);
- rv = InsertBRIfNeeded(*rightPara);
+ rv = InsertBRIfNeeded(*splitDivOrPResult.GetNextNode());
NS_ENSURE_SUCCESS(rv, rv);
// selection to beginning of right hand para;
// look inside any containers that are up front.
- nsIContent* child = htmlEditor->GetLeftmostChild(rightPara, true);
+ nsIContent* child = htmlEditor->GetLeftmostChild(&aParentDivOrP, true);
if (EditorBase::IsTextNode(child) || htmlEditor->IsContainer(child)) {
- aSelection.Collapse(child, 0);
+ EditorRawDOMPoint atStartOfChild(child, 0);
+ ErrorResult error;
+ aSelection.Collapse(atStartOfChild, error);
+ if (NS_WARN_IF(error.Failed())) {
+ error.SuppressException();
+ }
} else {
EditorRawDOMPoint atChild(child);
- aSelection.Collapse(atChild);
+ ErrorResult error;
+ aSelection.Collapse(atChild, error);
+ if (NS_WARN_IF(error.Failed())) {
+ error.SuppressException();
+ }
}
return NS_OK;
}
/**
* ReturnInListItem: do the right thing for returns pressed in list items
*/
nsresult
@@ -7018,20 +7100,26 @@ HTMLEditRules::ReturnInListItem(Selectio
}
// Else we want a new list item at the same list level. Get ws code to
// adjust any ws.
nsCOMPtr<nsINode> selNode = &aNode;
rv = WSRunObject::PrepareToSplitAcrossBlocks(htmlEditor,
address_of(selNode), &aOffset);
NS_ENSURE_SUCCESS(rv, rv);
- // Now split list item
- NS_ENSURE_STATE(selNode->IsContent());
- htmlEditor->SplitNodeDeep(aListItem, *selNode->AsContent(), aOffset,
- SplitAtEdges::eAllowToCreateEmptyContainer);
+ if (NS_WARN_IF(!selNode->IsContent())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Now split the list item.
+ SplitNodeResult splitListItemResult =
+ htmlEditor->SplitNodeDeep(aListItem, EditorRawDOMPoint(selNode, aOffset),
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ NS_WARNING_ASSERTION(splitListItemResult.Succeeded(),
+ "Failed to split the list item");
// Hack: until I can change the damaged doc range code back to being
// extra-inclusive, I have to manually detect certain list items that may be
// left empty.
nsCOMPtr<nsIContent> prevItem = htmlEditor->GetPriorHTMLSibling(&aListItem);
if (prevItem && HTMLEditUtils::IsListItem(prevItem)) {
bool isEmptyNode;
rv = htmlEditor->IsEmptyNode(prevItem, &isEmptyNode);
@@ -7069,46 +7157,56 @@ HTMLEditRules::ReturnInListItem(Selectio
&aListItem,
getter_AddRefs(brNode));
NS_ENSURE_SUCCESS(rv, rv);
if (brNode) {
EditorRawDOMPoint atBrNode(brNode);
if (NS_WARN_IF(!atBrNode.IsSetAndValid())) {
return NS_ERROR_FAILURE;
}
- rv = aSelection.Collapse(atBrNode);
- NS_ENSURE_SUCCESS(rv, rv);
+ ErrorResult error;
+ aSelection.Collapse(atBrNode, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
return NS_OK;
}
} else {
WSRunObject wsObj(htmlEditor, &aListItem, 0);
nsCOMPtr<nsINode> visNode;
int32_t visOffset = 0;
WSType wsType;
wsObj.NextVisibleNode(&aListItem, 0, address_of(visNode),
&visOffset, &wsType);
if (wsType == WSType::special || wsType == WSType::br ||
visNode->IsHTMLElement(nsGkAtoms::hr)) {
EditorRawDOMPoint atVisNode(visNode);
if (NS_WARN_IF(!atVisNode.IsSetAndValid())) {
return NS_ERROR_FAILURE;
}
- rv = aSelection.Collapse(atVisNode);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- } else {
- rv = aSelection.Collapse(visNode, visOffset);
- NS_ENSURE_SUCCESS(rv, rv);
+ ErrorResult error;
+ aSelection.Collapse(atVisNode, error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
return NS_OK;
}
- }
- }
- }
- rv = aSelection.Collapse(&aListItem, 0);
- NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aSelection.Collapse(visNode, visOffset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+ }
+ }
+ }
+
+ ErrorResult error;
+ aSelection.Collapse(EditorRawDOMPoint(&aListItem, 0), error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
return NS_OK;
}
/**
* MakeBlockquote() puts the list of nodes into one or more blockquotes.
*/
nsresult
HTMLEditRules::MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray)
@@ -7419,58 +7517,74 @@ HTMLEditRules::SplitAsNeeded(nsAtom& aTa
}
nsresult
HTMLEditRules::SplitAsNeeded(nsAtom& aTag,
nsCOMPtr<nsINode>& inOutParent,
int32_t& inOutOffset,
nsCOMPtr<nsIContent>* inOutChildAtOffset)
{
- NS_ENSURE_TRUE(inOutParent, NS_ERROR_NULL_POINTER);
+ if (NS_WARN_IF(!inOutParent)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_WARN_IF(!mHTMLEditor)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
// Check that we have a place that can legally contain the tag
nsCOMPtr<nsINode> tagParent, splitNode;
for (nsCOMPtr<nsINode> parent = inOutParent; parent;
parent = parent->GetParentNode()) {
// Sniffing up the parent tree until we find a legal place for the block
// Don't leave the active editing host
NS_ENSURE_STATE(mHTMLEditor);
- if (!mHTMLEditor->IsDescendantOfEditorRoot(parent)) {
+ if (!htmlEditor->IsDescendantOfEditorRoot(parent)) {
// XXX Why do we need to check mHTMLEditor again here?
NS_ENSURE_STATE(mHTMLEditor);
- if (parent != mHTMLEditor->GetActiveEditingHost()) {
+ if (parent != htmlEditor->GetActiveEditingHost()) {
return NS_ERROR_FAILURE;
}
}
- NS_ENSURE_STATE(mHTMLEditor);
- if (mHTMLEditor->CanContainTag(*parent, aTag)) {
+ if (htmlEditor->CanContainTag(*parent, aTag)) {
// Success
tagParent = parent;
break;
}
splitNode = parent;
}
+
if (!tagParent) {
// Could not find a place to build tag!
return NS_ERROR_FAILURE;
}
- if (splitNode && splitNode->IsContent() && inOutParent->IsContent()) {
- // We found a place for block, but above inOutParent. We need to split.
- NS_ENSURE_STATE(mHTMLEditor);
- int32_t offset =
- mHTMLEditor->SplitNodeDeep(*splitNode->AsContent(),
- *inOutParent->AsContent(), inOutOffset,
- SplitAtEdges::eAllowToCreateEmptyContainer,
- nullptr, nullptr, inOutChildAtOffset);
- NS_ENSURE_STATE(offset != -1);
- inOutParent = tagParent;
- inOutOffset = offset;
+
+ if (!splitNode || !splitNode->IsContent() || !inOutParent->IsContent()) {
+ return NS_OK;
+ }
+
+ // We found a place for block, but above inOutParent. We need to split.
+ NS_ENSURE_STATE(mHTMLEditor);
+ SplitNodeResult splitNodeResult =
+ mHTMLEditor->SplitNodeDeep(*splitNode->AsContent(),
+ EditorRawDOMPoint(inOutParent, inOutOffset),
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ if (NS_WARN_IF(splitNodeResult.Failed())) {
+ return splitNodeResult.Rv();
+ }
+
+ EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
+ inOutParent = splitPoint.Container();
+ inOutOffset = splitPoint.Offset();
+ if (inOutChildAtOffset) {
+ *inOutChildAtOffset = splitPoint.GetChildAtOffset();
}
return NS_OK;
}
/**
* JoinNodesSmart: Join two nodes, doing whatever makes sense for their
* children (which often means joining them, too). aNodeLeft & aNodeRight must
* be same type of node.
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1614,29 +1614,28 @@ HTMLEditor::InsertNodeAtPoint(nsIDOMNode
// where we were originally asked.
parent = topChild = origParent;
break;
}
topChild = parent;
parent = parent->GetParent();
}
if (parent != topChild) {
- nsCOMPtr<nsIContent> child;
- if (ioChildAtOffset) {
- child = do_QueryInterface(*ioChildAtOffset);
+ // We need to split some levels above the original selection parent.
+ SplitNodeResult splitNodeResult =
+ SplitNodeDeep(*topChild, EditorRawDOMPoint(origParent, *ioOffset),
+ aSplitAtEdges);
+ if (NS_WARN_IF(splitNodeResult.Failed())) {
+ return splitNodeResult.Rv();
}
- // we need to split some levels above the original selection parent
- int32_t offset = SplitNodeDeep(*topChild, *origParent, *ioOffset,
- aSplitAtEdges,
- nullptr, nullptr, address_of(child));
- NS_ENSURE_STATE(offset != -1);
- *ioParent = GetAsDOMNode(parent);
- *ioOffset = offset;
+ EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
+ *ioParent = GetAsDOMNode(splitPoint.Container());
+ *ioOffset = splitPoint.Offset();
if (ioChildAtOffset) {
- *ioChildAtOffset = GetAsDOMNode(child);
+ *ioChildAtOffset = GetAsDOMNode(splitPoint.GetChildAtOffset());
}
}
// Now we can insert the new node
nsresult rv = InsertNode(*node, *parent, *ioOffset);
if (isDocumentFragment) {
*ioChildAtOffset = do_QueryInterface(parent->GetChildAt(*ioOffset));
}
return rv;
@@ -1970,62 +1969,66 @@ HTMLEditor::MakeOrChangeList(const nsASt
ruleInfo.blockType = &aListType;
ruleInfo.entireList = entireList;
ruleInfo.bulletType = &aBulletType;
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
if (cancel || NS_FAILED(rv)) {
return rv;
}
- if (!handled) {
- // Find out if the selection is collapsed:
- bool isCollapsed = selection->Collapsed();
-
- NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
- selection->GetRangeAt(0)->GetStartContainer() &&
- selection->GetRangeAt(0)->GetStartContainer()->IsContent(),
- NS_ERROR_FAILURE);
- OwningNonNull<nsIContent> node =
- *selection->GetRangeAt(0)->GetStartContainer()->AsContent();
- int32_t offset = selection->GetRangeAt(0)->StartOffset();
- nsCOMPtr<nsIContent> child =
- selection->GetRangeAt(0)->GetChildAtStartOffset();
-
- if (isCollapsed) {
- // have to find a place to put the list
- nsCOMPtr<nsIContent> parent = node;
- nsCOMPtr<nsIContent> topChild = node;
-
- RefPtr<nsAtom> listAtom = NS_Atomize(aListType);
- while (!CanContainTag(*parent, *listAtom)) {
- topChild = parent;
- parent = parent->GetParent();
+ if (!handled && selection->Collapsed()) {
+ nsRange* firstRange = selection->GetRangeAt(0);
+ if (NS_WARN_IF(!firstRange)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+ if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
+ NS_WARN_IF(!atStartOfSelection.Container()->IsContent())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Have to find a place to put the list.
+ EditorDOMPoint pointToInsertList(atStartOfSelection);
+
+ RefPtr<nsAtom> listAtom = NS_Atomize(aListType);
+ while (!CanContainTag(*pointToInsertList.Container(), *listAtom)) {
+ pointToInsertList.Set(pointToInsertList.Container());
+ if (NS_WARN_IF(!pointToInsertList.IsSet()) ||
+ NS_WARN_IF(!pointToInsertList.Container()->IsContent())) {
+ return NS_ERROR_FAILURE;
}
-
- if (parent != node) {
- // we need to split up to the child of parent
- offset = SplitNodeDeep(*topChild, *node, offset,
- SplitAtEdges::eAllowToCreateEmptyContainer,
- nullptr, nullptr,
- address_of(child));
- NS_ENSURE_STATE(offset != -1);
+ }
+
+ if (pointToInsertList.Container() != atStartOfSelection.Container()) {
+ // We need to split up to the child of parent.
+ SplitNodeResult splitNodeResult =
+ SplitNodeDeep(*pointToInsertList.GetChildAtOffset(),
+ atStartOfSelection,
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ if (NS_WARN_IF(splitNodeResult.Failed())) {
+ return splitNodeResult.Rv();
}
-
- // make a list
- MOZ_DIAGNOSTIC_ASSERT(child);
- EditorRawDOMPoint atChild(parent, child, offset);
- RefPtr<Element> newList = CreateNode(listAtom, atChild);
- NS_ENSURE_STATE(newList);
- // make a list item
- EditorRawDOMPoint atStartOfNewList(newList, 0);
- RefPtr<Element> newItem = CreateNode(nsGkAtoms::li, atStartOfNewList);
- NS_ENSURE_STATE(newItem);
- rv = selection->Collapse(newItem, 0);
- NS_ENSURE_SUCCESS(rv, rv);
+ pointToInsertList = splitNodeResult.SplitPoint();
+ if (NS_WARN_IF(!pointToInsertList.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
}
+
+ // Create a list and insert it before the right node if we split some
+ // parents of start of selection above, or just start of selection
+ // otherwise.
+ RefPtr<Element> newList = CreateNode(listAtom, pointToInsertList.AsRaw());
+ NS_ENSURE_STATE(newList);
+ // make a list item
+ EditorRawDOMPoint atStartOfNewList(newList, 0);
+ RefPtr<Element> newItem = CreateNode(nsGkAtoms::li, atStartOfNewList);
+ NS_ENSURE_STATE(newItem);
+ rv = selection->Collapse(newItem, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
}
return rules->DidDoAction(selection, &ruleInfo, rv);
}
NS_IMETHODIMP
HTMLEditor::RemoveList(const nsAString& aListType)
{
@@ -2115,61 +2118,65 @@ HTMLEditor::InsertBasicBlock(const nsASt
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
TextRulesInfo ruleInfo(EditAction::makeBasicBlock);
ruleInfo.blockType = &aBlockType;
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
if (cancel || NS_FAILED(rv)) {
return rv;
}
- if (!handled) {
- // Find out if the selection is collapsed:
- bool isCollapsed = selection->Collapsed();
-
- NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
- selection->GetRangeAt(0)->GetStartContainer() &&
- selection->GetRangeAt(0)->GetStartContainer()->IsContent(),
- NS_ERROR_FAILURE);
- OwningNonNull<nsIContent> node =
- *selection->GetRangeAt(0)->GetStartContainer()->AsContent();
- int32_t offset = selection->GetRangeAt(0)->StartOffset();
- nsCOMPtr<nsIContent> child =
- selection->GetRangeAt(0)->GetChildAtStartOffset();
-
- if (isCollapsed) {
- // have to find a place to put the block
- nsCOMPtr<nsIContent> parent = node;
- nsCOMPtr<nsIContent> topChild = node;
-
- RefPtr<nsAtom> blockAtom = NS_Atomize(aBlockType);
- while (!CanContainTag(*parent, *blockAtom)) {
- NS_ENSURE_TRUE(parent->GetParent(), NS_ERROR_FAILURE);
- topChild = parent;
- parent = parent->GetParent();
+ if (!handled && selection->Collapsed()) {
+ nsRange* firstRange = selection->GetRangeAt(0);
+ if (NS_WARN_IF(!firstRange)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+ if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
+ NS_WARN_IF(!atStartOfSelection.Container()->IsContent())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Have to find a place to put the block.
+ EditorDOMPoint pointToInsertBlock(atStartOfSelection);
+
+ RefPtr<nsAtom> blockAtom = NS_Atomize(aBlockType);
+ while (!CanContainTag(*pointToInsertBlock.Container(), *blockAtom)) {
+ pointToInsertBlock.Set(pointToInsertBlock.Container());
+ if (NS_WARN_IF(!pointToInsertBlock.IsSet()) ||
+ NS_WARN_IF(!pointToInsertBlock.Container()->IsContent())) {
+ return NS_ERROR_FAILURE;
}
-
- if (parent != node) {
- // we need to split up to the child of parent
- offset = SplitNodeDeep(*topChild, *node, offset,
- SplitAtEdges::eAllowToCreateEmptyContainer,
- nullptr, nullptr,
- address_of(child));
- NS_ENSURE_STATE(offset != -1);
+ }
+
+ if (pointToInsertBlock.Container() != atStartOfSelection.Container()) {
+ // We need to split up to the child of the point to insert a block.
+ SplitNodeResult splitBlockResult =
+ SplitNodeDeep(*pointToInsertBlock.GetChildAtOffset(),
+ atStartOfSelection,
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ if (NS_WARN_IF(splitBlockResult.Failed())) {
+ return splitBlockResult.Rv();
}
-
- // make a block
- MOZ_DIAGNOSTIC_ASSERT(child);
- EditorRawDOMPoint atChild(parent, child, offset);
- RefPtr<Element> newBlock = CreateNode(blockAtom, atChild);
- NS_ENSURE_STATE(newBlock);
-
- // reposition selection to inside the block
- rv = selection->Collapse(newBlock, 0);
- NS_ENSURE_SUCCESS(rv, 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<Element> newBlock =
+ CreateNode(blockAtom, pointToInsertBlock.AsRaw());
+ NS_ENSURE_STATE(newBlock);
+
+ // reposition selection to inside the block
+ rv = selection->Collapse(newBlock, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
}
return rules->DidDoAction(selection, &ruleInfo, rv);
}
NS_IMETHODIMP
HTMLEditor::Indent(const nsAString& aIndent)
{
@@ -2193,67 +2200,72 @@ HTMLEditor::Indent(const nsAString& aInd
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
TextRulesInfo ruleInfo(opID);
nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
if (cancel || NS_FAILED(rv)) {
return rv;
}
- if (!handled) {
- // Do default - insert a blockquote node if selection collapsed
- bool isCollapsed = selection->Collapsed();
-
- NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
- selection->GetRangeAt(0)->GetStartContainer() &&
- selection->GetRangeAt(0)->GetStartContainer()->IsContent(),
- NS_ERROR_FAILURE);
- OwningNonNull<nsIContent> node =
- *selection->GetRangeAt(0)->GetStartContainer()->AsContent();
- int32_t offset = selection->GetRangeAt(0)->StartOffset();
- nsCOMPtr<nsIContent> child =
- selection->GetRangeAt(0)->GetChildAtStartOffset();
-
- if (aIndent.EqualsLiteral("indent")) {
- if (isCollapsed) {
- // have to find a place to put the blockquote
- nsCOMPtr<nsIContent> parent = node;
- nsCOMPtr<nsIContent> topChild = node;
- while (!CanContainTag(*parent, *nsGkAtoms::blockquote)) {
- NS_ENSURE_TRUE(parent->GetParent(), NS_ERROR_FAILURE);
- topChild = parent;
- parent = parent->GetParent();
- }
-
- if (parent != node) {
- // we need to split up to the child of parent
- offset = SplitNodeDeep(*topChild, *node, offset,
- SplitAtEdges::eAllowToCreateEmptyContainer,
- nullptr, nullptr,
- address_of(child));
- NS_ENSURE_STATE(offset != -1);
- }
-
- // make a blockquote
- MOZ_DIAGNOSTIC_ASSERT(child);
- EditorRawDOMPoint atChild(parent, child, offset);
- RefPtr<Element> newBQ = CreateNode(nsGkAtoms::blockquote, atChild);
- NS_ENSURE_STATE(newBQ);
- // put a space in it so layout will draw the list item
- rv = selection->Collapse(newBQ, 0);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = InsertText(NS_LITERAL_STRING(" "));
- NS_ENSURE_SUCCESS(rv, rv);
- // reposition selection to before the space character
- NS_ENSURE_STATE(selection->GetRangeAt(0));
- rv = selection->Collapse(selection->GetRangeAt(0)->GetStartContainer(),
- 0);
- NS_ENSURE_SUCCESS(rv, rv);
+ if (!handled && selection->Collapsed() && aIndent.EqualsLiteral("indent")) {
+ nsRange* firstRange = selection->GetRangeAt(0);
+ if (NS_WARN_IF(!firstRange)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+ if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
+ NS_WARN_IF(!atStartOfSelection.Container()->IsContent())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Have to find a place to put the blockquote.
+ EditorDOMPoint pointToInsertBlockquote(atStartOfSelection);
+
+ while (!CanContainTag(*pointToInsertBlockquote.Container(),
+ *nsGkAtoms::blockquote)) {
+ pointToInsertBlockquote.Set(pointToInsertBlockquote.Container());
+ if (NS_WARN_IF(!pointToInsertBlockquote.IsSet()) ||
+ NS_WARN_IF(!pointToInsertBlockquote.Container()->IsContent())) {
+ return NS_ERROR_FAILURE;
}
}
+
+ if (pointToInsertBlockquote.Container() !=
+ atStartOfSelection.Container()) {
+ // We need to split up to the child of parent.
+ SplitNodeResult splitBlockquoteResult =
+ SplitNodeDeep(*pointToInsertBlockquote.GetChildAtOffset(),
+ atStartOfSelection,
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ if (NS_WARN_IF(splitBlockquoteResult.Failed())) {
+ return splitBlockquoteResult.Rv();
+ }
+ pointToInsertBlockquote = splitBlockquoteResult.SplitPoint();
+ if (NS_WARN_IF(!pointToInsertBlockquote.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // Create a list and insert it before the right node if we split some
+ // parents of start of selection above, or just start of selection
+ // otherwise.
+ RefPtr<Element> newBQ =
+ CreateNode(nsGkAtoms::blockquote, pointToInsertBlockquote.AsRaw());
+ NS_ENSURE_STATE(newBQ);
+ // put a space in it so layout will draw the list item
+ rv = selection->Collapse(newBQ, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = InsertText(NS_LITERAL_STRING(" "));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // reposition selection to before the space character
+ NS_ENSURE_STATE(selection->GetRangeAt(0));
+ rv = selection->Collapse(selection->GetRangeAt(0)->GetStartContainer(),
+ 0);
+ NS_ENSURE_SUCCESS(rv, rv);
}
return rules->DidDoAction(selection, &ruleInfo, rv);
}
//TODO: IMPLEMENT ALIGNMENT!
NS_IMETHODIMP
HTMLEditor::Align(const nsAString& aAlignType)
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -267,20 +267,16 @@ HTMLEditor::DoInsertHTMLWithContext(cons
// to delete everything.
if (aDeleteSelection) {
return DeleteSelection(eNone, eStrip);
}
return NS_OK;
}
// Are there any table elements in the list?
- // node and offset for insertion
- nsCOMPtr<nsIDOMNode> parentNode;
- int32_t offsetOfNewNode;
-
// check for table cell selection mode
bool cellSelectionMode = false;
nsCOMPtr<nsIDOMElement> cell;
rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
if (NS_SUCCEEDED(rv) && cell) {
cellSelectionMode = true;
}
@@ -327,16 +323,18 @@ HTMLEditor::DoInsertHTMLWithContext(cons
NS_ENSURE_SUCCESS(rv, rv);
if (cancel) {
return NS_OK; // rules canceled the operation
}
if (!handled) {
// The rules code (WillDoAction above) might have changed the selection.
// refresh our memory...
+ nsCOMPtr<nsIDOMNode> parentNode;
+ int32_t offsetOfNewNode;
rv = GetStartNodeAndOffset(selection, getter_AddRefs(parentNode), &offsetOfNewNode);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE);
// Adjust position based on the first node we are going to insert.
NormalizeEOLInsertPosition(nodeList[0], address_of(parentNode),
&offsetOfNewNode);
@@ -352,25 +350,32 @@ HTMLEditor::DoInsertHTMLWithContext(cons
}
// Remember if we are in a link.
bool bStartedInLink = IsInLink(parentNode);
// Are we in a text node? If so, split it.
if (IsTextNode(parentNode)) {
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parentNode);
- NS_ENSURE_STATE(parentContent || !parentNode);
- offsetOfNewNode =
- SplitNodeDeep(*parentContent, *parentContent, offsetOfNewNode,
+ EditorRawDOMPoint pointToSplit(parentContent, offsetOfNewNode);
+ if (NS_WARN_IF(!pointToSplit.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
+ SplitNodeResult splitNodeResult =
+ SplitNodeDeep(*parentContent, pointToSplit,
SplitAtEdges::eAllowToCreateEmptyContainer);
- NS_ENSURE_STATE(offsetOfNewNode != -1);
- nsCOMPtr<nsIDOMNode> temp;
- rv = parentNode->GetParentNode(getter_AddRefs(temp));
- NS_ENSURE_SUCCESS(rv, rv);
- parentNode = temp;
+ if (NS_WARN_IF(splitNodeResult.Failed())) {
+ return splitNodeResult.Rv();
+ }
+ EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
+ if (NS_WARN_IF(!splitPoint.IsSet())) {
+ return NS_ERROR_FAILURE;
+ }
+ parentNode = do_QueryInterface(splitPoint.Container());
+ offsetOfNewNode = splitPoint.Offset();
}
// build up list of parents of first node in list that are either
// lists or tables. First examine front of paste node list.
nsTArray<OwningNonNull<Element>> startListAndTableArray;
GetListAndTableParents(StartOrEnd::start, nodeList,
startListAndTableArray);
@@ -644,22 +649,23 @@ HTMLEditor::DoInsertHTMLWithContext(cons
// so, if we just pasted a link, I split it. Why do that instead of just
// nudging selection point beyond it? Because it might have ended in a BR
// that is not visible. If so, the code above just placed selection
// inside that. So I split it instead.
nsCOMPtr<nsIContent> linkContent = do_QueryInterface(link);
NS_ENSURE_STATE(linkContent || !link);
nsCOMPtr<nsIContent> selContent = do_QueryInterface(selNode);
NS_ENSURE_STATE(selContent || !selNode);
- nsCOMPtr<nsIContent> leftLink;
- SplitNodeDeep(*linkContent, *selContent, selOffset,
- SplitAtEdges::eDoNotCreateEmptyContainer,
- getter_AddRefs(leftLink));
- if (leftLink) {
- EditorRawDOMPoint afterLeftLink(leftLink);
+ SplitNodeResult splitLinkResult =
+ SplitNodeDeep(*linkContent, EditorRawDOMPoint(selContent, selOffset),
+ SplitAtEdges::eDoNotCreateEmptyContainer);
+ NS_WARNING_ASSERTION(splitLinkResult.Succeeded(),
+ "Failed to split the link");
+ if (splitLinkResult.GetPreviousNode()) {
+ EditorRawDOMPoint afterLeftLink(splitLinkResult.GetPreviousNode());
if (afterLeftLink.AdvanceOffset()) {
selection->Collapse(afterLeftLink);
}
}
}
}
}
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -520,16 +520,23 @@ HTMLEditor::SplitStyleAbovePoint(nsCOMPt
nsAtom* aProperty,
const nsAString* aAttribute,
nsIContent** aOutLeftNode,
nsIContent** aOutRightNode)
{
NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE((*aNode)->IsContent(), NS_OK);
+ if (aOutLeftNode) {
+ *aOutLeftNode = nullptr;
+ }
+ if (aOutRightNode) {
+ *aOutRightNode = nullptr;
+ }
+
// Split any matching style nodes above the node/offset
OwningNonNull<nsIContent> node = *(*aNode)->AsContent();
bool useCSS = IsCSSEnabled();
bool isSet;
while (!IsBlockNode(node) && node->GetParent() &&
IsEditable(node->GetParent())) {
@@ -548,23 +555,31 @@ HTMLEditor::SplitStyleAbovePoint(nsCOMPt
(aProperty && node->IsHTMLElement(aProperty)) ||
// node is href - test if really <a href=...
(aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(node)) ||
// or node is any prop, and we asked to split them all
(!aProperty && NodeIsProperty(node)) ||
// or the style is specified in the style attribute
isSet) {
// Found a style node we need to split
- int32_t offset = SplitNodeDeep(*node, *(*aNode)->AsContent(), *aOffset,
- SplitAtEdges::eAllowToCreateEmptyContainer,
- aOutLeftNode, aOutRightNode);
- NS_ENSURE_TRUE(offset != -1, NS_ERROR_FAILURE);
- // reset startNode/startOffset
- *aNode = node->GetParent();
- *aOffset = offset;
+ SplitNodeResult splitNodeResult =
+ SplitNodeDeep(*node, EditorRawDOMPoint(*aNode, *aOffset),
+ SplitAtEdges::eAllowToCreateEmptyContainer);
+ NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
+ "Failed to split the node");
+
+ EditorRawDOMPoint atRightNode(splitNodeResult.SplitPoint());
+ *aNode = atRightNode.Container();
+ *aOffset = atRightNode.Offset();
+ if (aOutLeftNode) {
+ NS_IF_ADDREF(*aOutLeftNode = splitNodeResult.GetPreviousNode());
+ }
+ if (aOutRightNode) {
+ NS_IF_ADDREF(*aOutRightNode = splitNodeResult.GetNextNode());
+ }
}
node = node->GetParent();
}
return NS_OK;
}
nsresult