Bug 200416 - Account for blocks inside inlines when deleting; r=ehsan

This commit is contained in:
Kai Engert 2012-11-25 14:38:23 +02:00
parent 4d4eaaac51
commit 838796da5f
2 changed files with 131 additions and 87 deletions

View File

@ -2072,7 +2072,7 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
&otherOffset, &otherWSType); &otherOffset, &otherWSType);
// first find the adjacent node in the block // first find the adjacent node in the block
nsCOMPtr<nsIDOMNode> leafNode, leftNode, rightNode, leftParent, rightParent; nsCOMPtr<nsIDOMNode> leafNode, leftNode, rightNode;
if (aAction == nsIEditor::ePrevious) if (aAction == nsIEditor::ePrevious)
{ {
res = mHTMLEditor->GetLastEditableLeaf( visNode, address_of(leafNode)); res = mHTMLEditor->GetLastEditableLeaf( visNode, address_of(leafNode));
@ -2113,29 +2113,14 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
} }
// else we are joining content to block // else we are joining content to block
// find the relavent blocks
if (IsBlockNode(leftNode))
leftParent = leftNode;
else if (leftNode)
leftParent = mHTMLEditor->GetBlockNodeParent(leftNode);
if (IsBlockNode(rightNode))
rightParent = rightNode;
else
rightParent = mHTMLEditor->GetBlockNodeParent(rightNode);
// sanity checks
NS_ENSURE_TRUE(leftParent && rightParent, NS_ERROR_NULL_POINTER);
if (leftParent == rightParent)
return NS_ERROR_UNEXPECTED;
// now join them
nsCOMPtr<nsIDOMNode> selPointNode = startNode; nsCOMPtr<nsIDOMNode> selPointNode = startNode;
int32_t selPointOffset = startOffset; int32_t selPointOffset = startOffset;
{ {
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset); nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel); res = JoinBlocks(leftNode, rightNode, aCancel);
*aHandled = true; *aHandled = true;
NS_ENSURE_SUCCESS(res, res);
} }
aSelection->Collapse(selPointNode, selPointOffset); aSelection->Collapse(selPointNode, selPointOffset);
return res; return res;
@ -2151,7 +2136,7 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
} }
// first find the relavent nodes // first find the relavent nodes
nsCOMPtr<nsIDOMNode> leftNode, rightNode, leftParent, rightParent; nsCOMPtr<nsIDOMNode> leftNode, rightNode;
if (aAction == nsIEditor::ePrevious) if (aAction == nsIEditor::ePrevious)
{ {
res = mHTMLEditor->GetPriorHTMLNode(visNode, address_of(leftNode)); res = mHTMLEditor->GetPriorHTMLNode(visNode, address_of(leftNode));
@ -2178,28 +2163,13 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
return NS_OK; return NS_OK;
} }
// find the relavent blocks
if (IsBlockNode(leftNode))
leftParent = leftNode;
else
leftParent = mHTMLEditor->GetBlockNodeParent(leftNode);
if (IsBlockNode(rightNode))
rightParent = rightNode;
else
rightParent = mHTMLEditor->GetBlockNodeParent(rightNode);
// sanity checks
NS_ENSURE_TRUE(leftParent && rightParent, NS_ERROR_NULL_POINTER);
if (leftParent == rightParent)
return NS_ERROR_UNEXPECTED;
// now join them
nsCOMPtr<nsIDOMNode> selPointNode = startNode; nsCOMPtr<nsIDOMNode> selPointNode = startNode;
int32_t selPointOffset = startOffset; int32_t selPointOffset = startOffset;
{ {
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset); nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel); res = JoinBlocks(leftNode, rightNode, aCancel);
*aHandled = true; *aHandled = true;
NS_ENSURE_SUCCESS(res, res);
} }
aSelection->Collapse(selPointNode, selPointOffset); aSelection->Collapse(selPointNode, selPointOffset);
return res; return res;
@ -2414,8 +2384,7 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
} }
if (join) { if (join) {
res = JoinBlocks(address_of(leftParent), address_of(rightParent), res = JoinBlocks(leftParent, rightParent, aCancel);
aCancel);
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
} }
} }
@ -2531,44 +2500,61 @@ nsHTMLEditRules::GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection
* bool *aCanceled return TRUE if we had to cancel operation * bool *aCanceled return TRUE if we had to cancel operation
*/ */
nsresult nsresult
nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock, nsHTMLEditRules::JoinBlocks(nsIDOMNode *aLeftNode,
nsCOMPtr<nsIDOMNode> *aRightBlock, nsIDOMNode *aRightNode,
bool *aCanceled) bool *aCanceled)
{ {
NS_ENSURE_TRUE(aLeftBlock && aRightBlock && *aLeftBlock && *aRightBlock, NS_ERROR_NULL_POINTER); NS_ENSURE_ARG_POINTER(aLeftNode && aRightNode);
if (nsHTMLEditUtils::IsTableElement(*aLeftBlock) || nsHTMLEditUtils::IsTableElement(*aRightBlock))
{ nsCOMPtr<nsIDOMNode> aLeftBlock, aRightBlock;
if (IsBlockNode(aLeftNode)) {
aLeftBlock = aLeftNode;
} else if (aLeftNode) {
aLeftBlock = mHTMLEditor->GetBlockNodeParent(aLeftNode);
}
if (IsBlockNode(aRightNode)) {
aRightBlock = aRightNode;
} else if (aRightNode) {
aRightBlock = mHTMLEditor->GetBlockNodeParent(aRightNode);
}
// sanity checks
NS_ENSURE_TRUE(aLeftBlock && aRightBlock, NS_ERROR_NULL_POINTER);
NS_ENSURE_STATE(aLeftBlock != aRightBlock);
if (nsHTMLEditUtils::IsTableElement(aLeftBlock) ||
nsHTMLEditUtils::IsTableElement(aRightBlock)) {
// do not try to merge table elements // do not try to merge table elements
*aCanceled = true; *aCanceled = true;
return NS_OK; return NS_OK;
} }
// make sure we don't try to move thing's into HR's, which look like blocks but aren't containers // make sure we don't try to move thing's into HR's, which look like blocks but aren't containers
if (nsHTMLEditUtils::IsHR(*aLeftBlock)) if (nsHTMLEditUtils::IsHR(aLeftBlock)) {
{ nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(aLeftBlock);
nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(*aLeftBlock); aLeftBlock = realLeft;
*aLeftBlock = realLeft;
} }
if (nsHTMLEditUtils::IsHR(*aRightBlock)) if (nsHTMLEditUtils::IsHR(aRightBlock)) {
{ nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(aRightBlock);
nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(*aRightBlock); aRightBlock = realRight;
*aRightBlock = realRight;
} }
// bail if both blocks the same // bail if both blocks the same
if (*aLeftBlock == *aRightBlock) if (aLeftBlock == aRightBlock) {
{
*aCanceled = true; *aCanceled = true;
return NS_OK; return NS_OK;
} }
// Joining a list item to its parent is a NOP. // Joining a list item to its parent is a NOP.
if (nsHTMLEditUtils::IsList(*aLeftBlock) && nsHTMLEditUtils::IsListItem(*aRightBlock)) if (nsHTMLEditUtils::IsList(aLeftBlock) &&
{ nsHTMLEditUtils::IsListItem(aRightBlock)) {
nsCOMPtr<nsIDOMNode> rightParent; nsCOMPtr<nsIDOMNode> rightParent;
(*aRightBlock)->GetParentNode(getter_AddRefs(rightParent)); aRightBlock->GetParentNode(getter_AddRefs(rightParent));
if (rightParent == *aLeftBlock) if (rightParent == aLeftBlock) {
return NS_OK; return NS_OK;
}
} }
// special rule here: if we are trying to join list items, and they are in different lists, // special rule here: if we are trying to join list items, and they are in different lists,
@ -2577,21 +2563,21 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
nsIAtom* existingList = nsGkAtoms::_empty; nsIAtom* existingList = nsGkAtoms::_empty;
int32_t theOffset; int32_t theOffset;
nsCOMPtr<nsIDOMNode> leftList, rightList; nsCOMPtr<nsIDOMNode> leftList, rightList;
if (nsHTMLEditUtils::IsListItem(*aLeftBlock) && nsHTMLEditUtils::IsListItem(*aRightBlock)) if (nsHTMLEditUtils::IsListItem(aLeftBlock) &&
{ nsHTMLEditUtils::IsListItem(aRightBlock)) {
(*aLeftBlock)->GetParentNode(getter_AddRefs(leftList)); aLeftBlock->GetParentNode(getter_AddRefs(leftList));
(*aRightBlock)->GetParentNode(getter_AddRefs(rightList)); aRightBlock->GetParentNode(getter_AddRefs(rightList));
if (leftList && rightList && (leftList!=rightList)) if (leftList && rightList && (leftList!=rightList))
{ {
// there are some special complications if the lists are descendants of // there are some special complications if the lists are descendants of
// the other lists' items. Note that it is ok for them to be descendants // the other lists' items. Note that it is ok for them to be descendants
// of the other lists themselves, which is the usual case for sublists // of the other lists themselves, which is the usual case for sublists
// in our impllementation. // in our impllementation.
if (!nsEditorUtils::IsDescendantOf(leftList, *aRightBlock, &theOffset) && if (!nsEditorUtils::IsDescendantOf(leftList, aRightBlock, &theOffset) &&
!nsEditorUtils::IsDescendantOf(rightList, *aLeftBlock, &theOffset)) !nsEditorUtils::IsDescendantOf(rightList, aLeftBlock, &theOffset))
{ {
*aLeftBlock = leftList; aLeftBlock = leftList;
*aRightBlock = rightList; aRightBlock = rightList;
bMergeLists = true; bMergeLists = true;
existingList = mHTMLEditor->GetTag(leftList); existingList = mHTMLEditor->GetTag(leftList);
} }
@ -2606,18 +2592,22 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
// theOffset below is where you find yourself in aRightBlock when you traverse upwards // theOffset below is where you find yourself in aRightBlock when you traverse upwards
// from aLeftBlock // from aLeftBlock
if (nsEditorUtils::IsDescendantOf(*aLeftBlock, *aRightBlock, &rightOffset)) if (nsEditorUtils::IsDescendantOf(aLeftBlock, aRightBlock, &rightOffset)) {
{
// tricky case. left block is inside right block. // tricky case. left block is inside right block.
// Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining. // Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
rightOffset++; rightOffset++;
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBlockEnd); res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
address_of(aLeftBlock),
nsWSRunObject::kBlockEnd);
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aRightBlock, nsWSRunObject::kAfterBlock, &rightOffset); res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
address_of(aRightBlock),
nsWSRunObject::kAfterBlock,
&rightOffset);
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
// Do br adjustment. // Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode; nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode)); res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
if (bMergeLists) if (bMergeLists)
{ {
@ -2639,23 +2629,27 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
} }
else else
{ {
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset); res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
} }
if (brNode) mHTMLEditor->DeleteNode(brNode); if (brNode) mHTMLEditor->DeleteNode(brNode);
}
// theOffset below is where you find yourself in aLeftBlock when you traverse upwards // theOffset below is where you find yourself in aLeftBlock when you traverse upwards
// from aRightBlock // from aRightBlock
else if (nsEditorUtils::IsDescendantOf(*aRightBlock, *aLeftBlock, &leftOffset)) } else if (nsEditorUtils::IsDescendantOf(aRightBlock, aLeftBlock, &leftOffset)) {
{
// tricky case. right block is inside left block. // tricky case. right block is inside left block.
// Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining. // Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aRightBlock, nsWSRunObject::kBlockStart); res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
address_of(aRightBlock),
nsWSRunObject::kBlockStart);
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBeforeBlock, &leftOffset); res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
address_of(aLeftBlock),
nsWSRunObject::kBeforeBlock,
&leftOffset);
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
// Do br adjustment. // Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode; nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBeforeBlock, address_of(brNode), leftOffset); res = CheckForInvisibleBR(aLeftBlock, kBeforeBlock, address_of(brNode),
leftOffset);
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
if (bMergeLists) if (bMergeLists)
{ {
@ -2663,7 +2657,58 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
} }
else else
{ {
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset); // Left block is a parent of right block, and the parent of the previous
// visible content. Right block is a child and contains the contents we
// want to move.
int32_t previousContentOffset;
nsCOMPtr<nsIDOMNode> previousContentParent;
if (aLeftNode == aLeftBlock) {
// We are working with valid HTML, aLeftNode is a block node, and is
// therefore allowed to contain aRightBlock. This is the simple case,
// we will simply move the content in aRightBlock out of its block.
previousContentParent = aLeftBlock;
previousContentOffset = leftOffset;
} else {
// We try to work as well as possible with HTML that's already invalid.
// Although "right block" is a block, and a block must not be contained
// in inline elements, reality is that broken documents do exist. The
// DIRECT parent of "left NODE" might be an inline element. Previous
// versions of this code skipped inline parents until the first block
// parent was found (and used "left block" as the destination).
// However, in some situations this strategy moves the content to an
// unexpected position. (see bug 200416) The new idea is to make the
// moving content a sibling, next to the previous visible content.
previousContentParent =
nsEditor::GetNodeLocation(aLeftNode, &previousContentOffset);
// We want to move our content just after the previous visible node.
previousContentOffset++;
}
// Because we don't want the moving content to receive the style of the
// previous content, we split the previous content's style.
nsCOMPtr<nsINode> editorRoot = mHTMLEditor->GetEditorRoot();
if (!editorRoot || aLeftNode != editorRoot->AsDOMNode()) {
nsCOMPtr<nsIDOMNode> splittedPreviousContent;
res = mHTMLEditor->SplitStyleAbovePoint(address_of(previousContentParent),
&previousContentOffset,
nullptr, nullptr, nullptr,
address_of(splittedPreviousContent));
NS_ENSURE_SUCCESS(res, res);
if (splittedPreviousContent) {
previousContentParent =
nsEditor::GetNodeLocation(splittedPreviousContent,
&previousContentOffset);
}
}
res = MoveBlock(previousContentParent, aRightBlock,
previousContentOffset, rightOffset);
} }
if (brNode) mHTMLEditor->DeleteNode(brNode); if (brNode) mHTMLEditor->DeleteNode(brNode);
} }
@ -2675,29 +2720,28 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
// from li into p. // from li into p.
// adjust whitespace at block boundaries // adjust whitespace at block boundaries
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, *aLeftBlock, *aRightBlock); res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, aLeftBlock, aRightBlock);
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
// Do br adjustment. // Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode; nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode)); res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
if (bMergeLists || mHTMLEditor->NodesSameType(*aLeftBlock, *aRightBlock)) if (bMergeLists || mHTMLEditor->NodesSameType(aLeftBlock, aRightBlock)) {
{
// nodes are same type. merge them. // nodes are same type. merge them.
nsCOMPtr<nsIDOMNode> parent; nsCOMPtr<nsIDOMNode> parent;
int32_t offset; int32_t offset;
res = JoinNodesSmart(*aLeftBlock, *aRightBlock, address_of(parent), &offset); res = JoinNodesSmart(aLeftBlock, aRightBlock, address_of(parent), &offset);
if (NS_SUCCEEDED(res) && bMergeLists) if (NS_SUCCEEDED(res) && bMergeLists)
{ {
nsCOMPtr<nsIDOMNode> newBlock; nsCOMPtr<nsIDOMNode> newBlock;
res = ConvertListType(*aRightBlock, address_of(newBlock), res = ConvertListType(aRightBlock, address_of(newBlock),
existingList, nsGkAtoms::li); existingList, nsGkAtoms::li);
} }
} }
else else
{ {
// nodes are disimilar types. // nodes are disimilar types.
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset); res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
} }
if (NS_SUCCEEDED(res) && brNode) if (NS_SUCCEEDED(res) && brNode)
{ {

View File

@ -150,7 +150,7 @@ protected:
nsresult InsertBRIfNeeded(nsISelection *aSelection); nsresult InsertBRIfNeeded(nsISelection *aSelection);
nsresult GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection aAction, nsresult GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection aAction,
nsCOMPtr<nsIDOMNode> *outSelNode, int32_t *outSelOffset); nsCOMPtr<nsIDOMNode> *outSelNode, int32_t *outSelOffset);
nsresult JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock, nsCOMPtr<nsIDOMNode> *aRightBlock, bool *aCanceled); nsresult JoinBlocks(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, bool *aCanceled);
nsresult MoveBlock(nsIDOMNode *aLeft, nsIDOMNode *aRight, int32_t aLeftOffset, int32_t aRightOffset); nsresult MoveBlock(nsIDOMNode *aLeft, nsIDOMNode *aRight, int32_t aLeftOffset, int32_t aRightOffset);
nsresult MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset); nsresult MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset);
nsresult MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset); nsresult MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset);