зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1408125 - part 5: Redesign HTMLEditor::NormalizeEOLInsertPosition() with EditorRawDOMPoint r=m_kato
HTMLEditor::NormalizeEOLInsertPosition() takes a set of container node and offset in it for specifying insertion point. So, this should be replaced with |const EditorRawDOMPoint&| and it should return |EditorDOMPoint| rather than modifying the argument. Additionally, perhaps, GetBetterInsertionPointFor() is better name for it. MozReview-Commit-ID: IB1FhrkzK2G --HG-- extra : rebase_source : 55f8c1d9fa1f149e81114c929f6e6232aba03905
This commit is contained in:
Родитель
36d48397f0
Коммит
d942210f2e
|
@ -158,7 +158,7 @@ public:
|
|||
}
|
||||
|
||||
template<typename PT, typename CT>
|
||||
explicit EditorDOMPointBase(const EditorDOMPointBase<PT, CT>& aOther)
|
||||
MOZ_IMPLICIT EditorDOMPointBase(const EditorDOMPointBase<PT, CT>& aOther)
|
||||
: mParent(aOther.mParent)
|
||||
, mChild(aOther.mChild)
|
||||
, mOffset(aOther.mOffset)
|
||||
|
|
|
@ -1356,80 +1356,64 @@ HTMLEditor::RebuildDocumentFromSource(const nsAString& aSourceString)
|
|||
return BeginningOfDocument();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLEditor::NormalizeEOLInsertPosition(nsINode* firstNodeToInsert,
|
||||
nsCOMPtr<nsIDOMNode>* insertParentNode,
|
||||
int32_t* insertOffset)
|
||||
EditorRawDOMPoint
|
||||
HTMLEditor::GetBetterInsertionPointFor(nsINode& aNodeToInsert,
|
||||
const EditorRawDOMPoint& aPointToInsert)
|
||||
{
|
||||
/*
|
||||
This function will either correct the position passed in,
|
||||
or leave the position unchanged.
|
||||
|
||||
When the (first) item to insert is a block level element,
|
||||
and our insertion position is after the last visible item in a line,
|
||||
i.e. the insertion position is just before a visible line break <br>,
|
||||
we want to skip to the position just after the line break (see bug 68767)
|
||||
|
||||
However, our logic to detect whether we should skip or not
|
||||
needs to be more clever.
|
||||
We must not skip when the caret appears to be positioned at the beginning
|
||||
of a block, in that case skipping the <br> would not insert the <br>
|
||||
at the caret position, but after the current empty line.
|
||||
|
||||
So we have several cases to test:
|
||||
|
||||
1) We only ever want to skip, if the next visible thing after the current position is a break
|
||||
|
||||
2) We do not want to skip if there is no previous visible thing at all
|
||||
That is detected if the call to PriorVisibleNode gives us an offset of zero.
|
||||
Because PriorVisibleNode always positions after the prior node, we would
|
||||
see an offset > 0, if there were a prior node.
|
||||
|
||||
3) We do not want to skip, if both the next and the previous visible things are breaks.
|
||||
|
||||
4) We do not want to skip if the previous visible thing is in a different block
|
||||
than the insertion position.
|
||||
*/
|
||||
|
||||
if (!IsBlockNode(firstNodeToInsert)) {
|
||||
return;
|
||||
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
|
||||
return aPointToInsert;
|
||||
}
|
||||
|
||||
WSRunObject wsObj(this, *insertParentNode, *insertOffset);
|
||||
nsCOMPtr<nsINode> nextVisNode, prevVisNode;
|
||||
int32_t nextVisOffset=0;
|
||||
WSType nextVisType;
|
||||
int32_t prevVisOffset=0;
|
||||
WSType prevVisType;
|
||||
|
||||
nsCOMPtr<nsINode> parent(do_QueryInterface(*insertParentNode));
|
||||
wsObj.NextVisibleNode(parent, *insertOffset, address_of(nextVisNode), &nextVisOffset, &nextVisType);
|
||||
if (!nextVisNode) {
|
||||
return;
|
||||
// If the node to insert is not a block level element, we can insert it
|
||||
// at any point.
|
||||
if (!IsBlockNode(&aNodeToInsert)) {
|
||||
return aPointToInsert;
|
||||
}
|
||||
|
||||
if (!(nextVisType & WSType::br)) {
|
||||
return;
|
||||
WSRunObject wsObj(this, aPointToInsert.Container(), aPointToInsert.Offset());
|
||||
|
||||
// If the insertion position is after the last visible item in a line,
|
||||
// i.e., the insertion position is just before a visible line break <br>,
|
||||
// we want to skip to the position just after the line break (see bug 68767).
|
||||
nsCOMPtr<nsINode> nextVisibleNode;
|
||||
int32_t nextVisibleOffset = 0;
|
||||
WSType nextVisibleType;
|
||||
wsObj.NextVisibleNode(aPointToInsert.Container(), aPointToInsert.Offset(),
|
||||
address_of(nextVisibleNode),
|
||||
&nextVisibleOffset, &nextVisibleType);
|
||||
// So, if the next visible node isn't a <br> element, we can insert the block
|
||||
// level element to the point.
|
||||
if (!nextVisibleNode ||
|
||||
!(nextVisibleType & WSType::br)) {
|
||||
return aPointToInsert;
|
||||
}
|
||||
|
||||
wsObj.PriorVisibleNode(parent, *insertOffset, address_of(prevVisNode), &prevVisOffset, &prevVisType);
|
||||
if (!prevVisNode) {
|
||||
return;
|
||||
// However, we must not skip next <br> element when the caret appears to be
|
||||
// positioned at the beginning of a block, in that case skipping the <br>
|
||||
// would not insert the <br> at the caret position, but after the current
|
||||
// empty line.
|
||||
nsCOMPtr<nsINode> previousVisibleNode;
|
||||
int32_t previousVisibleOffset = 0;
|
||||
WSType previousVisibleType;
|
||||
wsObj.PriorVisibleNode(aPointToInsert.Container(), aPointToInsert.Offset(),
|
||||
address_of(previousVisibleNode),
|
||||
&previousVisibleOffset, &previousVisibleType);
|
||||
// So, if there is no previous visible node,
|
||||
// or, if both nodes of the insertion point is <br> elements,
|
||||
// or, if the previous visible node is different block,
|
||||
// we need to skip the following <br>. So, otherwise, we can insert the
|
||||
// block at the insertion point.
|
||||
if (!previousVisibleNode ||
|
||||
(previousVisibleType & WSType::br) ||
|
||||
(previousVisibleType & WSType::thisBlock)) {
|
||||
return aPointToInsert;
|
||||
}
|
||||
|
||||
if (prevVisType & WSType::br) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevVisType & WSType::thisBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t brOffset=0;
|
||||
nsCOMPtr<nsIDOMNode> brNode = GetNodeLocation(GetAsDOMNode(nextVisNode), &brOffset);
|
||||
|
||||
*insertParentNode = brNode;
|
||||
*insertOffset = brOffset + 1;
|
||||
EditorRawDOMPoint afterBRNode(nextVisibleNode);
|
||||
DebugOnly<bool> advanced = afterBRNode.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced,
|
||||
"Failed to advance offset to after the <br> node");
|
||||
return afterBRNode;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1490,19 +1474,23 @@ HTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement,
|
|||
}
|
||||
}
|
||||
|
||||
nsINode* parentSelectedNode = selection->GetAnchorNode();
|
||||
if (parentSelectedNode) {
|
||||
int32_t offsetForInsert = selection->AnchorOffset();
|
||||
if (selection->GetAnchorNode()) {
|
||||
EditorRawDOMPoint atAnchor;
|
||||
if (selection->GetChildAtAnchorOffset()) {
|
||||
atAnchor.Set(selection->GetChildAtAnchorOffset());
|
||||
} else {
|
||||
atAnchor.Set(selection->GetAnchorNode(), selection->AnchorOffset());
|
||||
}
|
||||
// Adjust position based on the node we are going to insert.
|
||||
nsCOMPtr<nsIDOMNode> parentSelectedDOMNode =
|
||||
GetAsDOMNode(parentSelectedNode);
|
||||
NormalizeEOLInsertPosition(element, address_of(parentSelectedDOMNode),
|
||||
&offsetForInsert);
|
||||
EditorRawDOMPoint pointToInsert =
|
||||
GetBetterInsertionPointFor(*element, atAnchor);
|
||||
if (NS_WARN_IF(!pointToInsert.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
EditorDOMPoint insertedPoint =
|
||||
InsertNodeIntoProperAncestor(
|
||||
*element, EditorRawDOMPoint(parentSelectedDOMNode, offsetForInsert),
|
||||
SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
*element, pointToInsert, SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
if (NS_WARN_IF(!insertedPoint.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -708,12 +708,19 @@ protected:
|
|||
bool IsVisibleBRElement(nsINode* aNode);
|
||||
|
||||
/**
|
||||
* Utility routine to possibly adjust the insertion position when
|
||||
* inserting a block level element.
|
||||
* GetBetterInsertionPointFor() returns better insertion point to insert
|
||||
* aNodeToInsert.
|
||||
*
|
||||
* @param aNodeToInsert The node to insert.
|
||||
* @param aPointToInsert A candidate point to insert the node.
|
||||
* @return Better insertion point if next visible node
|
||||
* is a <br> element and previous visible node
|
||||
* is neither none, another <br> element nor
|
||||
* different block level element.
|
||||
*/
|
||||
void NormalizeEOLInsertPosition(nsINode* firstNodeToInsert,
|
||||
nsCOMPtr<nsIDOMNode>* insertParentNode,
|
||||
int32_t* insertOffset);
|
||||
EditorRawDOMPoint
|
||||
GetBetterInsertionPointFor(nsINode& aNodeToInsert,
|
||||
const EditorRawDOMPoint& aPointToInsert);
|
||||
|
||||
/**
|
||||
* Helpers for block transformations.
|
||||
|
|
|
@ -344,51 +344,42 @@ 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);
|
||||
|
||||
// Adjust position based on the first node we are going to insert.
|
||||
NormalizeEOLInsertPosition(nodeList[0], address_of(parentNode),
|
||||
&offsetOfNewNode);
|
||||
// FYI: WillDoAction() above might have changed the selection.
|
||||
EditorDOMPoint pointToInsert =
|
||||
GetBetterInsertionPointFor(nodeList[0], GetStartPoint(selection));
|
||||
if (NS_WARN_IF(!pointToInsert.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// if there are any invisible br's after our insertion point, remove them.
|
||||
// this is because if there is a br at end of what we paste, it will make
|
||||
// the invisible br visible.
|
||||
WSRunObject wsObj(this, parentNode, offsetOfNewNode);
|
||||
WSRunObject wsObj(this, pointToInsert.Container(), pointToInsert.Offset());
|
||||
if (wsObj.mEndReasonNode &&
|
||||
TextEditUtils::IsBreak(wsObj.mEndReasonNode) &&
|
||||
!IsVisibleBRElement(wsObj.mEndReasonNode)) {
|
||||
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
|
||||
rv = DeleteNode(wsObj.mEndReasonNode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Remember if we are in a link.
|
||||
bool bStartedInLink = IsInLink(parentNode);
|
||||
bool bStartedInLink = IsInLink(pointToInsert.Container()->AsDOMNode());
|
||||
|
||||
// Are we in a text node? If so, split it.
|
||||
if (IsTextNode(parentNode)) {
|
||||
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parentNode);
|
||||
EditorRawDOMPoint pointToSplit(parentContent, offsetOfNewNode);
|
||||
if (NS_WARN_IF(!pointToSplit.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (IsTextNode(pointToInsert.Container())) {
|
||||
SplitNodeResult splitNodeResult =
|
||||
SplitNodeDeep(*parentContent, pointToSplit,
|
||||
SplitNodeDeep(*pointToInsert.Container()->AsContent(),
|
||||
pointToInsert.AsRaw(),
|
||||
SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!splitPoint.IsSet())) {
|
||||
pointToInsert = splitNodeResult.SplitPoint();
|
||||
if (NS_WARN_IF(!pointToInsert.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
|
||||
|
@ -429,19 +420,16 @@ HTMLEditor::DoInsertHTMLWithContext(const nsAString& aInputString,
|
|||
endListAndTableArray, highWaterMark);
|
||||
}
|
||||
|
||||
// Loop over the node list and paste the nodes:
|
||||
nsCOMPtr<nsIDOMNode> parentBlock;
|
||||
nsCOMPtr<nsINode> parentNodeNode = do_QueryInterface(parentNode);
|
||||
NS_ENSURE_STATE(parentNodeNode || !parentNode);
|
||||
if (IsBlockNode(parentNodeNode)) {
|
||||
parentBlock = parentNode;
|
||||
} else if (parentNodeNode) {
|
||||
parentBlock = GetAsDOMNode(GetBlockNodeParent(parentNodeNode));
|
||||
}
|
||||
MOZ_ASSERT(pointToInsert.Container()->GetChildAt(pointToInsert.Offset()) ==
|
||||
pointToInsert.GetChildAtOffset());
|
||||
|
||||
// Loop over the node list and paste the nodes:
|
||||
nsCOMPtr<nsINode> parentBlock =
|
||||
IsBlockNode(pointToInsert.Container()) ?
|
||||
pointToInsert.Container() :
|
||||
GetBlockNodeParent(pointToInsert.Container());
|
||||
nsCOMPtr<nsIContent> lastInsertNode;
|
||||
nsCOMPtr<nsINode> insertedContextParent;
|
||||
EditorDOMPoint pointToInsert(parentNodeNode, offsetOfNewNode);
|
||||
for (OwningNonNull<nsINode>& curNode : nodeList) {
|
||||
if (NS_WARN_IF(curNode == fragmentAsNodeNode) ||
|
||||
NS_WARN_IF(TextEditUtils::IsBody(curNode))) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче