mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-19 07:26:26 +00:00
Bug 1413181 - part 10: Redesign EditorBase::SplitNodeDeep() r=m_kato
First of all, this patches fixes a bug of EditorBase::CreateNode(). It takes |EditorRawDOMPoint&| but it should be |const EditorRawDOMPoint&| for making callers can specify temporary instances. Next, this patch creates |SplitNodeResult| stack class for result of EditorBase::SplitNodeDeep(). SplitNodeDeep() needs to return previous node and next node of split point. They are called as left node and right node, but these calls are really different term usage. Therefore, this patch names: aOutLeftNode -> SplitNodeResult::GetPreviousNode() aOutRightNode -> SplitNodeResult::GetNextNode() and also declares SplitNodeResult::GetLeftNode() and SplitNodeResult::GetRightNode() which are same meaning as left/right node of other splitting methods. Additionally, of course, this patch makes SplitNodeDeep() use |const EditorRawDOMPoint&| to receive the start point of right node. MozReview-Commit-ID: FnJpeKgtzm4 --HG-- extra : rebase_source : 3829e5528ef837b13fed305e0df1dbbf00e02a07
This commit is contained in:
parent
f9e1aa29a0
commit
419273aff1
@ -1419,15 +1419,17 @@ EditorBase::SetSpellcheckUserOverride(bool enable)
|
||||
|
||||
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);
|
||||
|
||||
@ -1435,14 +1437,14 @@ EditorBase::CreateNode(nsAtom* aTag,
|
||||
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();
|
||||
@ -1450,10 +1452,10 @@ EditorBase::CreateNode(nsAtom* aTag,
|
||||
// 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);
|
||||
pointToInsert.Set(ret);
|
||||
}
|
||||
|
||||
mRangeUpdater.SelAdjCreateNode(aPointToInsert.Container(), offset);
|
||||
mRangeUpdater.SelAdjCreateNode(pointToInsert.Container(), offset);
|
||||
|
||||
{
|
||||
AutoActionListenerArray listeners(mActionListeners);
|
||||
@ -4042,48 +4044,28 @@ EditorBase::IsPreformatted(nsIDOMNode* aNode,
|
||||
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)
|
||||
SplitNodeResult
|
||||
EditorBase::SplitNodeDeep(nsIContent& aMostAncestorToSplit,
|
||||
const EditorRawDOMPoint& aStartOfDeepestRightNode,
|
||||
SplitAtEdges aSplitAtEdges)
|
||||
{
|
||||
MOZ_ASSERT(&aSplitPointParent == &aNode ||
|
||||
EditorUtils::IsDescendantOf(aSplitPointParent, aNode));
|
||||
MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
|
||||
MOZ_ASSERT(aStartOfDeepestRightNode.Container() == &aMostAncestorToSplit ||
|
||||
EditorUtils::IsDescendantOf(*aStartOfDeepestRightNode.Container(),
|
||||
aMostAncestorToSplit));
|
||||
|
||||
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;
|
||||
if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
|
||||
return SplitNodeResult(NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
MOZ_ASSERT(atStartOfRightNode.IsSetAndValid());
|
||||
|
||||
nsCOMPtr<nsIContent> leftNode, rightNode;
|
||||
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
|
||||
@ -4092,7 +4074,7 @@ EditorBase::SplitNodeDeep(nsIContent& aNode,
|
||||
// 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();
|
||||
|
||||
@ -4103,11 +4085,15 @@ EditorBase::SplitNodeDeep(nsIContent& aNode,
|
||||
(!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.
|
||||
@ -4116,12 +4102,9 @@ EditorBase::SplitNodeDeep(nsIContent& aNode,
|
||||
// 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);
|
||||
@ -4132,34 +4115,16 @@ EditorBase::SplitNodeDeep(nsIContent& aNode,
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,6 +122,7 @@ class InsertTextTransaction;
|
||||
class JoinNodeTransaction;
|
||||
class PlaceholderTransaction;
|
||||
class RemoveStyleSheetTransaction;
|
||||
class SplitNodeResult;
|
||||
class SplitNodeTransaction;
|
||||
class TextComposition;
|
||||
class TextEditor;
|
||||
@ -225,7 +226,6 @@ enum class SplitAtEdges
|
||||
eAllowToCreateEmptyContainer
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of an editor object. it will be the controller/focal point
|
||||
* for the main editor services. i.e. the GUIManager, publishing, transaction
|
||||
@ -479,7 +479,7 @@ protected:
|
||||
* @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.
|
||||
@ -1147,13 +1147,28 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
|
@ -141,6 +141,114 @@ 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.
|
||||
|
@ -1856,12 +1856,15 @@ HTMLEditRules::StandardBreakImpl(nsINode& aNode,
|
||||
// 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);
|
||||
@ -1967,38 +1970,59 @@ HTMLEditRules::SplitMailCites(Selection* aSelection,
|
||||
|
||||
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);
|
||||
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 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());
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
selNode = citeNode->GetParentNode();
|
||||
// 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);
|
||||
// Now, offset of pointToInsertBrNode is invalid. Let's clear it.
|
||||
pointToInsertBrNode.Clear();
|
||||
|
||||
// want selection before the break, and on same line
|
||||
// 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
|
||||
@ -2006,24 +2030,24 @@ HTMLEditRules::SplitMailCites(Selection* aSelection,
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@ -2046,15 +2070,15 @@ HTMLEditRules::SplitMailCites(Selection* aSelection,
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -3830,67 +3854,78 @@ HTMLEditRules::MakeBasicBlock(Selection& aSelection, nsAtom& blockType)
|
||||
// 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);
|
||||
if (!HTMLEditUtils::IsFormatNode(curBlock)) {
|
||||
return NS_OK;
|
||||
}
|
||||
// Else nothing to do!
|
||||
} else {
|
||||
// We are making a block. Consume a br, if needed.
|
||||
|
||||
// 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);
|
||||
// Do the splits!
|
||||
SplitNodeResult splitNodeResult =
|
||||
htmlEditor->SplitNodeDeep(curBlock,
|
||||
EditorRawDOMPoint(container, offset),
|
||||
SplitAtEdges::eDoNotCreateEmptyContainer);
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
// Put selection in new block
|
||||
rv = aSelection.Collapse(block, 0);
|
||||
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();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
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
|
||||
@ -4711,32 +4746,37 @@ HTMLEditRules::SplitBlock(Element& 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);
|
||||
// Split at the start.
|
||||
SplitNodeResult splitAtStartResult =
|
||||
htmlEditor->SplitNodeDeep(aBlock, EditorRawDOMPoint(&aStartChild),
|
||||
SplitAtEdges::eDoNotCreateEmptyContainer);
|
||||
NS_WARNING_ASSERTION(splitAtStartResult.Succeeded(),
|
||||
"Failed to split aBlock at start");
|
||||
|
||||
// Do the splits!
|
||||
nsCOMPtr<nsIContent> newMiddleNode1;
|
||||
htmlEditor->SplitNodeDeep(aBlock, startParent, startOffset,
|
||||
SplitAtEdges::eDoNotCreateEmptyContainer,
|
||||
aOutLeftNode, getter_AddRefs(newMiddleNode1));
|
||||
// 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");
|
||||
|
||||
// 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);
|
||||
if (aOutLeftNode) {
|
||||
NS_IF_ADDREF(*aOutLeftNode = splitAtStartResult.GetPreviousNode());
|
||||
}
|
||||
|
||||
// Do the splits!
|
||||
nsCOMPtr<nsIContent> newMiddleNode2;
|
||||
htmlEditor->SplitNodeDeep(aBlock, endParent, endOffset,
|
||||
SplitAtEdges::eDoNotCreateEmptyContainer,
|
||||
getter_AddRefs(newMiddleNode2), aOutRightNode);
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4851,13 +4891,19 @@ HTMLEditRules::CreateStyleForInsertText(Selection& aSelection,
|
||||
// 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;
|
||||
@ -6397,41 +6443,60 @@ HTMLEditRules::GetParagraphFormatNodes(
|
||||
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 (NS_WARN_IF(!mHTMLEditor)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// if we have inline parents above range endpoints, split them
|
||||
// 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;
|
||||
@ -6457,45 +6522,42 @@ HTMLEditRules::BustUpInlinesAtBRs(
|
||||
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;
|
||||
// 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();
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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
|
||||
nsresult rv =
|
||||
htmlEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
|
||||
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);
|
||||
// Move break outside of container and also put in node list
|
||||
EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode());
|
||||
nsresult rv =
|
||||
htmlEditor->MoveNode(brNode->AsContent(), atNextNode.Container(),
|
||||
atNextNode.Offset());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aOutArrayOfNodes.AppendElement(*brNode);
|
||||
|
||||
nextNode = splitNodeResult.GetNextNode();
|
||||
}
|
||||
|
||||
// Now tack on remaining next node.
|
||||
aOutArrayOfNodes.AppendElement(*nextNode);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -6679,15 +6741,23 @@ HTMLEditRules::ReturnInHeader(Selection& aSelection,
|
||||
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);
|
||||
ErrorResult error;
|
||||
SplitNodeResult splitHeaderResult =
|
||||
htmlEditor->SplitNodeDeep(aHeader, EditorRawDOMPoint(node, aOffset),
|
||||
SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
NS_WARNING_ASSERTION(splitHeaderResult.Succeeded(),
|
||||
"Failed to split aHeader");
|
||||
|
||||
// If the left-hand heading is empty, put a mozbr in it
|
||||
// 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);
|
||||
@ -6889,25 +6959,28 @@ HTMLEditRules::SplitParagraph(Selection& aSelection,
|
||||
|
||||
// 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)) {
|
||||
@ -6915,8 +6988,8 @@ HTMLEditRules::SplitParagraph(Selection& aSelection,
|
||||
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.
|
||||
@ -6925,19 +6998,28 @@ HTMLEditRules::SplitParagraph(Selection& aSelection,
|
||||
// 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;
|
||||
}
|
||||
@ -7023,10 +7105,16 @@ HTMLEditRules::ReturnInListItem(Selection& aSelection,
|
||||
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
|
||||
@ -7074,8 +7162,11 @@ HTMLEditRules::ReturnInListItem(Selection& aSelection,
|
||||
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 {
|
||||
@ -7091,19 +7182,26 @@ HTMLEditRules::ReturnInListItem(Selection& aSelection,
|
||||
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(visNode, visOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
rv = aSelection.Collapse(&aListItem, 0);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ErrorResult error;
|
||||
aSelection.Collapse(EditorRawDOMPoint(&aListItem, 0), error);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -7424,7 +7522,14 @@ HTMLEditRules::SplitAsNeeded(nsAtom& aTag,
|
||||
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;
|
||||
@ -7434,16 +7539,15 @@ HTMLEditRules::SplitAsNeeded(nsAtom& aTag,
|
||||
|
||||
// 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;
|
||||
@ -7451,21 +7555,31 @@ HTMLEditRules::SplitAsNeeded(nsAtom& aTag,
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -1619,19 +1619,18 @@ HTMLEditor::InsertNodeAtPoint(nsIDOMNode* aNode,
|
||||
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
|
||||
@ -1975,52 +1974,56 @@ HTMLEditor::MakeOrChangeList(const nsAString& aListType,
|
||||
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 (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 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);
|
||||
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 (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();
|
||||
}
|
||||
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);
|
||||
@ -2120,51 +2123,55 @@ HTMLEditor::InsertBasicBlock(const nsAString& aBlockType)
|
||||
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 (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 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);
|
||||
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 (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();
|
||||
}
|
||||
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);
|
||||
@ -2198,57 +2205,62 @@ HTMLEditor::Indent(const nsAString& aIndent)
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
// Do default - insert a blockquote node if selection collapsed
|
||||
bool isCollapsed = selection->Collapsed();
|
||||
if (!handled && selection->Collapsed() && aIndent.EqualsLiteral("indent")) {
|
||||
nsRange* firstRange = selection->GetRangeAt(0);
|
||||
if (NS_WARN_IF(!firstRange)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
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();
|
||||
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
|
||||
if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
|
||||
NS_WARN_IF(!atStartOfSelection.Container()->IsContent())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
// Have to find a place to put the blockquote.
|
||||
EditorDOMPoint pointToInsertBlockquote(atStartOfSelection);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
@ -272,10 +272,6 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
|
||||
}
|
||||
|
||||
// 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;
|
||||
@ -332,6 +328,8 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
|
||||
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);
|
||||
@ -357,15 +355,22 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
|
||||
// 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
|
||||
@ -649,12 +654,13 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
|
||||
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);
|
||||
}
|
||||
|
@ -525,6 +525,13 @@ HTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode,
|
||||
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();
|
||||
|
||||
@ -553,13 +560,21 @@ HTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode,
|
||||
// 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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user