* 113691 : Composer freezes when removing multiple ol or ul tags.
    * 113290 : Freezes when deleting some formatted text.
    * 112144 : Attempt to reply to mail freezes entire Mozilla.
    * 103685 : Caret disappears after deleting a blockquote w/ paragraph style.
    * 121282 : Pressing enter causes caret to jump from end of <p> to the<h1>.
    * 117418 : rewrote some code in nsWSRunObject.cpp: fixed warnings in GetWSNodes().
    * 114911 : can't join two lists using delete key.
    * 120000 : Indent list inside table causes table to split.
r=fm;sr=kin
This commit is contained in:
jfrancis%netscape.com 2002-02-07 03:56:20 +00:00
Родитель 5eab47f3cb
Коммит d5f3c6a9c2
5 изменённых файлов: 449 добавлений и 427 удалений

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

@ -86,6 +86,7 @@
const static PRUnichar nbsp = 160;
static NS_DEFINE_IID(kContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_IID(kLeafIteratorCID, NS_LEAFITERATOR_CID);
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
@ -139,13 +140,13 @@ class nsEmptyFunctor : public nsBoolDomIterFunctor
public:
nsEmptyFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
virtual PRBool operator()(nsIDOMNode* aNode) // used to build list of empty li's and td's
{
if (nsHTMLEditUtils::IsListItem(aNode) || nsHTMLEditUtils::IsTableCellOrCaption(aNode))
{
PRBool bIsEmptyNode;
nsresult res = mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, PR_FALSE, PR_FALSE);
if (NS_FAILED(res)) return PR_FALSE;
if (bIsEmptyNode
&& (nsHTMLEditUtils::IsListItem(aNode) || nsHTMLEditUtils::IsTableCellOrCaption(aNode)))
{
if (bIsEmptyNode)
return PR_TRUE;
}
return PR_FALSE;
@ -2180,7 +2181,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
* JoinBlocks: this method is used to join two block elements. The right element is always joined
* to the left element. If the elements are the same type and not nested within each other,
* JoinNodesSmart is called (example, joining two list items together into one). If the elements
* are not the same type, or one is a ddescendant of the other, we instead destroy the right block
* 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
@ -2199,11 +2200,38 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
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;
nsAutoString existingListStr;
PRInt32 theOffset;
nsCOMPtr<nsIDOMNode> leftList, 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 (!nsHTMLEditUtils::IsDescendantOf(leftList, *aRightBlock, &theOffset) &&
!nsHTMLEditUtils::IsDescendantOf(rightList, *aLeftBlock, &theOffset))
{
*aLeftBlock = leftList;
*aRightBlock = rightList;
bMergeLists = PR_TRUE;
mHTMLEditor->GetTagString(leftList, existingListStr);
ToLowerCase(existingListStr);
}
}
}
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
nsresult res = NS_OK;
PRInt32 theOffset;
// theOffset below is where you find yourself in aRightBlock when you traverse upwards
// from aLeftBlock
if (nsHTMLEditUtils::IsDescendantOf(*aLeftBlock, *aRightBlock, &theOffset))
@ -2219,7 +2247,26 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode));
if (NS_FAILED(res)) return res;
if (bMergeLists)
{
// idea here is to take all list items and sublists in rightList that are past
// theOffset, and pull them into leftlist.
nsCOMPtr<nsIDOMNode> childToMove;
nsCOMPtr<nsIContent> parent(do_QueryInterface(rightList)), child;
if (!parent) return NS_ERROR_NULL_POINTER;
parent->ChildAt(theOffset, *getter_AddRefs(child));
while (child)
{
childToMove = do_QueryInterface(child);
res = mHTMLEditor->MoveNode(childToMove, leftList, -1);
if (NS_FAILED(res)) return res;
parent->ChildAt(theOffset, *getter_AddRefs(child));
}
}
else
{
res = MoveBlock(aSelection, *aLeftBlock, -1);
}
if (brNode) mHTMLEditor->DeleteNode(brNode);
}
// theOffset below is where you find yourself in aLeftBlock when you traverse upwards
@ -2236,7 +2283,14 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBeforeBlock, address_of(brNode), theOffset);
if (NS_FAILED(res)) return res;
if (bMergeLists)
{
res = MoveContents(rightList, leftList, &theOffset);
}
else
{
res = MoveBlock(aSelection, *aLeftBlock, theOffset);
}
if (brNode) mHTMLEditor->DeleteNode(brNode);
}
else
@ -2253,12 +2307,15 @@ nsHTMLEditRules::JoinBlocks(nsISelection *aSelection, nsCOMPtr<nsIDOMNode> *aLef
nsCOMPtr<nsIDOMNode> brNode;
res = CheckForInvisibleBR(*aLeftBlock, kBlockEnd, address_of(brNode));
if (NS_FAILED(res)) return res;
if (mHTMLEditor->NodesSameType(*aLeftBlock, *aRightBlock))
if (bMergeLists || mHTMLEditor->NodesSameType(*aLeftBlock, *aRightBlock))
{
// nodes are same type. merge them.
nsCOMPtr<nsIDOMNode> parent;
PRInt32 offset;
res = JoinNodesSmart(*aLeftBlock, *aRightBlock, address_of(parent), &offset);
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMNode> newBlock;
res = ConvertListType(*aRightBlock, address_of(newBlock), existingListStr, NS_LITERAL_STRING("li"));
}
else
{
@ -3138,7 +3195,6 @@ nsresult
nsHTMLEditRules::WillHTMLIndent(nsISelection *aSelection, PRBool *aCancel, PRBool * aHandled)
{
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
nsresult res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
@ -3211,6 +3267,10 @@ nsHTMLEditRules::WillHTMLIndent(nsISelection *aSelection, PRBool *aCancel, PRBoo
// here's where we actually figure out what to do
nsCOMPtr<nsISupports> isupports = dont_AddRef(arrayOfNodes->ElementAt(i));
nsCOMPtr<nsIDOMNode> curNode( do_QueryInterface(isupports ) );
// Ignore all non-editable nodes. Leave them be.
if (!mHTMLEditor->IsEditable(curNode)) continue;
PRInt32 offset;
res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
if (NS_FAILED(res)) return res;
@ -3243,6 +3303,8 @@ nsHTMLEditRules::WillHTMLIndent(nsISelection *aSelection, PRBool *aCancel, PRBoo
// tuck the node into the end of the active list
res = mHTMLEditor->MoveNode(curNode, curList, -1);
if (NS_FAILED(res)) return res;
// forget curQuote, if any
curQuote = nsnull;
}
else // not a list item, use blockquote?
@ -3518,8 +3580,10 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
///////////////////////////////////////////////////////////////////////////
// ConvertListType: convert list type and list item type.
//
// RemovePartOfBlock: split aBlock and move aStartChild to aEndChild out
// of aBlock. return left side of block (if any) in
// aLeftNode. return right side of block (if any) in
// aRightNode.
//
nsresult
nsHTMLEditRules::RemovePartOfBlock(nsIDOMNode *aBlock,
@ -3581,7 +3645,7 @@ nsHTMLEditRules::ConvertListType(nsIDOMNode *aList,
const nsAReadableString& aItemType)
{
if (!aList || !outList) return NS_ERROR_NULL_POINTER;
*outList = aList; // we migvht not need to change the list
*outList = aList; // we might not need to change the list
nsresult res = NS_OK;
nsCOMPtr<nsIDOMNode> child, temp;
aList->GetFirstChild(getter_AddRefs(child));
@ -4224,13 +4288,16 @@ nsHTMLEditRules::CheckForInvisibleBR(nsIDOMNode *aBlock,
///////////////////////////////////////////////////////////////////////////
// GetInnerContent: aList and aTbl allow the caller to specify what kind
// of content to "look inside". If aTbl is true, look inside
// any table content, and append the inner content to the
// supplied issupportsarray. Similarly with aList and list content.
// any table content, and insert the inner content into the
// supplied issupportsarray at offset aIndex.
// Similarly with aList and list content.
// aIndex is updated to point past inserted elements.
//
nsresult
nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRBool aList, PRBool aTbl)
nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes,
PRInt32 *aIndex, PRBool aList, PRBool aTbl)
{
if (!aNode || !outArrayOfNodes) return NS_ERROR_NULL_POINTER;
if (!aNode || !outArrayOfNodes || !aIndex) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsISupports> isupports;
@ -4242,13 +4309,14 @@ nsHTMLEditRules::GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOf
nsHTMLEditUtils::IsListItem(node) ) )
|| ( aTbl && nsHTMLEditUtils::IsTableElement(node) ) )
{
res = GetInnerContent(node, outArrayOfNodes, aList, aTbl);
res = GetInnerContent(node, outArrayOfNodes, aIndex, aList, aTbl);
if (NS_FAILED(res)) return res;
}
else
{
isupports = do_QueryInterface(node);
outArrayOfNodes->AppendElement(isupports);
outArrayOfNodes->InsertElementAt(isupports, *aIndex);
(*aIndex)++;
}
nsCOMPtr<nsIDOMNode> tmp;
res = node->GetNextSibling(getter_AddRefs(tmp));
@ -4820,8 +4888,9 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
nsCOMPtr<nsIDOMNode> node( do_QueryInterface(isupports) );
if (nsHTMLEditUtils::IsListItem(node))
{
PRInt32 j=i;
(*outArrayOfNodes)->RemoveElementAt(i);
res = GetInnerContent(node, *outArrayOfNodes);
res = GetInnerContent(node, *outArrayOfNodes, &j);
if (NS_FAILED(res)) return res;
}
}
@ -4839,8 +4908,9 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
nsCOMPtr<nsIDOMNode> node( do_QueryInterface(isupports) );
if ( (nsHTMLEditUtils::IsTableElement(node) && !nsHTMLEditUtils::IsTable(node)) )
{
PRInt32 j=i;
(*outArrayOfNodes)->RemoveElementAt(i);
res = GetInnerContent(node, *outArrayOfNodes);
res = GetInnerContent(node, *outArrayOfNodes, &j);
if (NS_FAILED(res)) return res;
}
}
@ -4856,8 +4926,9 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
nsCOMPtr<nsIDOMNode> node( do_QueryInterface(isupports) );
if (nsHTMLEditUtils::IsDiv(node))
{
PRInt32 j=i;
(*outArrayOfNodes)->RemoveElementAt(i);
res = GetInnerContent(node, *outArrayOfNodes, PR_FALSE, PR_FALSE);
res = GetInnerContent(node, *outArrayOfNodes, &j, PR_FALSE, PR_FALSE);
if (NS_FAILED(res)) return res;
}
}
@ -5029,8 +5100,9 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr<nsISupportsArray> *outArrayOfNodes,
if ( (nsHTMLEditUtils::IsTableElement(testNode) && !nsHTMLEditUtils::IsTable(testNode))
|| nsHTMLEditUtils::IsDiv(testNode) )
{
PRInt32 j=i;
(*outArrayOfNodes)->RemoveElementAt(i);
res = GetInnerContent(testNode, *outArrayOfNodes, PR_FALSE);
res = GetInnerContent(testNode, *outArrayOfNodes, &j, PR_FALSE);
if (NS_FAILED(res)) return res;
}
}
@ -5087,7 +5159,8 @@ nsHTMLEditRules::LookInsideDivBQandList(nsISupportsArray *aNodeArray)
aNodeArray->RemoveElementAt(0);
if ((nsHTMLEditUtils::IsDiv(curNode) || nsHTMLEditUtils::IsBlockquote(curNode)))
{
res = GetInnerContent(curNode, aNodeArray, PR_FALSE, PR_FALSE);
PRInt32 j=0;
res = GetInnerContent(curNode, aNodeArray, &j, PR_FALSE, PR_FALSE);
}
else
{
@ -5158,8 +5231,9 @@ nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr<nsISupportsArray> *outArrayOfN
nsHTMLEditUtils::IsList(testNode) ||
nsHTMLEditUtils::IsListItem(testNode) )
{
PRInt32 j=i;
(*outArrayOfNodes)->RemoveElementAt(i);
res = GetInnerContent(testNode, *outArrayOfNodes);
res = GetInnerContent(testNode, *outArrayOfNodes, &j);
if (NS_FAILED(res)) return res;
}
}
@ -5517,8 +5591,7 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
// just fall out to default of inserting a BR
return res;
}
if (nsTextEditUtils::IsBreak(sibling)
&& !nsTextEditUtils::HasMozAttr(sibling))
if (IsVisBreak(sibling) && !nsTextEditUtils::HasMozAttr(sibling))
{
PRInt32 newOffset;
*aCancel = PR_TRUE;
@ -5559,8 +5632,7 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
// just fall out to default of inserting a BR
return res;
}
if (nsTextEditUtils::IsBreak(sibling)
&& !nsTextEditUtils::HasMozAttr(sibling))
if (IsVisBreak(sibling) && !nsTextEditUtils::HasMozAttr(sibling))
{
PRInt32 newOffset;
*aCancel = PR_TRUE;
@ -5591,14 +5663,12 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
nsCOMPtr<nsIDOMNode> nearNode;
res = mHTMLEditor->GetPriorHTMLNode(aNode, aOffset, address_of(nearNode));
if (NS_FAILED(res)) return res;
if (!nearNode || !nsTextEditUtils::IsBreak(nearNode)
|| nsTextEditUtils::HasMozAttr(nearNode))
if (!nearNode || !IsVisBreak(nearNode) || nsTextEditUtils::HasMozAttr(nearNode))
{
// is there a BR after to it?
// is there a BR after it?
res = mHTMLEditor->GetNextHTMLNode(aNode, aOffset, address_of(nearNode));
if (NS_FAILED(res)) return res;
if (!nearNode || !nsTextEditUtils::IsBreak(nearNode)
|| nsTextEditUtils::HasMozAttr(nearNode))
if (!nearNode || !IsVisBreak(nearNode) || nsTextEditUtils::HasMozAttr(nearNode))
{
// just fall out to default of inserting a BR
return res;
@ -5810,7 +5880,7 @@ nsHTMLEditRules::MakeBlockquote(nsISupportsArray *arrayOfNodes)
///////////////////////////////////////////////////////////////////////////
// RemoveBlockStyle: make the list nodes have no special block type.
// RemoveBlockStyle: make the nodes have no special block type.
//
nsresult
nsHTMLEditRules::RemoveBlockStyle(nsISupportsArray *arrayOfNodes)
@ -6473,8 +6543,8 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection
nsCOMPtr<nsIDOMNode> nearNode;
res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode));
if (NS_FAILED(res)) return res;
if (!nearNode) return res;
if (nearNode)
{
// is nearNode also a descendant of same block?
nsCOMPtr<nsIDOMNode> block, nearBlock;
if (IsBlockNode(selNode)) block = selNode;
@ -6537,7 +6607,7 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection
}
}
}
}
// we aren't in a textnode: are we adjacent to a break or an image?
res = mHTMLEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(nearNode));
@ -6558,7 +6628,24 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection
res = FindNearSelectableNode(selNode, selOffset, aAction, address_of(nearNode));
if (NS_FAILED(res)) return res;
if (!nearNode) return NS_OK; // couldn't find a near text node
if (!nearNode)
{
// make sure we aren't in an empty block - user will see no cursor. If this
// is happening, put a <br> in the block if allowed.
nsCOMPtr<nsIDOMNode> block;
if (IsBlockNode(selNode)) block = selNode;
else block = mHTMLEditor->GetBlockNodeParent(selNode);
PRBool bIsEmptyNode;
res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, PR_FALSE, PR_FALSE);
if (NS_FAILED(res)) return res;
// check if br can go into the destination node
if (mHTMLEditor->CanContainTag(selNode, NS_LITERAL_STRING("br")))
{
nsCOMPtr<nsIDOMNode> brNode;
res = CreateMozBR(selNode, selOffset, address_of(brNode));
}
return res;
}
// is the nearnode a text node?
textNode = do_QueryInterface(nearNode);
@ -6695,6 +6782,28 @@ nsHTMLEditRules::RemoveEmptyNodes()
nsCOMPtr<nsISupports> isupports;
PRUint32 nodeCount,j;
// some general notes on the algorithm used here: the goal is to examine all the
// nodes in mDocChangeRange, and remove the empty ones. We do this by using a
// content iterator to traverse all the nodes in the range, and placing the empty
// nodes into an array. After finishing the iteration, we delete the empty nodes
// in the array. (they cannot be deleted as we find them becasue that would
// invalidate the iterator.)
// Since checking to see if a node is empty can be costly for nodes with many
// descendants, there are some optimizations made. I rely on the fact that the
// iterator is post-order: it will visit children of a node before visiting the
// parent node. So if I find that a child node is not empty, I know that it's
// parent is not empty without even checking. So I put the parent on a "skipList"
// which is just a voidArray of nodes I can skip the empty check on. If I
// encounter a node on the skiplist, i skip the processing for that node and replace
// it's slot in the skiplist with that node's parent.
// An interseting idea is to go ahead and regard parent nodes that are NOT on the
// skiplist as being empty (without even doing the IsEmptyNode check) on the theory
// that if they weren't empty, we would have encountered a non-empty child earlier
// and thus put this parent node on the skiplist.
// Unfortunately I can't use that strategy here, because the range may include
// some children of a node while excluding others. Thus I could find all the
// _examined_ children empty, but still not have an empty parent.
// make an isupportsArray to hold a list of nodes
nsresult res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
if (NS_FAILED(res)) return res;
@ -6703,28 +6812,39 @@ nsHTMLEditRules::RemoveEmptyNodes()
iter = do_CreateInstance(kContentIteratorCID);
if (!iter) return NS_ERROR_NULL_POINTER;
// loop over iter and create list of empty containers
do
{
res = iter->Init(mDocChangeRange);
if (NS_FAILED(res)) return res;
// gather up a list of empty nodes
nsVoidArray skipList;
// check for empty nodes
while (NS_ENUMERATOR_FALSE == iter->IsDone())
{
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsIDOMNode> node, parent;
nsCOMPtr<nsIContent> content;
res = iter->CurrentNode(getter_AddRefs(content));
if (NS_FAILED(res)) return res;
node = do_QueryInterface(content);
if (!node) return NS_ERROR_FAILURE;
node->GetParentNode(getter_AddRefs(parent));
PRBool bIsEmptyNode;
res = mHTMLEditor->IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE);
if (NS_FAILED(res)) return res;
PRInt32 idx = skipList.IndexOf((void*)node);
if (idx>=0)
{
// this node is on our skip list. Skip processing for this node,
// and replace it's value in the skip list with the value of it's parent
skipList.ReplaceElementAt((void*)parent, idx);
}
else
{
PRBool bIsCandidate = PR_FALSE;
PRBool bIsEmptyNode = PR_FALSE;
// dont delete the body
if (!nsTextEditUtils::IsBody(node))
{
// only consider certain nodes to be empty for purposes of removal
if (!(
if (
nsTextEditUtils::NodeIsType(node, NS_LITERAL_STRING("a")) ||
nsTextEditUtils::NodeIsType(node, NS_LITERAL_STRING("b")) ||
nsTextEditUtils::NodeIsType(node, NS_LITERAL_STRING("i")) ||
@ -6739,20 +6859,12 @@ nsHTMLEditRules::RemoveEmptyNodes()
nsTextEditUtils::NodeIsType(node, NS_LITERAL_STRING("sup")) ||
nsTextEditUtils::NodeIsType(node, NS_LITERAL_STRING("font")) ||
nsHTMLEditUtils::IsList(node) ||
nsHTMLEditUtils::IsParagraph(node) ||
nsHTMLEditUtils::IsHeader(node) ||
nsHTMLEditUtils::IsListItem(node) ||
nsHTMLEditUtils::IsBlockquote(node)||
nsHTMLEditUtils::IsDiv(node) ||
nsHTMLEditUtils::IsPre(node) ||
nsHTMLEditUtils::IsAddress(node) ) )
nsHTMLEditUtils::IsDiv(node) )
{
bIsEmptyNode = PR_FALSE;
bIsCandidate = PR_TRUE;
}
if (bIsEmptyNode && !nsTextEditUtils::IsBody(node))
{
if (nsHTMLEditUtils::IsParagraph(node) ||
// these node types are candidates if selection is not in them
else if (nsHTMLEditUtils::IsParagraph(node) ||
nsHTMLEditUtils::IsHeader(node) ||
nsHTMLEditUtils::IsListItem(node) ||
nsHTMLEditUtils::IsBlockquote(node)||
@ -6767,18 +6879,29 @@ nsHTMLEditRules::RemoveEmptyNodes()
if (NS_FAILED(res)) return res;
if (!bIsSelInNode)
{
isupports = do_QueryInterface(node);
arrayOfNodes->AppendElement(isupports);
bIsCandidate = PR_TRUE;
}
}
else
}
if (bIsCandidate)
{
res = mHTMLEditor->IsEmptyNode(node, &bIsEmptyNode, PR_FALSE, PR_TRUE);
if (NS_FAILED(res)) return res;
if (bIsEmptyNode)
{
// if it's not such an element, delete it even if sel is inside
isupports = do_QueryInterface(node);
if (!isupports) return NS_ERROR_NULL_POINTER;
arrayOfNodes->AppendElement(isupports);
}
}
if (!bIsEmptyNode)
{
// put parent on skip list
skipList.AppendElement((void*)parent);
}
}
res = iter->Next();
if (NS_FAILED(res)) return res;
}
@ -6795,8 +6918,6 @@ nsHTMLEditRules::RemoveEmptyNodes()
if (NS_FAILED(res)) return res;
}
} while (nodeCount); // if we deleted any, loop again
// deleting some nodes may make some parents now empty
return res;
}

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

