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);
// first find the adjacent node in the block
nsCOMPtr<nsIDOMNode> leafNode, leftNode, rightNode, leftParent, rightParent;
nsCOMPtr<nsIDOMNode> leafNode, leftNode, rightNode;
if (aAction == nsIEditor::ePrevious)
{
res = mHTMLEditor->GetLastEditableLeaf( visNode, address_of(leafNode));
@ -2114,28 +2114,13 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
// 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;
int32_t selPointOffset = startOffset;
{
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel);
res = JoinBlocks(leftNode, rightNode, aCancel);
*aHandled = true;
NS_ENSURE_SUCCESS(res, res);
}
aSelection->Collapse(selPointNode, selPointOffset);
return res;
@ -2151,7 +2136,7 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
}
// first find the relavent nodes
nsCOMPtr<nsIDOMNode> leftNode, rightNode, leftParent, rightParent;
nsCOMPtr<nsIDOMNode> leftNode, rightNode;
if (aAction == nsIEditor::ePrevious)
{
res = mHTMLEditor->GetPriorHTMLNode(visNode, address_of(leftNode));
@ -2178,28 +2163,13 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
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;
int32_t selPointOffset = startOffset;
{
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel);
res = JoinBlocks(leftNode, rightNode, aCancel);
*aHandled = true;
NS_ENSURE_SUCCESS(res, res);
}
aSelection->Collapse(selPointNode, selPointOffset);
return res;
@ -2414,8 +2384,7 @@ nsHTMLEditRules::WillDeleteSelection(Selection* aSelection,
}
if (join) {
res = JoinBlocks(address_of(leftParent), address_of(rightParent),
aCancel);
res = JoinBlocks(leftParent, rightParent, aCancel);
NS_ENSURE_SUCCESS(res, res);
}
}
@ -2531,45 +2500,62 @@ nsHTMLEditRules::GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection
* bool *aCanceled return TRUE if we had to cancel operation
*/
nsresult
nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
nsCOMPtr<nsIDOMNode> *aRightBlock,
nsHTMLEditRules::JoinBlocks(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
bool *aCanceled)
{
NS_ENSURE_TRUE(aLeftBlock && aRightBlock && *aLeftBlock && *aRightBlock, NS_ERROR_NULL_POINTER);
if (nsHTMLEditUtils::IsTableElement(*aLeftBlock) || nsHTMLEditUtils::IsTableElement(*aRightBlock))
{
NS_ENSURE_ARG_POINTER(aLeftNode && aRightNode);
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
*aCanceled = true;
return NS_OK;
}
// 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))
{
nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(*aLeftBlock);
*aLeftBlock = realLeft;
if (nsHTMLEditUtils::IsHR(aLeftBlock)) {
nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(aLeftBlock);
aLeftBlock = realLeft;
}
if (nsHTMLEditUtils::IsHR(*aRightBlock))
{
nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(*aRightBlock);
*aRightBlock = realRight;
if (nsHTMLEditUtils::IsHR(aRightBlock)) {
nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(aRightBlock);
aRightBlock = realRight;
}
// bail if both blocks the same
if (*aLeftBlock == *aRightBlock)
{
if (aLeftBlock == aRightBlock) {
*aCanceled = true;
return NS_OK;
}
// 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;
(*aRightBlock)->GetParentNode(getter_AddRefs(rightParent));
if (rightParent == *aLeftBlock)
aRightBlock->GetParentNode(getter_AddRefs(rightParent));
if (rightParent == aLeftBlock) {
return NS_OK;
}
}
// special rule here: if we are trying to join list items, and they are in different lists,
// join the lists instead.
@ -2577,21 +2563,21 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
nsIAtom* existingList = nsGkAtoms::_empty;
int32_t theOffset;
nsCOMPtr<nsIDOMNode> leftList, rightList;
if (nsHTMLEditUtils::IsListItem(*aLeftBlock) && nsHTMLEditUtils::IsListItem(*aRightBlock))
{
(*aLeftBlock)->GetParentNode(getter_AddRefs(leftList));
(*aRightBlock)->GetParentNode(getter_AddRefs(rightList));
if (nsHTMLEditUtils::IsListItem(aLeftBlock) &&
nsHTMLEditUtils::IsListItem(aRightBlock)) {
aLeftBlock->GetParentNode(getter_AddRefs(leftList));
aRightBlock->GetParentNode(getter_AddRefs(rightList));
if (leftList && rightList && (leftList!=rightList))
{
// 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
// of the other lists themselves, which is the usual case for sublists
// in our impllementation.
if (!nsEditorUtils::IsDescendantOf(leftList, *aRightBlock, &theOffset) &&
!nsEditorUtils::IsDescendantOf(rightList, *aLeftBlock, &theOffset))
if (!nsEditorUtils::IsDescendantOf(leftList, aRightBlock, &theOffset) &&
!nsEditorUtils::IsDescendantOf(rightList, aLeftBlock, &theOffset))
{
*aLeftBlock = leftList;
*aRightBlock = rightList;
aLeftBlock = leftList;
aRightBlock = rightList;
bMergeLists = true;
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
// from aLeftBlock
if (nsEditorUtils::IsDescendantOf(*aLeftBlock, *aRightBlock, &rightOffset))
{
if (nsEditorUtils::IsDescendantOf(aLeftBlock, aRightBlock, &rightOffset)) {
// tricky case. left block is inside right block.
// Do ws adjustment. This just destroys non-visible ws at boundaries we will be joining.
rightOffset++;
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBlockEnd);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
address_of(aLeftBlock),
nsWSRunObject::kBlockEnd);
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);
// Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode));
res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
NS_ENSURE_SUCCESS(res, res);
if (bMergeLists)
{
@ -2639,23 +2629,27 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
}
else
{
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
}
if (brNode) mHTMLEditor->DeleteNode(brNode);
}
// theOffset below is where you find yourself in aLeftBlock when you traverse upwards
// from aRightBlock
else if (nsEditorUtils::IsDescendantOf(*aRightBlock, *aLeftBlock, &leftOffset))
{
} else if (nsEditorUtils::IsDescendantOf(aRightBlock, aLeftBlock, &leftOffset)) {
// tricky case. right block is inside left block.
// 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);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBeforeBlock, &leftOffset);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
address_of(aLeftBlock),
nsWSRunObject::kBeforeBlock,
&leftOffset);
NS_ENSURE_SUCCESS(res, res);
// Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBeforeBlock, address_of(brNode), leftOffset);
res = CheckForInvisibleBR(aLeftBlock, kBeforeBlock, address_of(brNode),
leftOffset);
NS_ENSURE_SUCCESS(res, res);
if (bMergeLists)
{
@ -2663,7 +2657,58 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
}
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);
}
@ -2675,29 +2720,28 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
// from li into p.
// adjust whitespace at block boundaries
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, *aLeftBlock, *aRightBlock);
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, aLeftBlock, aRightBlock);
NS_ENSURE_SUCCESS(res, res);
// Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode));
res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
NS_ENSURE_SUCCESS(res, res);
if (bMergeLists || mHTMLEditor->NodesSameType(*aLeftBlock, *aRightBlock))
{
if (bMergeLists || mHTMLEditor->NodesSameType(aLeftBlock, aRightBlock)) {
// nodes are same type. merge them.
nsCOMPtr<nsIDOMNode> parent;
int32_t offset;
res = JoinNodesSmart(*aLeftBlock, *aRightBlock, address_of(parent), &offset);
res = JoinNodesSmart(aLeftBlock, aRightBlock, address_of(parent), &offset);
if (NS_SUCCEEDED(res) && bMergeLists)
{
nsCOMPtr<nsIDOMNode> newBlock;
res = ConvertListType(*aRightBlock, address_of(newBlock),
res = ConvertListType(aRightBlock, address_of(newBlock),
existingList, nsGkAtoms::li);
}
}
else
{
// nodes are disimilar types.
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
}
if (NS_SUCCEEDED(res) && brNode)
{

View File

@ -150,7 +150,7 @@ protected:
nsresult InsertBRIfNeeded(nsISelection *aSelection);
nsresult GetGoodSelPointForNode(nsIDOMNode *aNode, nsIEditor::EDirection aAction,
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 MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset);
nsresult MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset);