fix for 161106: rewrite of deletion in editor. fixes various deletion probs. r=glazman; sr=kin

This commit is contained in:
jfrancis%netscape.com 2002-08-29 07:49:31 +00:00
Родитель 1b155ee4fd
Коммит 0d49c95048
6 изменённых файлов: 326 добавлений и 640 удалений

Просмотреть файл

@ -238,4 +238,26 @@ class nsTrivialFunctor : public nsBoolDomIterFunctor
};
/******************************************************************************
* general dom point utility struct
*****************************************************************************/
struct DOMPoint
{
nsCOMPtr<nsIDOMNode> node;
PRInt32 offset;
DOMPoint() : node(0),offset(0) {}
DOMPoint(nsIDOMNode *aNode, PRInt32 aOffset) :
node(aNode),offset(aOffset) {}
void SetPoint(nsIDOMNode *aNode, PRInt32 aOffset)
{
node = aNode; offset = aOffset;
}
void GetPoint(nsCOMPtr<nsIDOMNode> &aNode, PRInt32 &aOffset)
{
aNode = node; aOffset = offset;
}
};
#endif // nsEditorUtils_h__

Просмотреть файл

@ -1652,6 +1652,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
PRBool *aCancel,
PRBool *aHandled)
{
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
// initialize out param
*aCancel = PR_FALSE;
@ -1694,11 +1695,10 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
// get the root element
nsCOMPtr<nsIDOMNode> bodyNode;
{
nsCOMPtr<nsIDOMElement> bodyElement;
res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyElement));
nsCOMPtr<nsIDOMElement> bodyNode;
res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyNode));
if (NS_FAILED(res)) return res;
if (!bodyElement) return NS_ERROR_UNEXPECTED;
bodyNode = do_QueryInterface(bodyElement);
if (!bodyNode) return NS_ERROR_UNEXPECTED;
}
if (bCollapsed)
@ -1715,99 +1715,75 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
if (*aCancel) return NS_OK;
#endif // IBMBIDI
if (!bPlaintext)
{
// Check for needed whitespace adjustments. If we need to delete
// just whitespace, that is handled here.
// CheckForWhitespaceDeletion also adjusts it's first two args to
// skip over invisible ws. So we need to check that we didn't cross a table
// element boundary afterwards.
nsCOMPtr<nsIDOMNode> visNode = startNode;
PRInt32 visOffset = startOffset;
res = CheckForWhitespaceDeletion(address_of(visNode), &visOffset,
aAction, aHandled);
if (NS_FAILED(res)) return res;
if (*aHandled) return NS_OK;
// dont cross any table elements
PRBool bInDifTblElems;
res = InDifferentTableElements(visNode, startNode, &bInDifTblElems);
if (NS_FAILED(res)) return res;
if (bInDifTblElems)
{
*aCancel = PR_TRUE;
return NS_OK;
}
// else reset startNode/startOffset
startNode = visNode; startOffset = visOffset;
}
// in a text node:
if (mHTMLEditor->IsTextNode(startNode))
{
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
PRUint32 strLength;
res = textNode->GetLength(&strLength);
// what's in the direction we are deleting?
nsWSRunObject wsObj(mHTMLEditor, startNode, startOffset);
nsCOMPtr<nsIDOMNode> visNode;
PRInt32 visOffset;
PRInt16 wsType;
// find next visible node
if (aAction == nsIEditor::eNext)
res = wsObj.NextVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
else
res = wsObj.PriorVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
if (NS_FAILED(res)) return res;
// at beginning of text node and backspaced?
if (!startOffset && (aAction == nsIEditor::ePrevious))
{
nsCOMPtr<nsIDOMNode> priorNode;
res = mHTMLEditor->GetPriorHTMLNode(startNode, address_of(priorNode));
if (NS_FAILED(res)) return res;
// if there is no prior node then cancel the deletion
if (!priorNode || !nsTextEditUtils::InBody(priorNode, mHTMLEditor))
if (!visNode) // can't find anything to delete!
{
*aCancel = PR_TRUE;
return res;
}
// block parents the same?
if (mHTMLEditor->HasSameBlockNodeParent(startNode, priorNode))
if (wsType==nsWSRunObject::eNormalWS)
{
// is prior node a text node?
if ( mHTMLEditor->IsTextNode(priorNode) )
{
// delete last character
PRUint32 offset;
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
nodeAsText = do_QueryInterface(priorNode);
nodeAsText->GetLength((PRUint32*)&offset);
NS_ENSURE_TRUE(offset, NS_ERROR_FAILURE);
res = aSelection->Collapse(priorNode,offset);
if (NS_FAILED(res)) return res;
if (!bPlaintext)
{
PRInt32 so = offset-1;
PRInt32 eo = offset;
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
address_of(priorNode), &so,
address_of(priorNode), &eo);
}
// just return without setting handled to true.
// default code will take care of actual deletion
// we found some visible ws to delete. Let ws code handle it.
if (aAction == nsIEditor::eNext)
res = wsObj.DeleteWSForward();
else
res = wsObj.DeleteWSBackward();
*aHandled = PR_TRUE;
return res;
}
// is prior node not a container? (ie, a br, hr, image...)
else if (!mHTMLEditor->IsContainer(priorNode)) // MOOSE: anchors not handled
else if (wsType==nsWSRunObject::eText)
{
if (!bPlaintext)
// found normal text to delete.
PRInt32 so = visOffset;
PRInt32 eo = visOffset+1;
if (aAction == nsIEditor::ePrevious)
{
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, priorNode);
if (NS_FAILED(res)) return res;
if (so == 0) return NS_ERROR_UNEXPECTED;
so--;
eo--;
}
// remember prior sibling to prior node, if any
return nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(visNode), &so, address_of(visNode), &eo);
}
else if ( (wsType==nsWSRunObject::eSpecial) ||
(wsType==nsWSRunObject::eBreak) ||
nsHTMLEditUtils::IsHR(visNode) )
{
// found break or image, or hr.
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, visNode);
if (NS_FAILED(res)) return res;
// remember sibling to visnode, if any
nsCOMPtr<nsIDOMNode> sibling, stepbrother;
mHTMLEditor->GetPriorHTMLSibling(priorNode, address_of(sibling));
// delete the break, and join like nodes if appropriate
res = mHTMLEditor->DeleteNode(priorNode);
if (aAction == nsIEditor::ePrevious)
mHTMLEditor->GetPriorHTMLSibling(visNode, address_of(sibling));
if (aAction == nsIEditor::eNext)
mHTMLEditor->GetNextHTMLSibling(visNode, address_of(sibling));
// delete the node, and join like nodes if appropriate
res = mHTMLEditor->DeleteNode(visNode);
if (NS_FAILED(res)) return res;
// we did something, so lets say so.
*aHandled = PR_TRUE;
// is there a prior node and are they siblings?
if (sibling)
sibling->GetNextSibling(getter_AddRefs(stepbrother));
{
if (aAction == nsIEditor::ePrevious)
mHTMLEditor->GetNextHTMLSibling(sibling, address_of(stepbrother));
if (aAction == nsIEditor::eNext)
mHTMLEditor->GetPriorHTMLSibling(sibling, address_of(stepbrother));
}
if (startNode == stepbrother)
{
// are they both text nodes?
@ -1822,364 +1798,111 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
}
return res;
}
else if ( IsInlineNode(priorNode) )
else if (wsType==nsWSRunObject::eOtherBlock)
{
if (!bPlaintext)
// next to a block. Look inside it and see if we can join to it
// first find the relavent nodes
nsCOMPtr<nsIDOMNode> leftNode, rightNode, leftParent, rightParent;
if (aAction == nsIEditor::ePrevious)
{
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, priorNode);
res = mHTMLEditor->GetLastEditableLeaf( visNode, address_of(leftNode));
if (NS_FAILED(res)) return res;
rightNode = startNode;
}
// remember where we are
PRInt32 offset;
nsCOMPtr<nsIDOMNode> node;
res = mHTMLEditor->GetNodeLocation(priorNode, address_of(node), &offset);
else
{
res = mHTMLEditor->GetFirstEditableLeaf( visNode, address_of(rightNode));
if (NS_FAILED(res)) return res;
// delete it
res = mHTMLEditor->DeleteNode(priorNode);
if (NS_FAILED(res)) return res;
// we did something, so lets say so.
*aHandled = PR_TRUE;
// fix up selection
res = aSelection->Collapse(node,offset);
return res;
}
else return NS_OK; // punt to default
leftNode = startNode;
}
// ---- deleting across blocks ---------------------------------------------
// dont cross any table elements
// dont cross table boundaries
PRBool bInDifTblElems;
res = InDifferentTableElements(startNode, priorNode, &bInDifTblElems);
if (NS_FAILED(res)) return res;
if (bInDifTblElems)
{
*aCancel = PR_TRUE;
return NS_OK;
}
res = InDifferentTableElements(leftNode, rightNode, &bInDifTblElems);
if (NS_FAILED(res) || bInDifTblElems) return res;
nsCOMPtr<nsIDOMNode> leftParent;
nsCOMPtr<nsIDOMNode> rightParent;
if (IsBlockNode(priorNode))
leftParent = priorNode;
// find the relavent blocks
if (IsBlockNode(leftNode))
leftParent = leftNode;
else
leftParent = mHTMLEditor->GetBlockNodeParent(priorNode);
if (IsBlockNode(startNode))
rightParent = startNode;
leftParent = mHTMLEditor->GetBlockNodeParent(leftNode);
if (IsBlockNode(rightNode))
rightParent = rightNode;
else
rightParent = mHTMLEditor->GetBlockNodeParent(startNode);
rightParent = mHTMLEditor->GetBlockNodeParent(rightNode);
// sanity checks
if (!leftParent || !rightParent)
return NS_ERROR_NULL_POINTER; // should not happen
return NS_ERROR_NULL_POINTER;
if (leftParent == rightParent)
return NS_ERROR_UNEXPECTED;
// now join them
nsCOMPtr<nsIDOMNode> selPointNode = startNode;
PRInt32 selPointOffset = startOffset;
{
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
res = JoinBlocks(aSelection, address_of(leftParent), address_of(rightParent), aCancel);
res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel);
*aHandled = PR_TRUE;
}
aSelection->Collapse(selPointNode, selPointOffset);
return res;
}
// at end of text node and deleted?
if ((startOffset == (PRInt32)strLength)
&& (aAction == nsIEditor::eNext))
else if (wsType==nsWSRunObject::eThisBlock)
{
nsCOMPtr<nsIDOMNode> nextNode;
res = mHTMLEditor->GetNextHTMLNode(startNode, address_of(nextNode));
// at edge of our block. Look beside it and see if we can join to an adjacent block
// first find the relavent nodes
nsCOMPtr<nsIDOMNode> leftNode, rightNode, leftParent, rightParent;
if (aAction == nsIEditor::ePrevious)
{
res = mHTMLEditor->GetPriorHTMLNode(visNode, address_of(leftNode));
if (NS_FAILED(res)) return res;
// if there is no next node, or it's not in the body, then cancel the deletion
if (!nextNode || !nsTextEditUtils::InBody(nextNode, mHTMLEditor))
rightNode = startNode;
}
else
{
*aCancel = PR_TRUE;
return res;
res = mHTMLEditor->GetNextHTMLNode( visNode, address_of(rightNode));
if (NS_FAILED(res)) return res;
leftNode = startNode;
}
// block parents the same?
if (mHTMLEditor->HasSameBlockNodeParent(startNode, nextNode))
{
// is next node a text node?
if ( mHTMLEditor->IsTextNode(nextNode) )
{
// delete first character
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
nodeAsText = do_QueryInterface(nextNode);
res = aSelection->Collapse(nextNode,0);
if (NS_FAILED(res)) return res;
if (!bPlaintext)
{
PRInt32 so = 0;
PRInt32 eo = 1;
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
address_of(nextNode), &so,
address_of(nextNode), &eo);
}
// just return without setting handled to true.
// default code will take care of actual deletion
return res;
}
// is next node not a container? (ie, a br, hr, image...)
else if (!mHTMLEditor->IsContainer(nextNode)) // MOOSE: anchors not handled
{
if (!bPlaintext)
{
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nextNode);
if (NS_FAILED(res)) return res;
}
// remember next sibling after nextNode, if any
nsCOMPtr<nsIDOMNode> sibling, stepbrother;
mHTMLEditor->GetNextHTMLSibling(nextNode, address_of(sibling));
// delete the break, and join like nodes if appropriate
res = mHTMLEditor->DeleteNode(nextNode);
if (NS_FAILED(res)) return res;
// we did something, so lets say so.
*aHandled = PR_TRUE;
// is there a prior node and are they siblings?
if (sibling)
sibling->GetPreviousSibling(getter_AddRefs(stepbrother));
if (startNode == stepbrother)
{
// are they both text nodes?
if (mHTMLEditor->IsTextNode(startNode) && mHTMLEditor->IsTextNode(sibling))
{
// if so, join them!
res = JoinNodesSmart(startNode, sibling, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
// fix up selection
res = aSelection->Collapse(selNode, selOffset);
}
}
return res;
}
else if ( IsInlineNode(nextNode) )
{
if (!bPlaintext)
{
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nextNode);
if (NS_FAILED(res)) return res;
}
// remember where we are
PRInt32 offset;
nsCOMPtr<nsIDOMNode> node;
res = mHTMLEditor->GetNodeLocation(nextNode, address_of(node), &offset);
if (NS_FAILED(res)) return res;
// delete it
res = mHTMLEditor->DeleteNode(nextNode);
if (NS_FAILED(res)) return res;
// we did something, so lets say so.
*aHandled = PR_TRUE;
// fix up selection
res = aSelection->Collapse(node,offset);
return res;
}
else return NS_OK; // punt to default
}
// ---- deleting across blocks ---------------------------------------------
// dont cross any table elements
// dont cross table boundaries
PRBool bInDifTblElems;
res = InDifferentTableElements(startNode, nextNode, &bInDifTblElems);
if (NS_FAILED(res)) return res;
if (bInDifTblElems)
{
*aCancel = PR_TRUE;
return NS_OK;
}
res = InDifferentTableElements(leftNode, rightNode, &bInDifTblElems);
if (NS_FAILED(res) || bInDifTblElems) return res;
nsCOMPtr<nsIDOMNode> leftParent;
nsCOMPtr<nsIDOMNode> rightParent;
if (IsBlockNode(startNode))
leftParent = startNode;
// find the relavent blocks
if (IsBlockNode(leftNode))
leftParent = leftNode;
else
leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
if (IsBlockNode(nextNode) || nsEditor::IsTextNode(nextNode))
{
rightParent = nextNode;
aSelection->Collapse(nextNode,0);
startNode = nextNode; startOffset = 0;
}
leftParent = mHTMLEditor->GetBlockNodeParent(leftNode);
if (IsBlockNode(rightNode))
rightParent = rightNode;
else
{
rightParent = mHTMLEditor->GetBlockNodeParent(nextNode);
nsEditor::GetNodeLocation(nextNode, address_of(startNode), &startOffset);
aSelection->Collapse(startNode, startOffset);
}
rightParent = mHTMLEditor->GetBlockNodeParent(rightNode);
// sanity checks
if (!leftParent || !rightParent)
return NS_ERROR_NULL_POINTER; // should not happen
return NS_ERROR_NULL_POINTER;
if (leftParent == rightParent)
return NS_ERROR_UNEXPECTED;
// now join them
nsCOMPtr<nsIDOMNode> selPointNode = startNode;
PRInt32 selPointOffset = startOffset;
{
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(selPointNode), &selPointOffset);
res = JoinBlocks(aSelection, address_of(leftParent), address_of(rightParent), aCancel);
res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel);
*aHandled = PR_TRUE;
}
aSelection->Collapse(selPointNode, selPointOffset);
return res;
}
// else in middle of text node. default will do right thing.
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
nodeAsText = do_QueryInterface(startNode);
if (!nodeAsText) return NS_ERROR_NULL_POINTER;
res = aSelection->Collapse(startNode,startOffset);
if (NS_FAILED(res)) return res;
if (!bPlaintext)
{
PRInt32 so = startOffset;
PRInt32 eo = startOffset;
if (aAction == nsIEditor::ePrevious)
so--; // we know so not zero - that case handled above
else
eo++; // we know eo not at end of text node - that case handled above
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
address_of(startNode), &so,
address_of(startNode), &eo);
}
// just return without setting handled to true.
// default code will take care of actual deletion
return res;
}
// else not in text node; we need to find right place to act on
else
{
nsCOMPtr<nsIDOMNode> nodeToDelete;
// first note that the right node to delete might be the one we
// are in. For example, if a list item is deleted one character at a time,
// eventually it will be empty (except for a moz-br). If the user hits
// backspace again, they expect the item itself to go away. Check to
// see if we are in an "empty" node.
// Note: do NOT delete table elements this way.
PRBool bIsEmptyNode;
res = mHTMLEditor->IsEmptyNode(startNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
if (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(startNode))
nodeToDelete = startNode;
else
{
// see if we are on empty line and need to delete it. This is true
// when a break is after a block and we are deleting backwards; or
// when a break is before a block and we are delting forwards. In
// these cases, we want to delete the break when we are between it
// and the block element, even though the break is on the "wrong"
// side of us.
nsCOMPtr<nsIDOMNode> maybeBreak;
nsCOMPtr<nsIDOMNode> maybeBlock;
if (aAction == nsIEditor::ePrevious)
{
res = mHTMLEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBlock));
if (NS_FAILED(res)) return res;
res = mHTMLEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBreak));
if (NS_FAILED(res)) return res;
}
else if (aAction == nsIEditor::eNext)
{
res = mHTMLEditor->GetPriorHTMLSibling(startNode, startOffset, address_of(maybeBreak));
if (NS_FAILED(res)) return res;
res = mHTMLEditor->GetNextHTMLSibling(startNode, startOffset, address_of(maybeBlock));
if (NS_FAILED(res)) return res;
}
if (maybeBreak && maybeBlock &&
nsTextEditUtils::IsBreak(maybeBreak) && IsBlockNode(maybeBlock))
nodeToDelete = maybeBreak;
else if (aAction == nsIEditor::ePrevious)
res = mHTMLEditor->GetPriorHTMLNode(startNode, startOffset, address_of(nodeToDelete));
else if (aAction == nsIEditor::eNext)
res = mHTMLEditor->GetNextHTMLNode(startNode, startOffset, address_of(nodeToDelete));
else
return NS_OK;
}
// don't delete the root element!
if (NS_FAILED(res)) return res;
if (!nodeToDelete) return NS_ERROR_NULL_POINTER;
if (mBody == nodeToDelete)
{
*aCancel = PR_TRUE;
return res;
}
// dont cross any table elements
PRBool bInDifTblElems;
res = InDifferentTableElements(startNode, nodeToDelete, &bInDifTblElems);
if (NS_FAILED(res)) return res;
if (bInDifTblElems)
{
*aCancel = PR_TRUE;
return NS_OK;
}
// if this node is text node, adjust selection
if (nsEditor::IsTextNode(nodeToDelete))
{
PRUint32 selPoint = 0;
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
nodeAsText = do_QueryInterface(nodeToDelete);
if (aAction == nsIEditor::ePrevious)
nodeAsText->GetLength(&selPoint);
res = aSelection->Collapse(nodeToDelete,selPoint);
return res;
}
else
{
// editable leaf node is not text; delete it.
// that's the default behavior
// EXCEPTION: if it's a mozBR, we have to check and see if
// there is a br in front of it. If so, we must delete both.
// else you get this: deletion code deletes mozBR, then selection
// adjusting code puts it back in. doh
if (nsTextEditUtils::IsMozBR(nodeToDelete))
{
nsCOMPtr<nsIDOMNode> brNode;
res = mHTMLEditor->GetPriorHTMLNode(nodeToDelete, address_of(brNode));
if (brNode && nsTextEditUtils::IsBreak(brNode))
{
// is brNode also a descendant of same block?
nsCOMPtr<nsIDOMNode> block, brBlock;
block = mHTMLEditor->GetBlockNodeParent(nodeToDelete);
brBlock = mHTMLEditor->GetBlockNodeParent(brNode);
if (block == brBlock)
{
// delete both breaks
if (!bPlaintext)
{
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, brNode);
if (NS_FAILED(res)) return res;
}
res = mHTMLEditor->DeleteNode(brNode);
if (NS_FAILED(res)) return res;
// fall through to delete other br
}
// else fall through
}
// else fall through
}
if (!bPlaintext)
{
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nodeToDelete);
if (NS_FAILED(res)) return res;
}
PRInt32 offset;
nsCOMPtr<nsIDOMNode> node;
res = nsEditor::GetNodeLocation(nodeToDelete, address_of(node), &offset);
if (NS_FAILED(res)) return res;
// adjust selection to be right after it
res = aSelection->Collapse(node, offset+1);
if (NS_FAILED(res)) return res;
res = mHTMLEditor->DeleteNode(nodeToDelete);
*aHandled = PR_TRUE;
return res;
}
}
return NS_OK;
}
// else we have a non collapsed selection
// first adjust the selection
@ -2359,14 +2082,14 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
* JoinNodesSmart is called (example, joining two list items together into one). If the elements
* are not the same type, or one is a descendant of the other, we instead destroy the right block
* placing it's children into leftblock. DTD containment rules are followed throughout.
* nsISelection *aSelection the selection.
* nsCOMPtr<nsIDOMNode> *aLeftBlock pointer to the left block
* nsCOMPtr<nsIDOMNode> *aRightBlock pointer to the right block; will have contents moved to left block
* PRBool *aCanceled return TRUE if we had to cancel operation
*/
nsresult
nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLeftBlock,
nsCOMPtr<nsIDOMNode> *aRightBlock, PRBool *aCanceled)
nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
nsCOMPtr<nsIDOMNode> *aRightBlock,
PRBool *aCanceled)
{
if (!aLeftBlock || !aRightBlock || !*aLeftBlock || !*aRightBlock) return NS_ERROR_NULL_POINTER;
if (nsHTMLEditUtils::IsTableElement(*aLeftBlock) || nsHTMLEditUtils::IsTableElement(*aRightBlock))
@ -2376,6 +2099,25 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
return NS_OK;
}
// make sure we dont 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(*aRightBlock))
{
nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(*aRightBlock);
*aRightBlock = realRight;
}
// bail if both blocks the same
if (*aLeftBlock == *aRightBlock)
{
*aCanceled = PR_TRUE;
return NS_OK;
}
// special rule here: if we are trying to join list items, and they are in different lists,
// join the lists instead.
PRBool bMergeLists = PR_FALSE;
@ -2407,17 +2149,19 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
nsresult res = NS_OK;
PRInt32 rightOffset = 0;
PRInt32 leftOffset = -1;
// theOffset below is where you find yourself in aRightBlock when you traverse upwards
// from aLeftBlock
if (nsHTMLEditUtils::IsDescendantOf(*aLeftBlock, *aRightBlock, &theOffset))
if (nsHTMLEditUtils::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.
theOffset++;
rightOffset++;
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBlockEnd);
if (NS_FAILED(res)) return res;
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aRightBlock, nsWSRunObject::kAfterBlock, &theOffset);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aRightBlock, nsWSRunObject::kAfterBlock, &rightOffset);
if (NS_FAILED(res)) return res;
// Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode;
@ -2425,7 +2169,7 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
if (NS_FAILED(res)) return res;
if (bMergeLists)
{
// idea here is to take all list items and sublists in rightList that are past
// idea here is to take all children in rightList that are past
// theOffset, and pull them into leftlist.
nsCOMPtr<nsIDOMNode> childToMove;
nsCOMPtr<nsIContent> parent(do_QueryInterface(rightList)), child;
@ -2436,36 +2180,36 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
childToMove = do_QueryInterface(child);
res = mHTMLEditor->MoveNode(childToMove, leftList, -1);
if (NS_FAILED(res)) return res;
parent->ChildAt(theOffset, *getter_AddRefs(child));
parent->ChildAt(rightOffset, *getter_AddRefs(child));
}
}
else
{
res = MoveBlock(aSelection, *aLeftBlock, -1);
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 (nsHTMLEditUtils::IsDescendantOf(*aRightBlock, *aLeftBlock, &theOffset))
else if (nsHTMLEditUtils::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);
if (NS_FAILED(res)) return res;
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBeforeBlock, &theOffset);
res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor, aLeftBlock, nsWSRunObject::kBeforeBlock, &leftOffset);
if (NS_FAILED(res)) return res;
// Do br adjustment.
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBeforeBlock, address_of(brNode), theOffset);
res = CheckForInvisibleBR(*aLeftBlock, kBeforeBlock, address_of(brNode), leftOffset);
if (NS_FAILED(res)) return res;
if (bMergeLists)
{
res = MoveContents(rightList, leftList, &theOffset);
res = MoveContents(rightList, leftList, &leftOffset);
}
else
{
res = MoveBlock(aSelection, *aLeftBlock, theOffset);
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
}
if (brNode) mHTMLEditor->DeleteNode(brNode);
}
@ -2496,7 +2240,7 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
else
{
// nodes are disimilar types.
res = MoveBlock(aSelection, *aLeftBlock);
res = MoveBlock(*aLeftBlock, *aRightBlock, leftOffset, rightOffset);
}
if (brNode) mHTMLEditor->DeleteNode(brNode);
}
@ -2505,25 +2249,22 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
/*****************************************************************************************************
* MoveBlock: this method is used to move the "block" implied by the selection into aNewParent.
* MoveBlock: this method is used to move the content from rightBlock into leftBlock
* Note that the "block" might merely be inline nodes between <br>s, or between blocks, etc.
* DTD containment rules are followed throughout.
* nsISelection *aSelection the selection.
* nsIDOMNode *aNewParent parent to receive moved content
* PRInt32 aOffset offset in aNewParent to move content to
* nsIDOMNode *aLeftBlock parent to receive moved content
* nsIDOMNode *aRightBlock parent to provide moved content
* PRInt32 aLeftOffset offset in aLeftBlock to move content to
* PRInt32 aRightOffset offset in aLeftBlock to move content to
*/
nsresult
nsHTMLEditRules::MoveBlock(nsISelection *aSelection, nsIDOMNode *aNewParent, PRInt32 aOffset)
nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, PRInt32 aLeftOffset, PRInt32 aRightOffset)
{
if (!aSelection) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsISupportsArray> arrayOfNodes;
nsCOMPtr<nsISupports> isupports;
nsCOMPtr<nsIDOMNode> curNode;
// GetNodesFromSelection is the workhorse that figures out what we wnat to move.
// TODO: it is bogus to have to pass selectin to JoinBlocks() and thru to MoveBlock. We only
// need it because of GetNodesFromSelection(). I need to write other versions of GetNodesFromSelection()
// that accept a nsIDOMRange or a DOMPoint.
nsresult res = GetNodesFromSelection(aSelection, kMakeList, address_of(arrayOfNodes), PR_TRUE);
// GetNodesFromPoint is the workhorse that figures out what we wnat to move.
nsresult res = GetNodesFromPoint(DOMPoint(aRightBlock,aRightOffset), kMakeList, address_of(arrayOfNodes), PR_TRUE);
if (NS_FAILED(res)) return res;
PRUint32 listCount;
arrayOfNodes->Count(&listCount);
@ -2536,14 +2277,14 @@ nsHTMLEditRules::MoveBlock(nsISelection *aSelection, nsIDOMNode *aNewParent, PRI
if (IsBlockNode(curNode))
{
// For block nodes, move their contents only, then delete block.
res = MoveContents(curNode, aNewParent, &aOffset);
res = MoveContents(curNode, aLeftBlock, &aLeftOffset);
if (NS_FAILED(res)) return res;
res = mHTMLEditor->DeleteNode(curNode);
}
else
{
// otherwise move the content as is, checking against the dtd.
res = MoveNodeSmart(curNode, aNewParent, &aOffset);
res = MoveNodeSmart(curNode, aLeftBlock, &aLeftOffset);
}
}
return res;
@ -2598,6 +2339,7 @@ nsresult
nsHTMLEditRules::MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset)
{
if (!aSource || !aDest || !aOffset) return NS_ERROR_NULL_POINTER;
if (aSource == aDest) return NS_ERROR_ILLEGAL_VALUE;
nsCOMPtr<nsIDOMNode> child;
nsAutoString tag;
@ -4471,62 +4213,6 @@ nsHTMLEditRules::CheckForEmptyBlock(nsIDOMNode *aStartNode,
return res;
}
///////////////////////////////////////////////////////////////////////////
// CheckForWhitespaceDeletion: Called by WillDeleteSelection to detect and handle
// case of deleting whitespace. If we need to skip over
// whitespace, visNode and visOffset are updated to new
// location to handle deletion.
//
nsresult
nsHTMLEditRules::CheckForWhitespaceDeletion(nsCOMPtr<nsIDOMNode> *ioStartNode,
PRInt32 *ioStartOffset,
PRInt32 aAction,
PRBool *aHandled)
{
nsresult res = NS_OK;
// gather up ws data here. We may be next to non-significant ws.
nsWSRunObject wsObj(mHTMLEditor, *ioStartNode, *ioStartOffset);
nsCOMPtr<nsIDOMNode> visNode;
PRInt32 visOffset;
PRInt16 wsType;
if (aAction == nsIEditor::ePrevious)
{
res = wsObj.PriorVisibleNode(*ioStartNode, *ioStartOffset, address_of(visNode), &visOffset, &wsType);
// note that visOffset is _after_ what we are about to delete.
}
else if (aAction == nsIEditor::eNext)
{
res = wsObj.NextVisibleNode(*ioStartNode, *ioStartOffset, address_of(visNode), &visOffset, &wsType);
// note that visOffset is _before_ what we are about to delete.
}
if (NS_SUCCEEDED(res))
{
if (wsType==nsWSRunObject::eNormalWS)
{
// we found some visible ws to delete. Let ws code handle it.
if (aAction == nsIEditor::ePrevious)
res = wsObj.DeleteWSBackward();
else if (aAction == nsIEditor::eNext)
res = wsObj.DeleteWSForward();
*aHandled = PR_TRUE;
}
else if (wsType==nsWSRunObject::eText)
{
// reposition startNode and startOffset so that we skip over any non-significant ws
*ioStartNode = visNode;
*ioStartOffset = visOffset;
}
else if (visNode)
{
// reposition startNode and startOffset so that we skip over any non-significant ws
res = nsEditor::GetNodeLocation(visNode, ioStartNode, ioStartOffset);
if (aAction == nsIEditor::ePrevious)
(*ioStartOffset)++;
}
}
return res;
}
nsresult
nsHTMLEditRules::CheckForInvisibleBR(nsIDOMNode *aBlock,
BRLocation aWhere,
@ -4534,7 +4220,7 @@ nsHTMLEditRules::CheckForInvisibleBR(nsIDOMNode *aBlock,
PRInt32 aOffset)
{
// for now I'm relying on fact that user has scrubbed any invisible whitespace
// inthe vicinity, so we don't need morecomplicated code here to check for that.
// in the vicinity, so we don't need more complicated code here to check for that.
if (!aBlock || !outBRNode) return NS_ERROR_NULL_POINTER;
*outBRNode = nsnull;
if (aWhere == kBlockEnd)
@ -5098,37 +4784,23 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
res = nsEditor::GetNodeLocation(aNode, address_of(parent), &offset);
if (NS_FAILED(res)) return res;
}
else
// look back through any further inline nodes that
// aren't across a <br> from us, and that are enclosed in the same block.
nsCOMPtr<nsIDOMNode> priorNode;
res = mHTMLEditor->GetPriorHTMLNode(parent, offset, address_of(priorNode), PR_TRUE);
while (priorNode && NS_SUCCEEDED(res))
{
node = nsEditor::GetChildAt(parent,offset);
}
if (!node) node = parent;
// if this is an inline node,
// back up through any prior inline nodes that
// aren't across a <br> from us.
if (!IsBlockNode(node))
{
nsCOMPtr<nsIDOMNode> block = nsHTMLEditor::GetBlockNodeParent(node);
nsCOMPtr<nsIDOMNode> prevNode, prevNodeBlock;
res = mHTMLEditor->GetPriorHTMLNode(node, address_of(prevNode));
while (prevNode && NS_SUCCEEDED(res))
{
prevNodeBlock = nsHTMLEditor::GetBlockNodeParent(prevNode);
if (prevNodeBlock != block)
if (mHTMLEditor->IsVisBreak(priorNode))
break;
if (nsTextEditUtils::IsBreak(prevNode))
if (IsBlockNode(priorNode))
break;
if (IsBlockNode(prevNode))
break;
node = prevNode;
res = mHTMLEditor->GetPriorHTMLNode(node, address_of(prevNode));
}
node = priorNode;
res = mHTMLEditor->GetPriorHTMLNode(node, address_of(priorNode), PR_TRUE);
}
// finding the real start for this point. look up the tree for as long as we are the
// first node in the container, and as long as we haven't hit the body node.
if (!nsTextEditUtils::IsBody(node))
@ -5164,41 +4836,14 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
res = nsEditor::GetNodeLocation(aNode, address_of(parent), &offset);
if (NS_FAILED(res)) return res;
}
else
{
if (offset) offset--; // we want node _before_ offset
else
{ // XXX HACK MOOSE
// The logic in this routine, while it works almost all the time, is fundamentally flawed.
// I need to rewrite it using something like nsWSRunObject::GetPreviousWSNode(). Til then
// I need this hack to correct the most commmon error here.
// we couldn't get a prior node in this parent. If next node is a br, just skip
// the promotion altogether. We have a br after us. If we fell through to code below,
// it would think br was before us and would proceed merrily along.
node = nsEditor::GetChildAt(parent,offset);
if (node && mHTMLEditor->IsVisBreak(node))
return NS_OK; // default values used.
}
node = nsEditor::GetChildAt(parent,offset);
}
if (!node) node = parent;
// if this is an inline node,
// look ahead through any further inline nodes that
// aren't across a <br> from us, and that are enclosed in the same block.
if (!IsBlockNode(node))
{
nsCOMPtr<nsIDOMNode> block = nsHTMLEditor::GetBlockNodeParent(node);
nsCOMPtr<nsIDOMNode> nextNode, nextNodeBlock;
res = mHTMLEditor->GetNextHTMLNode(node, address_of(nextNode));
nsCOMPtr<nsIDOMNode> nextNode;
res = mHTMLEditor->GetNextHTMLNode(parent, offset, address_of(nextNode), PR_TRUE);
while (nextNode && NS_SUCCEEDED(res))
{
nextNodeBlock = nsHTMLEditor::GetBlockNodeParent(nextNode);
if (nextNodeBlock != block)
break;
if (mHTMLEditor->IsVisBreak(nextNode))
{
node = nextNode;
@ -5207,8 +4852,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt
if (IsBlockNode(nextNode))
break;
node = nextNode;
res = mHTMLEditor->GetNextHTMLNode(node, address_of(nextNode));
}
res = mHTMLEditor->GetNextHTMLNode(node, address_of(nextNode), PR_TRUE);
}
// finding the real end for this point. look up the tree for as long as we are the
@ -5948,6 +5592,51 @@ nsHTMLEditRules::GetHighestInlineParent(nsIDOMNode* aNode)
}
///////////////////////////////////////////////////////////////////////////
// GetNodesFromPoint: given a particular operation, construct a list
// of nodes from a point that will be operated on.
//
nsresult
nsHTMLEditRules::GetNodesFromPoint(DOMPoint point,
PRInt32 operation,
nsCOMPtr<nsISupportsArray> *arrayOfNodes,
PRBool dontTouchContent)
{
if (!arrayOfNodes) return NS_ERROR_NULL_POINTER;
nsresult res;
// get our point
nsCOMPtr<nsIDOMNode> node;
PRInt32 offset;
point.GetPoint(node, offset);
// use it to make a range
nsCOMPtr<nsIDOMRange> range = do_CreateInstance(kRangeCID);
res = range->SetStart(node, offset);
if (NS_FAILED(res)) return res;
/* SetStart() will also set the end for this new range
res = range->SetEnd(node, offset);
if (NS_FAILED(res)) return res; */
// expand the range to include adjacent inlines
res = PromoteRange(range, operation);
if (NS_FAILED(res)) return res;
// make array of ranges
nsCOMPtr<nsISupportsArray> arrayOfRanges;
res = NS_NewISupportsArray(getter_AddRefs(arrayOfRanges));
if (NS_FAILED(res)) return res;
// stuff new opRange into array
nsCOMPtr<nsISupports> isupports = do_QueryInterface(range);
arrayOfRanges->AppendElement(isupports);
// use these ranges to contruct a list of nodes to act on.
res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent);
return res;
}
///////////////////////////////////////////////////////////////////////////
// GetNodesFromSelection: given a particular operation, construct a list
// of nodes from the selection that will be operated on.

Просмотреть файл

@ -46,6 +46,7 @@
#include "nsISupportsArray.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsEditorUtils.h"
class nsISupportsArray;
class nsVoidArray;
@ -133,9 +134,8 @@ protected:
nsresult SplitMailCites(nsISelection *aSelection, PRBool aPlaintext, PRBool *aHandled);
nsresult WillDeleteSelection(nsISelection *aSelection, nsIEditor::EDirection aAction,
PRBool *aCancel, PRBool *aHandled);
nsresult JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLeftBlock,
nsCOMPtr<nsIDOMNode> *aRightBlock, PRBool *aCanceled);
nsresult MoveBlock(nsISelection *aSelection, nsIDOMNode *aNewParent, PRInt32 aOffset = -1);
nsresult JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock, nsCOMPtr<nsIDOMNode> *aRightBlock, PRBool *aCanceled);
nsresult MoveBlock(nsIDOMNode *aLeft, nsIDOMNode *aRight, PRInt32 aLeftOffset, PRInt32 aRightOffset);
nsresult MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset);
nsresult MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset);
nsresult DeleteNonTableElements(nsIDOMNode *aNode);
@ -180,10 +180,6 @@ protected:
nsIDOMNode *aBodyNode,
nsISelection *aSelection,
PRBool *aHandled);
nsresult CheckForWhitespaceDeletion(nsCOMPtr<nsIDOMNode> *ioStartNode,
PRInt32 *ioStartOffset,
PRInt32 aAction,
PRBool *aHandled);
nsresult CheckForInvisibleBR(nsIDOMNode *aBlock, nsHTMLEditRules::BRLocation aWhere,
nsCOMPtr<nsIDOMNode> *outBRNode, PRInt32 aOffset=0);
PRBool ExpandSelectionForDeletion(nsISelection *aSelection);
@ -206,6 +202,10 @@ protected:
PRBool aDontTouchContent=PR_FALSE);
nsresult GetChildNodesForOperation(nsIDOMNode *inNode,
nsCOMPtr<nsISupportsArray> *outArrayOfNodes);
nsresult GetNodesFromPoint(DOMPoint point,
PRInt32 operation,
nsCOMPtr<nsISupportsArray> *arrayOfNodes,
PRBool dontTouchContent);
nsresult GetNodesFromSelection(nsISelection *selection,
PRInt32 operation,
nsCOMPtr<nsISupportsArray> *arrayOfNodes,

Просмотреть файл

@ -4859,7 +4859,7 @@ nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOu
if (!aOutLastChild || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutLastChild = nsnull;
*aOutLastChild = aNode;
// find last editable child
nsCOMPtr<nsIDOMNode> child;
@ -4879,8 +4879,6 @@ nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOu
return res;
}
// jfrancis or glazman may want to use this method (currently it's unused)
#ifdef XXX_DEAD_CODE
nsresult
nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf)
{
@ -4888,7 +4886,7 @@ nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOu
if (!aOutFirstLeaf || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutFirstLeaf = nsnull;
*aOutFirstLeaf = aNode;
// find leftmost leaf
nsCOMPtr<nsIDOMNode> child;
@ -4913,7 +4911,6 @@ nsHTMLEditor::GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOu
*aOutFirstLeaf = child;
return res;
}
#endif
nsresult

Просмотреть файл

@ -728,10 +728,7 @@ protected:
nsresult GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild);
nsresult GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild);
#ifdef XXX_DEAD_CODE
// these should be removed some day by jfrancis: GetFirstEditableLeaf & GetLastEditableLeaf
nsresult GetFirstEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstLeaf);
#endif
nsresult GetLastEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastLeaf);
nsresult GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver);

Просмотреть файл

@ -45,6 +45,7 @@
#include "nsISupportsArray.h"
#include "nsITextContent.h"
#include "nsIEditor.h"
#include "nsEditorUtils.h"
class nsIDOMDocument;
class nsIDOMNode;
@ -240,26 +241,6 @@ class nsWSRunObject
mTextNode(aTextNode),mOffset(aOffset),mChar(aChar) {}
};
// general dom point utility struct
struct DOMPoint
{
nsCOMPtr<nsIDOMNode> node;
PRInt32 offset;
DOMPoint() : node(0),offset(0) {}
DOMPoint(nsIDOMNode *aNode, PRInt32 aOffset) :
node(aNode),offset(aOffset) {}
void SetPoint(nsIDOMNode *aNode, PRInt32 aOffset)
{
node = aNode; offset = aOffset;
}
void GetPoint(nsCOMPtr<nsIDOMNode> &aNode, PRInt32 &aOffset)
{
aNode = node; aOffset = offset;
}
};
// protected methods ---------------------------------------------------------
// tons of utility methods.