@ -150,7 +150,7 @@ protected:
nsresult DidMakeBasicBlock(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
nsresult AlignInnerBlocks(nsIDOMNode *aNode, const nsAReadableString *alignType);
nsresult AlignBlockContents(nsIDOMNode *aNode, const nsAReadableString *alignType);
nsresult GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRBool aList = PR_TRUE, PRBool aTble = PR_TRUE);
nsresult GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRInt32 *aIndex, PRBool aList = PR_TRUE, PRBool aTble = PR_TRUE);
nsCOMPtr<nsIDOMNode> IsInListItem(nsIDOMNode *aNode);
nsresult ReturnInHeader(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult ReturnInParagraph(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);

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

@ -4837,6 +4837,7 @@ nsHTMLEditor::GetLastEditableLeaf( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOut
}
#endif
///////////////////////////////////////////////////////////////////////////
// IsEmptyNode: figure out if aNode is an empty node.
// A block can have children and still be considered empty,
@ -4851,6 +4852,7 @@ nsHTMLEditor::IsEmptyNode( nsIDOMNode *aNode,
{
if (!aNode || !outIsEmptyNode) return NS_ERROR_NULL_POINTER;
*outIsEmptyNode = PR_TRUE;
nsresult res = NS_OK;
// effeciency hack - special case if it's a text node
if (nsEditor::IsTextNode(aNode))
@ -4887,28 +4889,14 @@ nsHTMLEditor::IsEmptyNode( nsIDOMNode *aNode,
PRBool isListItemOrCell =
nsHTMLEditUtils::IsListItem(aNode) || nsHTMLEditUtils::IsTableCell(aNode);
// iterate over node. if no children, or all children are either
// loop over children of node. if no children, or all children are either
// empty text nodes or non-editable, then node qualifies as empty
nsCOMPtr<nsIContentIterator> iter;
nsCOMPtr<nsIContent> nodeAsContent = do_QueryInterface(aNode);
if (!nodeAsContent) return NS_ERROR_FAILURE;
nsresult res = nsComponentManager::CreateInstance(kCContentIteratorCID,
nsnull,
NS_GET_IID(nsIContentIterator),
getter_AddRefs(iter));
if (NS_FAILED(res)) return res;
res = iter->Init(nodeAsContent);
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMNode> child;
aNode->GetFirstChild(getter_AddRefs(child));
while (NS_ENUMERATOR_FALSE == iter->IsDone())
while (child)
{
nsCOMPtr<nsIDOMNode> node;
nsCOMPtr<nsIContent> content;
res = iter->CurrentNode(getter_AddRefs(content));
if (NS_FAILED(res)) return res;
node = do_QueryInterface(content);
if (!node) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> node = child;
// is the node editable and non-empty? if so, return false
if (nsEditor::IsEditable(node))
{
@ -4936,16 +4924,16 @@ nsHTMLEditor::IsEmptyNode( nsIDOMNode *aNode,
if (isVisible)
{
*outIsEmptyNode = PR_FALSE;
break;
return NS_OK;
}
}
else if (length)
{
*outIsEmptyNode = PR_FALSE;
break;
return NS_OK;
}
}
else // an editable, non-text node. we aren't an empty block
else // an editable, non-text node. we need to check it's content.
{
// is it the node we are iterating over?
if (node.get() == aNode) break;
@ -4964,14 +4952,14 @@ nsHTMLEditor::IsEmptyNode( nsIDOMNode *aNode,
if (nsHTMLEditUtils::IsList(node) || nsHTMLEditUtils::IsTable(node))
{
*outIsEmptyNode = PR_FALSE;
break;
return NS_OK;
}
}
// is it a form widget?
else if (nsHTMLEditUtils::IsFormWidget(aNode))
{
*outIsEmptyNode = PR_FALSE;
break;
return NS_OK;
}
PRBool isEmptyNode;
@ -4981,13 +4969,12 @@ nsHTMLEditor::IsEmptyNode( nsIDOMNode *aNode,
{
// otherwise it ain't empty
*outIsEmptyNode = PR_FALSE;
break;
return NS_OK;
}
}
}
}
res = iter->Next();
if (NS_FAILED(res)) return res;
node->GetNextSibling(getter_AddRefs(child));
}
return NS_OK;

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

@ -679,7 +679,8 @@ nsWSRunObject::GetWSNodes()
// block boundary.
nsresult res = NS_OK;
nsCOMPtr<nsIDOMNode> blockParent, curStartNode, curEndNode;
nsCOMPtr<nsIDOMNode> blockParent;
DOMPoint start(mNode, mOffset), end(mNode, mOffset);
if (IsBlockNode(mNode)) blockParent = mNode;
else blockParent = mHTMLEditor->GetBlockNodeParent(mNode);
@ -718,48 +719,22 @@ nsWSRunObject::GetWSNodes()
mLastNBSPOffset = pos;
}
}
start.SetPoint(mNode,pos);
}
}
if (!mStartNode)
{
// we didnt find beginning of whitespace. remember this text node
// (and offset 0) as the extent to which we have looked back.
curStartNode = mNode;
}
}
PRInt32 curStartOffset=0;
nsCOMPtr<nsIDOMNode> priorNode;
while (!mStartNode)
{
// we haven't found the start of ws yet. Keep looking
// do we have a curStartNode? If not, get one.
if (!curStartNode)
{
res = GetPreviousWSNode(mNode, mOffset, blockParent, address_of(curStartNode));
res = GetPreviousWSNode(start, blockParent, address_of(priorNode));
NS_ENSURE_SUCCESS(res, res);
priorNode = curStartNode;
}
else
{
res = GetPreviousWSNode(curStartNode, blockParent, address_of(priorNode));
NS_ENSURE_SUCCESS(res, res);
}
if (priorNode)
{
if (IsBlockNode(priorNode))
{
// we encountered a new block. therefore no more ws.
if (mHTMLEditor->IsTextNode(curStartNode))
{
mStartNode = curStartNode;
mStartOffset = curStartOffset;
}
else
{
mHTMLEditor->GetNodeLocation(curStartNode, address_of(mStartNode), &mStartOffset);
mStartOffset++;
}
start.GetPoint(mStartNode, mStartOffset);
mStartReason = eOtherBlock;
}
else if (mHTMLEditor->IsTextNode(priorNode))
@ -796,71 +771,28 @@ nsWSRunObject::GetWSNodes()
mLastNBSPOffset = pos;
}
}
start.SetPoint(priorNode,pos);
}
if (!mStartNode)
{
// we didnt find beginning of whitespace. remember this text node
// (and offset 0) as the extent to which we have looked back.
curStartNode = priorNode;
curStartOffset = 0;
}
}
else if (nsTextEditUtils::IsBreak(priorNode))
{
// we encountered a break. therefore no more ws.
if (mHTMLEditor->IsTextNode(curStartNode))
{
mStartNode = curStartNode;
mStartOffset = curStartOffset;
}
else
{
mHTMLEditor->GetNodeLocation(curStartNode, address_of(mStartNode), &mStartOffset);
mStartOffset++;
}
mStartReason = eBreak;
}
else
{
// it's a special node, like <img>, that is not a block and not
// it's a break or a special node, like <img>, that is not a block and not
// a break but still serves as a terminator to ws runs.
if (mHTMLEditor->IsTextNode(curStartNode))
{
mStartNode = curStartNode;
mStartOffset = curStartOffset;
}
start.GetPoint(mStartNode, mStartOffset);
if (nsTextEditUtils::IsBreak(priorNode))
mStartReason = eBreak;
else
{
mHTMLEditor->GetNodeLocation(curStartNode, address_of(mStartNode), &mStartOffset);
mStartOffset++;
}
mStartReason = eSpecial;
}
}
else
{
// no prior node means we exhausted blockParent
if (!curStartNode)
{
// we never found anything to work with, so start
// is at beginning of block parent
mStartNode = blockParent;
mStartOffset = 0;
}
else if (mHTMLEditor->IsTextNode(curStartNode))
{
mStartNode = curStartNode;
mStartOffset = curStartOffset;
}
else
{
mHTMLEditor->GetNodeLocation(curStartNode, address_of(mStartNode), &mStartOffset);
mStartOffset++;
}
start.GetPoint(mStartNode, mStartOffset);
mStartReason = eThisBlock;
}
}
PRInt32 curEndOffset=0;
// then look ahead to find following ws nodes
if (mHTMLEditor->IsTextNode(mNode))
{
@ -896,48 +828,23 @@ nsWSRunObject::GetWSNodes()
mFirstNBSPOffset = pos;
}
}
end.SetPoint(mNode,pos);
}
}
if (!mEndNode)
{
// we didnt find end of whitespace. remember this text node
// (and offset len) as the extent to which we have looked ahead.
curEndNode = mNode;
curEndOffset = len;
}
}
nsCOMPtr<nsIDOMNode> nextNode;
while (!mEndNode)
{
// we haven't found the end of ws yet. Keep looking
// do we have a curEndNode? If not, get one.
if (!curEndNode)
{
res = GetNextWSNode(mNode, mOffset, blockParent, address_of(curEndNode));
res = GetNextWSNode(end, blockParent, address_of(nextNode));
NS_ENSURE_SUCCESS(res, res);
nextNode = curEndNode;
}
else
{
res = GetNextWSNode(curEndNode, blockParent, address_of(nextNode));
NS_ENSURE_SUCCESS(res, res);
}
if (nextNode)
{
if (IsBlockNode(nextNode))
{
// we encountered a new block. therefore no more ws.
if (mHTMLEditor->IsTextNode(curEndNode))
{
mEndNode = curEndNode;
mEndOffset = curEndOffset;
}
else
{
mHTMLEditor->GetNodeLocation(curEndNode, address_of(mEndNode), &mEndOffset);
}
end.GetPoint(mEndNode, mEndOffset);
mEndReason = eOtherBlock;
}
else if (mHTMLEditor->IsTextNode(nextNode))
@ -975,66 +882,25 @@ nsWSRunObject::GetWSNodes()
mFirstNBSPOffset = pos;
}
}
end.SetPoint(nextNode,pos+1);
}
if (!mEndNode)
{
// we didnt find end of whitespace. remember this text node
// (and offset len) as the extent to which we have looked ahead.
curEndNode = nextNode;
curEndOffset = len;
}
}
else if (nsTextEditUtils::IsBreak(nextNode))
{
// we encountered a break. therefore no more ws.
if (mHTMLEditor->IsTextNode(curEndNode))
{
mEndNode = curEndNode;
mEndOffset = curEndOffset;
}
else
{
mHTMLEditor->GetNodeLocation(curEndNode, address_of(mEndNode), &mEndOffset);
}
mEndReason = eBreak;
}
// we encountered a break or a special node, like <img>,
// that is not a block and not a break but still
// serves as a terminator to ws runs.
end.GetPoint(mEndNode, mEndOffset);
if (nsTextEditUtils::IsBreak(nextNode))
mStartReason = eBreak;
else
{
// it's a special node, like <img>, that is not a block and not
// a break but still serves as a terminator to ws runs.
if (mHTMLEditor->IsTextNode(curEndNode))
{
mEndNode = curEndNode;
mEndOffset = curEndOffset;
}
else
{
mHTMLEditor->GetNodeLocation(curEndNode, address_of(mEndNode), &mEndOffset);
}
mEndReason = eSpecial;
mStartReason = eSpecial;
}
}
else
{
// no next node means we exhausted blockParent
if (!curEndNode)
{
// we never found anything to work with, so end
// is at end of block parent
mEndNode = blockParent;
PRUint32 count;
mHTMLEditor->GetLengthOfDOMNode(blockParent, count);
mEndOffset = count;
}
else if (mHTMLEditor->IsTextNode(curEndNode))
{
mEndNode = curEndNode;
mEndOffset = curEndOffset;
}
else
{
mHTMLEditor->GetNodeLocation(curEndNode, address_of(mEndNode), &mEndOffset);
}
end.GetPoint(mEndNode, mEndOffset);
mEndReason = eThisBlock;
}
}
@ -1272,50 +1138,15 @@ nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode,
}
nsresult
nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode,
nsWSRunObject::GetPreviousWSNode(DOMPoint aPoint,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aNextNode)
nsCOMPtr<nsIDOMNode> *aPriorNode)
{
// can't really recycle various getnext/prior routines because we have special needs
// here. Need to step into inline containers but not block containers.
if (!aStartNode || !aBlockParent || !aNextNode) return NS_ERROR_NULL_POINTER;
*aNextNode = 0;
nsresult res = aStartNode->GetNextSibling(getter_AddRefs(*aNextNode));
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsIDOMNode> temp, curNode = aStartNode;
while (!*aNextNode)
{
// we have exhausted nodes in parent of aStartNode.
res = curNode->GetParentNode(getter_AddRefs(temp));
NS_ENSURE_SUCCESS(res, res);
if (!temp) return NS_ERROR_NULL_POINTER;
if (temp == aBlockParent)
{
// we have exhausted nodes in the block parent. The convention here is to return null.
*aNextNode = nsnull;
return NS_OK;
nsCOMPtr<nsIDOMNode> node;
PRInt32 offset;
aPoint.GetPoint(node, offset);
return GetPreviousWSNode(node,offset,aBlockParent,aPriorNode);
}
// we have a parent: look for next sibling
res = temp->GetNextSibling(getter_AddRefs(*aNextNode));
NS_ENSURE_SUCCESS(res, res);
curNode = temp;
}
// we have a next node. If it's a block, return it.
if (IsBlockNode(*aNextNode))
return NS_OK;
// else if it's a container, get deep leftmost child
else if (mHTMLEditor->IsContainer(*aNextNode))
{
temp = mHTMLEditor->GetLeftmostChild(*aNextNode);
if (temp)
*aNextNode = temp;
return NS_OK;
}
// else return the node itself
return NS_OK;
}
nsresult
nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode,
@ -1369,6 +1200,62 @@ nsWSRunObject::GetPreviousWSNode(nsIDOMNode *aStartNode,
return NS_OK;
}
nsresult
nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aNextNode)
{
// can't really recycle various getnext/prior routines because we have special needs
// here. Need to step into inline containers but not block containers.
if (!aStartNode || !aBlockParent || !aNextNode) return NS_ERROR_NULL_POINTER;
*aNextNode = 0;
nsresult res = aStartNode->GetNextSibling(getter_AddRefs(*aNextNode));
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsIDOMNode> temp, curNode = aStartNode;
while (!*aNextNode)
{
// we have exhausted nodes in parent of aStartNode.
res = curNode->GetParentNode(getter_AddRefs(temp));
NS_ENSURE_SUCCESS(res, res);
if (!temp) return NS_ERROR_NULL_POINTER;
if (temp == aBlockParent)
{
// we have exhausted nodes in the block parent. The convention here is to return null.
*aNextNode = nsnull;
return NS_OK;
}
// we have a parent: look for next sibling
res = temp->GetNextSibling(getter_AddRefs(*aNextNode));
NS_ENSURE_SUCCESS(res, res);
curNode = temp;
}
// we have a next node. If it's a block, return it.
if (IsBlockNode(*aNextNode))
return NS_OK;
// else if it's a container, get deep leftmost child
else if (mHTMLEditor->IsContainer(*aNextNode))
{
temp = mHTMLEditor->GetLeftmostChild(*aNextNode);
if (temp)
*aNextNode = temp;
return NS_OK;
}
// else return the node itself
return NS_OK;
}
nsresult
nsWSRunObject::GetNextWSNode(DOMPoint aPoint,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aNextNode)
{
nsCOMPtr<nsIDOMNode> node;
PRInt32 offset;
aPoint.GetPoint(node, offset);
return GetNextWSNode(node,offset,aBlockParent,aNextNode);
}
nsresult
nsWSRunObject::GetNextWSNode(nsIDOMNode *aStartNode,
PRInt16 aOffset,

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

@ -240,6 +240,27 @@ 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.
nsresult GetWSNodes();
@ -251,17 +272,23 @@ class nsWSRunObject
nsresult GetPreviousWSNode(nsIDOMNode *aStartNode,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aPriorNode);
nsresult GetNextWSNode(nsIDOMNode *aStartNode,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aNextNode);
nsresult GetPreviousWSNode(nsIDOMNode *aStartNode,
PRInt16 aOffset,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aPriorNode);
nsresult GetPreviousWSNode(DOMPoint aPoint,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aPriorNode);
nsresult GetNextWSNode(nsIDOMNode *aStartNode,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aNextNode);
nsresult GetNextWSNode(nsIDOMNode *aStartNode,
PRInt16 aOffset,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aNextNode);
nsresult GetNextWSNode(DOMPoint aPoint,
nsIDOMNode *aBlockParent,
nsCOMPtr<nsIDOMNode> *aNextNode);
nsresult PrepareToDeleteRangePriv(nsWSRunObject* aEndObject);
nsresult PrepareToSplitAcrossBlocksPriv();
nsresult DeleteChars(nsIDOMNode *aStartNode, PRInt32 aStartOffset,