From bc37219a9a8b3040c9efa3770cf36fbc3097ef3d Mon Sep 17 00:00:00 2001 From: "jfrancis%netscape.com" Date: Mon, 18 Jun 2001 21:15:43 +0000 Subject: [PATCH] fixes for: 61232 - Caret comes back to previous line after creating paragraph 54520 - Extra break with list items 54539 - Disappearing numbered list with outdent 60867 - Composer adds extra break when round-tripping empty document through View Source 62347 - Selecting Body Text doesn't work inside other blocks that have embedding breaks --- editor/base/nsHTMLEditRules.cpp | 576 +++++++++++++++------- editor/base/nsHTMLEditRules.h | 5 + editor/base/nsHTMLEditor.cpp | 99 +++- editor/base/nsHTMLEditor.h | 1 + editor/libeditor/html/nsHTMLEditRules.cpp | 576 +++++++++++++++------- editor/libeditor/html/nsHTMLEditRules.h | 5 + editor/libeditor/html/nsHTMLEditor.cpp | 99 +++- editor/libeditor/html/nsHTMLEditor.h | 1 + 8 files changed, 998 insertions(+), 364 deletions(-) diff --git a/editor/base/nsHTMLEditRules.cpp b/editor/base/nsHTMLEditRules.cpp index ed7ee4e61565..9b43b81830af 100644 --- a/editor/base/nsHTMLEditRules.cpp +++ b/editor/base/nsHTMLEditRules.cpp @@ -2108,41 +2108,10 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection, } // if there is only one node in the array, and it is a list, div, or blockquote, - // then look inside of it until we find what we want to make a list out of. - if (listCount == 1) - { - nsCOMPtr isupports = dont_AddRef(arrayOfNodes->ElementAt(0)); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - - while (nsHTMLEditUtils::IsDiv(curNode) - || nsHTMLEditUtils::IsList(curNode) - || nsHTMLEditUtils::IsBlockquote(curNode)) - { - // dive as long as there is only one child, and it is a list, div, blockquote - PRUint32 numChildren; - res = mHTMLEditor->CountEditableChildren(curNode, numChildren); - if (NS_FAILED(res)) return res; - - if (numChildren == 1) - { - // keep diving - nsCOMPtr tmpNode = nsEditor::GetChildAt(curNode, 0); - if (nsHTMLEditUtils::IsDiv(tmpNode) - || nsHTMLEditUtils::IsList(tmpNode) - || nsHTMLEditUtils::IsBlockquote(tmpNode)) - { - // check editablility XXX floppy moose - curNode = tmpNode; - } - else break; - } - else break; - } - // we've found innermost list/blockquote/div: - // replace the one node in the array with this node - isupports = do_QueryInterface(curNode); - arrayOfNodes->ReplaceElementAt(isupports, 0); - } + // then look inside of it until we find inner list or content. + + res = LookInsideDivBQandList(arrayOfNodes); + if (NS_FAILED(res)) return res; // Next we detect all the transitions in the array, where a transition // means that adjacent nodes in the array don't have the same parent. @@ -2209,7 +2178,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection, if (NS_FAILED(res)) return res; res = ConvertListType(curNode, address_of(newBlock), *aListType, itemType); if (NS_FAILED(res)) return res; - res = mHTMLEditor->RemoveContainer(newBlock); + res = mHTMLEditor->RemoveBlockContainer(newBlock); if (NS_FAILED(res)) return res; } else @@ -2358,7 +2327,7 @@ nsHTMLEditRules::WillRemoveList(nsISelection *aSelection, // use these ranges to contruct a list of nodes to act on. nsCOMPtr arrayOfNodes; - res = GetNodesForOperation(arrayOfRanges, address_of(arrayOfNodes), kMakeList); + res = GetListActionNodes(address_of(arrayOfNodes), PR_FALSE); if (NS_FAILED(res)) return res; // Remove all non-editable nodes. Leave them be. @@ -2479,6 +2448,9 @@ nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection, nsString tString(*aBlockType); if (tString.EqualsWithConversion("blockquote")) res = MakeBlockquote(arrayOfNodes); + else if (tString.EqualsWithConversion("normal") || + tString.IsEmpty() ) + res = RemoveBlockStyle(arrayOfNodes); else res = ApplyBlockStyle(arrayOfNodes, aBlockType); return res; @@ -2660,20 +2632,15 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool * if (NS_FAILED(res)) return res; // use these ranges to contruct a list of nodes to act on. + nsCOMPtr arrayOfNodes; res = GetNodesForOperation(arrayOfRanges, address_of(arrayOfNodes), kOutdent); if (NS_FAILED(res)) return res; - // Next we detect all the transitions in the array, where a transition - // means that adjacent nodes in the array don't have the same parent. - - nsVoidArray transitionList; - res = MakeTransitionList(arrayOfNodes, &transitionList); - if (NS_FAILED(res)) return res; - // Ok, now go through all the nodes and remove a level of blockquoting, // or whatever is appropriate. Wohoo! + nsCOMPtr curBlockQuote, firstBQChild, lastBQChild; PRUint32 listCount; PRInt32 i; arrayOfNodes->Count(&listCount); @@ -2687,78 +2654,175 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool * res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset); if (NS_FAILED(res)) return res; - if (nsHTMLEditUtils::IsList(curParent)) // move node out of list + // is it a blockquote? + if (nsHTMLEditUtils::IsBlockquote(curNode)) { - if (nsHTMLEditUtils::IsList(curNode)) // just unwrap this sublist + // if it is a blockquote, remove it. + // So we need to finish up dealng with any curBlockQuote first. + if (curBlockQuote) { - res = mHTMLEditor->RemoveContainer(curNode); + res = RemovePartOfBlock(curBlockQuote, firstBQChild, lastBQChild); if (NS_FAILED(res)) return res; + curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0; } - else // we are moving a list item, but not whole list - { - PRBool bOutOfList; - res = PopListItem(curNode, &bOutOfList); - if (NS_FAILED(res)) return res; - } - } - else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, but parent is non-list: move list items out - { - nsCOMPtr child; - curNode->GetLastChild(getter_AddRefs(child)); - while (child) - { - if (nsHTMLEditUtils::IsListItem(child)) - { - PRBool bOutOfList; - res = PopListItem(child, &bOutOfList); - if (NS_FAILED(res)) return res; - } - else if (nsHTMLEditUtils::IsList(child)) - { - // We have an embedded list, so move it out from under the - // parent list. Be sure to put it after the parent list - // because this loop iterates backwards through the parent's - // list of children. - - res = mHTMLEditor->MoveNode(child, curParent, offset + 1); - if (NS_FAILED(res)) return res; - } - else - { - // delete any non- list items for now - res = mHTMLEditor->DeleteNode(child); - if (NS_FAILED(res)) return res; - } - curNode->GetLastChild(getter_AddRefs(child)); - } - // delete the now-empty list - res = mHTMLEditor->DeleteNode(curNode); + res = mHTMLEditor->RemoveBlockContainer(curNode); if (NS_FAILED(res)) return res; + continue; } - else if (transitionList[i]) // not list related - look for enclosing blockquotes and remove + // is it a list item? + if (nsHTMLEditUtils::IsListItem(curNode)) { - // look for a blockquote somewhere above us and remove it. - // this is a hack until i think about outdent for real. - nsCOMPtr n = curNode; - nsCOMPtr tmp; - while (!nsTextEditUtils::IsBody(n)) + // if it is a list item, that means we are not outdenting whole list. + // So we need to finish up dealng with any curBlockQuote, and then + // pop this list item. + if (curBlockQuote) { - if (nsHTMLEditUtils::IsBlockquote(n)) - { - res = AddTerminatingBR(n); - if (NS_FAILED(res)) return res; - mHTMLEditor->RemoveContainer(n); - break; - } - n->GetParentNode(getter_AddRefs(tmp)); - n = tmp; + res = RemovePartOfBlock(curBlockQuote, firstBQChild, lastBQChild); + if (NS_FAILED(res)) return res; + curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0; + } + PRBool bOutOfList; + res = PopListItem(curNode, &bOutOfList); + if (NS_FAILED(res)) return res; + continue; + } + // do we have a blockquote that we are already committed to removing? + if (curBlockQuote) + { + // if so, is this node a descendant? + if (nsHTMLEditUtils::IsDescendantOf(curNode, curBlockQuote)) + { + lastBQChild = curNode; + continue; // then we dont need to do anything different for this node + } + else + { + // otherwise, we have progressed beyond end of curBlockQuote, + // so lets handle it now. We need to remove the portion of + // curBlockQuote that contains [firstBQChild - lastBQChild]. + res = RemovePartOfBlock(curBlockQuote, firstBQChild, lastBQChild); + if (NS_FAILED(res)) return res; + curBlockQuote = 0; firstBQChild = 0; lastBQChild = 0; + // fall out and handle curNode } } + + // are we inside a blockquote? + nsCOMPtr n = curNode; + nsCOMPtr tmp; + while (!nsTextEditUtils::IsBody(n)) + { + n->GetParentNode(getter_AddRefs(tmp)); + n = tmp; + if (nsHTMLEditUtils::IsBlockquote(n)) + { + // if so, remember it, and remember first node we are taking out of it. + curBlockQuote = n; + firstBQChild = curNode; + lastBQChild = curNode; + break; + } + } + + if (!curBlockQuote) + { + // could not find an enclosing blockquote for this node. handle list cases. + if (nsHTMLEditUtils::IsList(curParent)) // move node out of list + { + if (nsHTMLEditUtils::IsList(curNode)) // just unwrap this sublist + { + res = mHTMLEditor->RemoveBlockContainer(curNode); + if (NS_FAILED(res)) return res; + } + // handled list item case above + } + else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, but parent is non-list: move list items out + { + nsCOMPtr child; + curNode->GetLastChild(getter_AddRefs(child)); + while (child) + { + if (nsHTMLEditUtils::IsListItem(child)) + { + PRBool bOutOfList; + res = PopListItem(child, &bOutOfList); + if (NS_FAILED(res)) return res; + } + else if (nsHTMLEditUtils::IsList(child)) + { + // We have an embedded list, so move it out from under the + // parent list. Be sure to put it after the parent list + // because this loop iterates backwards through the parent's + // list of children. + + res = mHTMLEditor->MoveNode(child, curParent, offset + 1); + if (NS_FAILED(res)) return res; + } + else + { + // delete any non- list items for now + res = mHTMLEditor->DeleteNode(child); + if (NS_FAILED(res)) return res; + } + curNode->GetLastChild(getter_AddRefs(child)); + } + // delete the now-empty list + res = mHTMLEditor->RemoveBlockContainer(curNode); + if (NS_FAILED(res)) return res; + } + } + } + if (curBlockQuote) + { + // we have a blockquote we haven't finished handling + res = RemovePartOfBlock(curBlockQuote, firstBQChild, lastBQChild); + if (NS_FAILED(res)) return res; } return res; } +/////////////////////////////////////////////////////////////////////////// +// ConvertListType: convert list type and list item type. +// +// +nsresult +nsHTMLEditRules::RemovePartOfBlock(nsIDOMNode *aBlock, + nsIDOMNode *aStartChild, + nsIDOMNode *aEndChild) +{ + if (!aBlock || !aStartChild || !aEndChild) + return NS_ERROR_NULL_POINTER; + + nsCOMPtr startParent, endParent, leftNode, rightNode; + PRInt32 startOffset, endOffset, offset; + nsresult res; + + // get split point location + res = nsEditor::GetNodeLocation(aStartChild, address_of(startParent), &startOffset); + if (NS_FAILED(res)) return res; + + // do the splits! + res = mHTMLEditor->SplitNodeDeep(aBlock, startParent, startOffset, &offset, + PR_TRUE, address_of(leftNode), address_of(rightNode)); + if (NS_FAILED(res)) return res; + if (rightNode) aBlock = rightNode; + + // get split point location + res = nsEditor::GetNodeLocation(aEndChild, address_of(endParent), &endOffset); + if (NS_FAILED(res)) return res; + endOffset++; // want to be after lastBQChild + + // do the splits! + res = mHTMLEditor->SplitNodeDeep(aBlock, endParent, endOffset, &offset, + PR_TRUE, address_of(leftNode), address_of(rightNode)); + if (NS_FAILED(res)) return res; + if (leftNode) aBlock = leftNode; + + // get rid of part of blockquote we are outdenting + res = mHTMLEditor->RemoveBlockContainer(aBlock); + return res; +} /////////////////////////////////////////////////////////////////////////// // ConvertListType: convert list type and list item type. @@ -3891,6 +3955,23 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges, } } } + // outdent should look inside of divs. + if (inOperationType == kOutdent) + { + PRUint32 listCount; + (*outArrayOfNodes)->Count(&listCount); + for (i=(PRInt32)listCount-1; i>=0; i--) + { + isupports = dont_AddRef((*outArrayOfNodes)->ElementAt(i)); + nsCOMPtr node( do_QueryInterface(isupports) ); + if (nsHTMLEditUtils::IsDiv(node)) + { + (*outArrayOfNodes)->RemoveElementAt(i); + res = GetInnerContent(node, *outArrayOfNodes, PR_FALSE, PR_FALSE); + if (NS_FAILED(res)) return res; + } + } + } // post process the list to break up inline containers that contain br's. @@ -4057,15 +4138,77 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes, (*outArrayOfNodes)->RemoveElementAt(i); } - // scan for table elements. If we find table elements other than table, + // scan for table elements and divs. If we find table elements other than table, // replace it with a list of any editable non-table content. - if (nsHTMLEditUtils::IsTableElement(testNode) && !nsHTMLEditUtils::IsTable(testNode)) + if ( (nsHTMLEditUtils::IsTableElement(testNode) && !nsHTMLEditUtils::IsTable(testNode)) + || nsHTMLEditUtils::IsDiv(testNode) ) { (*outArrayOfNodes)->RemoveElementAt(i); res = GetInnerContent(testNode, *outArrayOfNodes, PR_FALSE); if (NS_FAILED(res)) return res; } } + + // if there is only one node in the array, and it is a list, div, or blockquote, + // then look inside of it until we find inner list or content. + res = LookInsideDivBQandList(*outArrayOfNodes); + return res; +} + + +/////////////////////////////////////////////////////////////////////////// +// LookInsideDivBQandList: +// +nsresult +nsHTMLEditRules::LookInsideDivBQandList(nsISupportsArray *aNodeArray) +{ + // if there is only one node in the array, and it is a list, div, or blockquote, + // then look inside of it until we find inner list or content. + nsresult res = NS_OK; + PRUint32 listCount; + aNodeArray->Count(&listCount); + if (listCount == 1) + { + nsCOMPtr isupports = dont_AddRef(aNodeArray->ElementAt(0)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + while (nsHTMLEditUtils::IsDiv(curNode) + || nsHTMLEditUtils::IsList(curNode) + || nsHTMLEditUtils::IsBlockquote(curNode)) + { + // dive as long as there is only one child, and it is a list, div, blockquote + PRUint32 numChildren; + res = mHTMLEditor->CountEditableChildren(curNode, numChildren); + if (NS_FAILED(res)) return res; + + if (numChildren == 1) + { + // keep diving + nsCOMPtr tmpNode = nsEditor::GetChildAt(curNode, 0); + if (nsHTMLEditUtils::IsDiv(tmpNode) + || nsHTMLEditUtils::IsList(tmpNode) + || nsHTMLEditUtils::IsBlockquote(tmpNode)) + { + // check editablility XXX floppy moose + curNode = tmpNode; + } + else break; + } + else break; + } + // we've found innermost list/blockquote/div: + // replace the one node in the array with these nodes + aNodeArray->RemoveElementAt(0); + if ((nsHTMLEditUtils::IsDiv(curNode) || nsHTMLEditUtils::IsBlockquote(curNode))) + { + res = GetInnerContent(curNode, aNodeArray, PR_FALSE, PR_FALSE); + } + else + { + nsCOMPtr isuports (do_QueryInterface(curNode)); + res = aNodeArray->AppendElement(isuports); + } + } return res; } @@ -4769,6 +4912,144 @@ nsHTMLEditRules::MakeBlockquote(nsISupportsArray *arrayOfNodes) +/////////////////////////////////////////////////////////////////////////// +// RemoveBlockStyle: make the list nodes have no special block type. +// +nsresult +nsHTMLEditRules::RemoveBlockStyle(nsISupportsArray *arrayOfNodes) +{ + // intent of this routine is to be used for converting to/from + // headers, paragraphs, pre, and address. Those blocks + // that pretty much just contain inline things... + + if (!arrayOfNodes) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + + nsCOMPtr curNode, curParent, curBlock, firstNode, lastNode; + PRInt32 offset; + PRUint32 listCount; + + arrayOfNodes->Count(&listCount); + + PRUint32 i; + for (i=0; i isupports = dont_AddRef(arrayOfNodes->ElementAt(i)); + curNode = do_QueryInterface(isupports); + res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset); + if (NS_FAILED(res)) return res; + nsAutoString curNodeTag; + nsEditor::GetTagString(curNode, curNodeTag); + curNodeTag.ToLowerCase(); + + // if curNode is a address, p, header, address, or pre, remove it + if ((curNodeTag.EqualsWithConversion("pre")) || + (curNodeTag.EqualsWithConversion("p")) || + (curNodeTag.EqualsWithConversion("h1")) || + (curNodeTag.EqualsWithConversion("h2")) || + (curNodeTag.EqualsWithConversion("h3")) || + (curNodeTag.EqualsWithConversion("h4")) || + (curNodeTag.EqualsWithConversion("h5")) || + (curNodeTag.EqualsWithConversion("h6")) || + (curNodeTag.EqualsWithConversion("address"))) + { + // process any partial progress saved + if (curBlock) + { + res = RemovePartOfBlock(curBlock, firstNode, lastNode); + if (NS_FAILED(res)) return res; + curBlock = 0; firstNode = 0; lastNode = 0; + } + // remove curent block + res = mHTMLEditor->RemoveBlockContainer(curNode); + if (NS_FAILED(res)) return res; + } + else if ((curNodeTag.EqualsWithConversion("table")) || + (curNodeTag.EqualsWithConversion("tbody")) || + (curNodeTag.EqualsWithConversion("tr")) || + (curNodeTag.EqualsWithConversion("td")) || + (curNodeTag.EqualsWithConversion("ol")) || + (curNodeTag.EqualsWithConversion("ul")) || + (curNodeTag.EqualsWithConversion("dl")) || + (curNodeTag.EqualsWithConversion("li")) || + (curNodeTag.EqualsWithConversion("blockquote")) || + (curNodeTag.EqualsWithConversion("div"))) + { + // process any partial progress saved + if (curBlock) + { + res = RemovePartOfBlock(curBlock, firstNode, lastNode); + if (NS_FAILED(res)) return res; + curBlock = 0; firstNode = 0; lastNode = 0; + } + // recursion time + nsCOMPtr childArray; + res = GetChildNodesForOperation(curNode, address_of(childArray)); + if (NS_FAILED(res)) return res; + res = RemoveBlockStyle(childArray); + if (NS_FAILED(res)) return res; + } + else if (IsInlineNode(curNode)) + { + if (curBlock) + { + // if so, is this node a descendant? + if (nsHTMLEditUtils::IsDescendantOf(curNode, curBlock)) + { + lastNode = curNode; + continue; // then we dont need to do anything different for this node + } + else + { + // otherwise, we have progressed beyond end of curBlock, + // so lets handle it now. We need to remove the portion of + // curBlock that contains [firstNode - lastNode]. + res = RemovePartOfBlock(curBlock, firstNode, lastNode); + if (NS_FAILED(res)) return res; + curBlock = 0; firstNode = 0; lastNode = 0; + // fall out and handle curNode + } + } + curBlock = mHTMLEditor->GetBlockNodeParent(curNode); + if ((curNodeTag.EqualsWithConversion("pre")) || + (curNodeTag.EqualsWithConversion("p")) || + (curNodeTag.EqualsWithConversion("h1")) || + (curNodeTag.EqualsWithConversion("h2")) || + (curNodeTag.EqualsWithConversion("h3")) || + (curNodeTag.EqualsWithConversion("h4")) || + (curNodeTag.EqualsWithConversion("h5")) || + (curNodeTag.EqualsWithConversion("h6")) || + (curNodeTag.EqualsWithConversion("address"))) + { + firstNode = curNode; + lastNode = curNode; + } + else + curBlock = 0; // not a block kind that we care about. + } + else + { // some node that is already sans block style. skip over it and + // process any partial progress saved + if (curBlock) + { + res = RemovePartOfBlock(curBlock, firstNode, lastNode); + if (NS_FAILED(res)) return res; + curBlock = 0; firstNode = 0; lastNode = 0; + } + } + } + // process any partial progress saved + if (curBlock) + { + res = RemovePartOfBlock(curBlock, firstNode, lastNode); + if (NS_FAILED(res)) return res; + curBlock = 0; firstNode = 0; lastNode = 0; + } + return res; +} + + /////////////////////////////////////////////////////////////////////////// // ApplyBlockStyle: do whatever it takes to make the list of nodes into // one or more blocks of type blockTag. @@ -4786,15 +5067,8 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab nsCOMPtr curNode, curParent, curBlock, newBlock; PRInt32 offset; PRUint32 listCount; - PRBool bNoParent = PR_FALSE; - - // we special case an empty tag name to mean "remove block parents". - // This is used for the "normal" paragraph style in mail-compose nsString tString(*aBlockTag);////MJUDGE SCC NEED HELP - if (tString.IsEmpty() || - !Compare(tString,NS_LITERAL_STRING("normal"),nsCaseInsensitiveStringComparator())) - bNoParent = PR_TRUE; - + arrayOfNodes->Count(&listCount); PRUint32 i; @@ -4810,13 +5084,13 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab curNodeTag.ToLowerCase(); // is it already the right kind of block? - if (!bNoParent && curNodeTag == *aBlockTag) + if (curNodeTag == *aBlockTag) { curBlock = 0; // forget any previous block used for previous inline nodes continue; // do nothing to this block } - // if curNode is a mozdiv, p, header, address, or pre, replace + // if curNode is a address, p, header, address, or pre, replace // it with a new block of correct type. // xxx floppy moose: pre cant hold everything the others can if (nsHTMLEditUtils::IsMozDiv(curNode) || @@ -4831,17 +5105,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab (curNodeTag.EqualsWithConversion("address"))) { curBlock = 0; // forget any previous block used for previous inline nodes - if (bNoParent) - { - // make sure we have a normal br at end of block - res = AddTerminatingBR(curNode); - if (NS_FAILED(res)) return res; - res = mHTMLEditor->RemoveContainer(curNode); - } - else - { - res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag); - } + res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag); if (NS_FAILED(res)) return res; } else if ((curNodeTag.EqualsWithConversion("table")) || @@ -4850,9 +5114,10 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab (curNodeTag.EqualsWithConversion("td")) || (curNodeTag.EqualsWithConversion("ol")) || (curNodeTag.EqualsWithConversion("ul")) || + (curNodeTag.EqualsWithConversion("dl")) || (curNodeTag.EqualsWithConversion("li")) || (curNodeTag.EqualsWithConversion("blockquote")) || - (curNodeTag.EqualsWithConversion("div"))) // div's other than mozdivs + (curNodeTag.EqualsWithConversion("div"))) { curBlock = 0; // forget any previous block used for previous inline nodes // recursion time @@ -4867,11 +5132,8 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab else if (curNodeTag.EqualsWithConversion("br")) { curBlock = 0; // forget any previous block used for previous inline nodes - if (!bNoParent) - { - res = mHTMLEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - } + res = mHTMLEditor->DeleteNode(curNode); + if (NS_FAILED(res)) return res; } @@ -4882,7 +5144,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab // arrayOfNodes is contructed, but some additional logic should // be added here if that should change - else if (IsInlineNode(curNode) && !bNoParent) + else if (IsInlineNode(curNode)) { // if curNode is a non editable, drop it if we are going to
       if (!Compare(tString,NS_LITERAL_STRING("pre"),nsCaseInsensitiveStringComparator()) 
@@ -4958,43 +5220,6 @@ nsHTMLEditRules::SplitAsNeeded(const nsAReadableString *aTag,
   return res;
 }      
 
-///////////////////////////////////////////////////////////////////////////
-// AddTerminatingBR:  place an ordinary br node at the end of aBlock,   
-//                  if it doens't already have one.  If it has a moz-BR,
-//                  simply convertit to a normal br.  
-nsresult 
-nsHTMLEditRules::AddTerminatingBR(nsIDOMNode *aBlock)
-{
-  if (!aBlock) return NS_ERROR_NULL_POINTER;
-  nsCOMPtr last;
-  nsresult res = mHTMLEditor->GetLastEditableLeaf(aBlock, address_of(last));
-  if (last && nsTextEditUtils::IsBreak(last))
-  {
-    if (nsTextEditUtils::IsMozBR(last))
-    {
-      // need to convert a br
-      nsCOMPtr elem = do_QueryInterface(last);
-      res = mHTMLEditor->RemoveAttribute(elem, NS_ConvertASCIItoUCS2("type")); 
-      if (NS_FAILED(res)) return res;
-    }
-    else // we have what we want, we're done
-    {
-      return res;
-    }
-  }
-  else // need to add a br
-  {
-    PRUint32 len;
-    nsCOMPtr brNode;
-    res = mHTMLEditor->GetLengthOfDOMNode(aBlock, len);
-    if (NS_FAILED(res)) return res;
-    res = mHTMLEditor->CreateBR(aBlock, len, address_of(brNode));
-    if (NS_FAILED(res)) return res;
-  }
-  return res;
-}
-
-
 ///////////////////////////////////////////////////////////////////////////
 // JoinNodesSmart:  join two nodes, doing whatever makes sense for their  
 //                  children (which often means joining them, too).
@@ -5744,9 +5969,7 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
   if (!nsHTMLEditUtils::IsList(curParPar)
       && nsHTMLEditUtils::IsListItem(curNode)) 
   {
-    res = AddTerminatingBR(curNode);
-    if (NS_FAILED(res)) return res;
-    res = mHTMLEditor->RemoveContainer(curNode);
+    res = mHTMLEditor->RemoveBlockContainer(curNode);
     if (NS_FAILED(res)) return res;
     *aOutOfList = PR_TRUE;
   }
@@ -5788,7 +6011,7 @@ nsHTMLEditRules::RemoveListStructure(nsIDOMNode *aList)
     aList->GetFirstChild(getter_AddRefs(child));
   }
   // delete the now-empty list
-  res = mHTMLEditor->DeleteNode(aList);
+  res = mHTMLEditor->RemoveBlockContainer(aList);
   if (NS_FAILED(res)) return res;
 
   return res;
@@ -6128,12 +6351,3 @@ nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection)
 {
   return NS_OK;
 }
-
-
-
-
-
-
-
-
-
diff --git a/editor/base/nsHTMLEditRules.h b/editor/base/nsHTMLEditRules.h
index 8a499f5c4f37..4a770413e472 100644
--- a/editor/base/nsHTMLEditRules.h
+++ b/editor/base/nsHTMLEditRules.h
@@ -122,6 +122,9 @@ protected:
   nsresult ReturnInParagraph(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);
   nsresult ReturnInListItem(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
   nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection);
+  nsresult RemovePartOfBlock(nsIDOMNode *curBlockQuote, 
+                             nsIDOMNode *firstBQChild, 
+                             nsIDOMNode *lastBQChild);
   nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr *outList, const nsAReadableString& aListType, const nsAReadableString& aItemType);
   nsresult CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc);
   nsresult IsEmptyBlock(nsIDOMNode *aNode, 
@@ -155,12 +158,14 @@ protected:
   nsresult GetListActionNodes(nsCOMPtr *outArrayOfNodes, PRBool aEntireList, PRBool aDontTouchContent=PR_FALSE);
   nsresult GetDefinitionListItemTypes(nsIDOMNode *aNode, PRBool &aDT, PRBool &aDD);
   nsresult GetParagraphFormatNodes(nsCOMPtr *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
+  nsresult LookInsideDivBQandList(nsISupportsArray *aNodeArray);
   nsresult BustUpInlinesAtRangeEndpoints(nsRangeStore &inRange);
   nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode, 
                                    nsCOMPtr *outArrayOfNodes);
   nsCOMPtr GetHighestInlineParent(nsIDOMNode* aNode);
   nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, 
                                    nsVoidArray *inTransitionArray);
+  nsresult RemoveBlockStyle(nsISupportsArray *arrayOfNodes);
   nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadableString *aBlockTag);
   nsresult MakeBlockquote(nsISupportsArray *arrayOfNodes);
   nsresult SplitAsNeeded(const nsAReadableString *aTag, nsCOMPtr *inOutParent, PRInt32 *inOutOffset);
diff --git a/editor/base/nsHTMLEditor.cpp b/editor/base/nsHTMLEditor.cpp
index ec9ac4dfaf96..419f8e4637ff 100644
--- a/editor/base/nsHTMLEditor.cpp
+++ b/editor/base/nsHTMLEditor.cpp
@@ -4095,6 +4095,103 @@ nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection *aSelection)
 #pragma mark -
 #endif
 
+///////////////////////////////////////////////////////////////////////////
+// RemoveBlockContainer: remove inNode, reparenting it's children into their
+//                  the parent of inNode.  In addition, INSERT ANY BR's NEEDED
+//                  TO PRESERVE IDENTITY OF REMOVED BLOCK.
+//
+nsresult
+nsHTMLEditor::RemoveBlockContainer(nsIDOMNode *inNode)
+{
+  if (!inNode)
+    return NS_ERROR_NULL_POINTER;
+  nsresult res;
+  nsCOMPtr sibling, child, unused;
+  
+  // Two possibilities: the container cold be empty of editable content.
+  // If that is the case, we need to compare what is before and after inNode
+  // to determine if we need a br.
+  // Or it could not be empty, in which case we have to compare previous
+  // sibling and first child to determine if we need a leading br,
+  // and compare following sibling and last child to determine if we need a
+  // trailing br.
+  
+  res = GetFirstEditableChild(inNode, address_of(child));
+  if (NS_FAILED(res)) return res;
+  
+  if (child)  // the case of inNode not being empty
+  {
+    // we need a br at start unless:
+    // 1) previous sibling of inNode is a block, OR
+    // 2) previous sibling of inNode is a br, OR
+    // 3) first child of inNode is a block OR
+    // 4) either is null
+    
+    res = GetPriorHTMLSibling(inNode, address_of(sibling));
+    if (NS_FAILED(res)) return res;
+    if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
+    {
+      res = GetFirstEditableChild(inNode, address_of(child));
+      if (NS_FAILED(res)) return res;
+      if (child && !IsBlockNode(child))
+      {
+        // insert br node
+        res = CreateBR(inNode, 0, address_of(unused));
+        if (NS_FAILED(res)) return res;
+      }
+    }
+    
+    // we need a br at end unless:
+    // 1) following sibling of inNode is a block, OR
+    // 2) last child of inNode is a block, OR
+    // 3) last child of inNode is a block OR
+    // 4) either is null
+
+    res = GetNextHTMLSibling(inNode, address_of(sibling));
+    if (NS_FAILED(res)) return res;
+    if (sibling && !IsBlockNode(sibling))
+    {
+      res = GetLastEditableChild(inNode, address_of(child));
+      if (NS_FAILED(res)) return res;
+      if (child && !IsBlockNode(child) && !nsTextEditUtils::IsBreak(child))
+      {
+        // insert br node
+        PRUint32 len;
+        res = GetLengthOfDOMNode(inNode, len);
+        if (NS_FAILED(res)) return res;
+        res = CreateBR(inNode, (PRInt32)len, address_of(unused));
+        if (NS_FAILED(res)) return res;
+      }
+    }
+  }
+  else  // the case of inNode being empty
+  {
+    // we need a br at start unless:
+    // 1) previous sibling of inNode is a block, OR
+    // 2) previous sibling of inNode is a br, OR
+    // 3) following sibling of inNode is a block, OR
+    // 4) following sibling of inNode is a br OR
+    // 5) either is null
+    res = GetPriorHTMLSibling(inNode, address_of(sibling));
+    if (NS_FAILED(res)) return res;
+    if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
+    {
+      res = GetNextHTMLSibling(inNode, address_of(sibling));
+      if (NS_FAILED(res)) return res;
+      if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
+      {
+        // insert br node
+        res = CreateBR(inNode, 0, address_of(unused));
+        if (NS_FAILED(res)) return res;
+      }
+    }
+  }
+    
+  // now remove container
+  return RemoveContainer(inNode);
+}
+
+
 ///////////////////////////////////////////////////////////////////////////
 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
 //                   one within the parent
@@ -4163,7 +4260,7 @@ nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNo
   {
     res = node->GetNextSibling(getter_AddRefs(temp));
     if (NS_FAILED(res)) return res;
-    if (!temp) return NS_ERROR_FAILURE;
+    if (!temp) return NS_OK;  // return null sibling
     // if it's editable, we're done
     if (IsEditable(temp)) break;
     // otherwise try again
diff --git a/editor/base/nsHTMLEditor.h b/editor/base/nsHTMLEditor.h
index eced4d50cece..f2170f286953 100644
--- a/editor/base/nsHTMLEditor.h
+++ b/editor/base/nsHTMLEditor.h
@@ -642,6 +642,7 @@ protected:
   PRBool HasMatchingAttributes(nsIDOMNode *aNode1, 
                                nsIDOMNode *aNode2);
 
+  nsresult RemoveBlockContainer(nsIDOMNode *inNode);
   nsresult GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode);
   nsresult GetPriorHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode);
   nsresult GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode);
diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp
index ed7ee4e61565..9b43b81830af 100644
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -2108,41 +2108,10 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
   }
 
   // if there is only one node in the array, and it is a list, div, or blockquote,
-  // then look inside of it until we find what we want to make a list out of.
-  if (listCount == 1)
-  {
-    nsCOMPtr isupports = dont_AddRef(arrayOfNodes->ElementAt(0));
-    nsCOMPtr curNode( do_QueryInterface(isupports) );
-    
-    while (nsHTMLEditUtils::IsDiv(curNode)
-           || nsHTMLEditUtils::IsList(curNode)
-           || nsHTMLEditUtils::IsBlockquote(curNode))
-    {
-      // dive as long as there is only one child, and it is a list, div, blockquote
-      PRUint32 numChildren;
-      res = mHTMLEditor->CountEditableChildren(curNode, numChildren);
-      if (NS_FAILED(res)) return res;
-      
-      if (numChildren == 1)
-      {
-        // keep diving
-        nsCOMPtr  tmpNode = nsEditor::GetChildAt(curNode, 0);
-        if (nsHTMLEditUtils::IsDiv(tmpNode)
-            || nsHTMLEditUtils::IsList(tmpNode)
-            || nsHTMLEditUtils::IsBlockquote(tmpNode))
-        {
-          // check editablility XXX floppy moose
-          curNode = tmpNode;
-        }
-        else break;
-      }
-      else break;
-    }
-    // we've found innermost list/blockquote/div: 
-    // replace the one node in the array with this node
-    isupports = do_QueryInterface(curNode);
-    arrayOfNodes->ReplaceElementAt(isupports, 0);
-  }
+  // then look inside of it until we find inner list or content.
+
+  res = LookInsideDivBQandList(arrayOfNodes);
+  if (NS_FAILED(res)) return res;                                 
 
   // Next we detect all the transitions in the array, where a transition
   // means that adjacent nodes in the array don't have the same parent.
@@ -2209,7 +2178,7 @@ nsHTMLEditRules::WillMakeList(nsISelection *aSelection,
         if (NS_FAILED(res)) return res;
         res = ConvertListType(curNode, address_of(newBlock), *aListType, itemType);
         if (NS_FAILED(res)) return res;
-        res = mHTMLEditor->RemoveContainer(newBlock);
+        res = mHTMLEditor->RemoveBlockContainer(newBlock);
         if (NS_FAILED(res)) return res;
       }
       else
@@ -2358,7 +2327,7 @@ nsHTMLEditRules::WillRemoveList(nsISelection *aSelection,
   
   // use these ranges to contruct a list of nodes to act on.
   nsCOMPtr arrayOfNodes;
-  res = GetNodesForOperation(arrayOfRanges, address_of(arrayOfNodes), kMakeList);
+  res = GetListActionNodes(address_of(arrayOfNodes), PR_FALSE);
   if (NS_FAILED(res)) return res;                                 
                                      
   // Remove all non-editable nodes.  Leave them be.
@@ -2479,6 +2448,9 @@ nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection,
     nsString tString(*aBlockType);
     if (tString.EqualsWithConversion("blockquote"))
       res = MakeBlockquote(arrayOfNodes);
+    else if (tString.EqualsWithConversion("normal") ||
+             tString.IsEmpty() )
+      res = RemoveBlockStyle(arrayOfNodes);
     else
       res = ApplyBlockStyle(arrayOfNodes, aBlockType);
     return res;
@@ -2660,20 +2632,15 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
   if (NS_FAILED(res)) return res;
   
   // use these ranges to contruct a list of nodes to act on.
+
   nsCOMPtr arrayOfNodes;
   res = GetNodesForOperation(arrayOfRanges, address_of(arrayOfNodes), kOutdent);
   if (NS_FAILED(res)) return res;                                 
                                      
-  // Next we detect all the transitions in the array, where a transition
-  // means that adjacent nodes in the array don't have the same parent.
-  
-  nsVoidArray transitionList;
-  res = MakeTransitionList(arrayOfNodes, &transitionList);
-  if (NS_FAILED(res)) return res;                                 
-  
   // Ok, now go through all the nodes and remove a level of blockquoting, 
   // or whatever is appropriate.  Wohoo!
 
+  nsCOMPtr curBlockQuote, firstBQChild, lastBQChild;
   PRUint32 listCount;
   PRInt32 i;
   arrayOfNodes->Count(&listCount);
@@ -2687,78 +2654,175 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool *
     res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
     if (NS_FAILED(res)) return res;
     
-    if (nsHTMLEditUtils::IsList(curParent))  // move node out of list
+    // is it a blockquote?
+    if (nsHTMLEditUtils::IsBlockquote(curNode)) 
     {
-      if (nsHTMLEditUtils::IsList(curNode))  // just unwrap this sublist
+      // if it is a blockquote, remove it.
+      // So we need to finish up dealng with any curBlockQuote first.
+      if (curBlockQuote)
       {
-        res = mHTMLEditor->RemoveContainer(curNode);
+        res = RemovePartOfBlock(curBlockQuote, firstBQChild, lastBQChild);
         if (NS_FAILED(res)) return res;
+        curBlockQuote = 0;  firstBQChild = 0;  lastBQChild = 0;
       }
-      else  // we are moving a list item, but not whole list
-      {
-        PRBool bOutOfList;
-        res = PopListItem(curNode, &bOutOfList);
-        if (NS_FAILED(res)) return res;
-      }
-    }
-    else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, but parent is non-list: move list items out
-    {
-      nsCOMPtr child;
-      curNode->GetLastChild(getter_AddRefs(child));
-      while (child)
-      {
-        if (nsHTMLEditUtils::IsListItem(child))
-        {
-          PRBool bOutOfList;
-          res = PopListItem(child, &bOutOfList);
-          if (NS_FAILED(res)) return res;
-        }
-        else if (nsHTMLEditUtils::IsList(child))
-        {
-          // We have an embedded list, so move it out from under the
-          // parent list. Be sure to put it after the parent list
-          // because this loop iterates backwards through the parent's
-          // list of children.
-
-          res = mHTMLEditor->MoveNode(child, curParent, offset + 1);
-          if (NS_FAILED(res)) return res;
-        }
-        else
-        {
-          // delete any non- list items for now
-          res = mHTMLEditor->DeleteNode(child);
-          if (NS_FAILED(res)) return res;
-        }
-        curNode->GetLastChild(getter_AddRefs(child));
-      }
-      // delete the now-empty list
-      res = mHTMLEditor->DeleteNode(curNode);
+      res = mHTMLEditor->RemoveBlockContainer(curNode);
       if (NS_FAILED(res)) return res;
+      continue;
     }
-    else if (transitionList[i])  // not list related - look for enclosing blockquotes and remove
+    // is it a list item?
+    if (nsHTMLEditUtils::IsListItem(curNode)) 
     {
-      // look for a blockquote somewhere above us and remove it.
-      // this is a hack until i think about outdent for real.
-      nsCOMPtr n = curNode;
-      nsCOMPtr tmp;
-      while (!nsTextEditUtils::IsBody(n))
+      // if it is a list item, that means we are not outdenting whole list.
+      // So we need to finish up dealng with any curBlockQuote, and then
+      // pop this list item.
+      if (curBlockQuote)
       {
-        if (nsHTMLEditUtils::IsBlockquote(n))
-        {
-          res = AddTerminatingBR(n);
-          if (NS_FAILED(res)) return res;
-          mHTMLEditor->RemoveContainer(n);
-          break;
-        }
-        n->GetParentNode(getter_AddRefs(tmp));
-        n = tmp;
+        res = RemovePartOfBlock(curBlockQuote, firstBQChild, lastBQChild);
+        if (NS_FAILED(res)) return res;
+        curBlockQuote = 0;  firstBQChild = 0;  lastBQChild = 0;
+      }
+      PRBool bOutOfList;
+      res = PopListItem(curNode, &bOutOfList);
+      if (NS_FAILED(res)) return res;
+      continue;
+    }
+    // do we have a blockquote that we are already committed to removing?
+    if (curBlockQuote)
+    {
+      // if so, is this node a descendant?
+      if (nsHTMLEditUtils::IsDescendantOf(curNode, curBlockQuote))
+      {
+        lastBQChild = curNode;
+        continue;  // then we dont need to do anything different for this node
+      }
+      else
+      {
+        // otherwise, we have progressed beyond end of curBlockQuote,
+        // so lets handle it now.  We need to remove the portion of 
+        // curBlockQuote that contains [firstBQChild - lastBQChild].
+        res = RemovePartOfBlock(curBlockQuote, firstBQChild, lastBQChild);
+        if (NS_FAILED(res)) return res;
+        curBlockQuote = 0;  firstBQChild = 0;  lastBQChild = 0;
+        // fall out and handle curNode
       }
     }
+    
+    // are we inside a blockquote?
+    nsCOMPtr n = curNode;
+    nsCOMPtr tmp;
+    while (!nsTextEditUtils::IsBody(n))
+    {
+      n->GetParentNode(getter_AddRefs(tmp));
+      n = tmp;
+      if (nsHTMLEditUtils::IsBlockquote(n))
+      {
+        // if so, remember it, and remember first node we are taking out of it.
+        curBlockQuote = n;
+        firstBQChild  = curNode;
+        lastBQChild   = curNode;
+        break;
+      }
+    }
+
+    if (!curBlockQuote)
+    {
+      // could not find an enclosing blockquote for this node.  handle list cases.
+      if (nsHTMLEditUtils::IsList(curParent))  // move node out of list
+      {
+        if (nsHTMLEditUtils::IsList(curNode))  // just unwrap this sublist
+        {
+          res = mHTMLEditor->RemoveBlockContainer(curNode);
+          if (NS_FAILED(res)) return res;
+        }
+        // handled list item case above
+      }
+      else if (nsHTMLEditUtils::IsList(curNode)) // node is a list, but parent is non-list: move list items out
+      {
+        nsCOMPtr child;
+        curNode->GetLastChild(getter_AddRefs(child));
+        while (child)
+        {
+          if (nsHTMLEditUtils::IsListItem(child))
+          {
+            PRBool bOutOfList;
+            res = PopListItem(child, &bOutOfList);
+            if (NS_FAILED(res)) return res;
+          }
+          else if (nsHTMLEditUtils::IsList(child))
+          {
+            // We have an embedded list, so move it out from under the
+            // parent list. Be sure to put it after the parent list
+            // because this loop iterates backwards through the parent's
+            // list of children.
+
+            res = mHTMLEditor->MoveNode(child, curParent, offset + 1);
+            if (NS_FAILED(res)) return res;
+          }
+          else
+          {
+            // delete any non- list items for now
+            res = mHTMLEditor->DeleteNode(child);
+            if (NS_FAILED(res)) return res;
+          }
+          curNode->GetLastChild(getter_AddRefs(child));
+        }
+        // delete the now-empty list
+        res = mHTMLEditor->RemoveBlockContainer(curNode);
+        if (NS_FAILED(res)) return res;
+      }
+    }
+  }
+  if (curBlockQuote)
+  {
+    // we have a blockquote we haven't finished handling
+    res = RemovePartOfBlock(curBlockQuote, firstBQChild, lastBQChild);
+    if (NS_FAILED(res)) return res;
   }
 
   return res;
 }
 
+///////////////////////////////////////////////////////////////////////////
+// ConvertListType:  convert list type and list item type.
+//                
+//                  
+nsresult 
+nsHTMLEditRules::RemovePartOfBlock(nsIDOMNode *aBlock, 
+                                   nsIDOMNode *aStartChild, 
+                                   nsIDOMNode *aEndChild)
+{
+  if (!aBlock || !aStartChild || !aEndChild)
+    return NS_ERROR_NULL_POINTER;
+  
+  nsCOMPtr startParent, endParent, leftNode, rightNode;
+  PRInt32 startOffset, endOffset, offset;
+  nsresult res;
+
+  // get split point location
+  res = nsEditor::GetNodeLocation(aStartChild, address_of(startParent), &startOffset);
+  if (NS_FAILED(res)) return res;
+  
+  // do the splits!
+  res = mHTMLEditor->SplitNodeDeep(aBlock, startParent, startOffset, &offset, 
+                                   PR_TRUE, address_of(leftNode), address_of(rightNode));
+  if (NS_FAILED(res)) return res;
+  if (rightNode)  aBlock = rightNode;
+
+  // get split point location
+  res = nsEditor::GetNodeLocation(aEndChild, address_of(endParent), &endOffset);
+  if (NS_FAILED(res)) return res;
+  endOffset++;  // want to be after lastBQChild
+
+  // do the splits!
+  res = mHTMLEditor->SplitNodeDeep(aBlock, endParent, endOffset, &offset, 
+                                   PR_TRUE, address_of(leftNode), address_of(rightNode));
+  if (NS_FAILED(res)) return res;
+  if (leftNode)  aBlock = leftNode;
+  
+  // get rid of part of blockquote we are outdenting
+  res = mHTMLEditor->RemoveBlockContainer(aBlock);
+  return res;
+}
 
 ///////////////////////////////////////////////////////////////////////////
 // ConvertListType:  convert list type and list item type.
@@ -3891,6 +3955,23 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges,
       }
     }
   }
+  // outdent should look inside of divs.
+  if (inOperationType == kOutdent) 
+  {
+    PRUint32 listCount;
+    (*outArrayOfNodes)->Count(&listCount);
+    for (i=(PRInt32)listCount-1; i>=0; i--)
+    {
+      isupports = dont_AddRef((*outArrayOfNodes)->ElementAt(i));
+      nsCOMPtr node( do_QueryInterface(isupports) );
+      if (nsHTMLEditUtils::IsDiv(node))
+      {
+        (*outArrayOfNodes)->RemoveElementAt(i);
+        res = GetInnerContent(node, *outArrayOfNodes, PR_FALSE, PR_FALSE);
+        if (NS_FAILED(res)) return res;
+      }
+    }
+  }
 
 
   // post process the list to break up inline containers that contain br's.
@@ -4057,15 +4138,77 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes,
       (*outArrayOfNodes)->RemoveElementAt(i);
     }
     
-    // scan for table elements.  If we find table elements other than table,
+    // scan for table elements and divs.  If we find table elements other than table,
     // replace it with a list of any editable non-table content.
-    if (nsHTMLEditUtils::IsTableElement(testNode) && !nsHTMLEditUtils::IsTable(testNode))
+    if ( (nsHTMLEditUtils::IsTableElement(testNode) && !nsHTMLEditUtils::IsTable(testNode))
+         || nsHTMLEditUtils::IsDiv(testNode) )
     {
       (*outArrayOfNodes)->RemoveElementAt(i);
       res = GetInnerContent(testNode, *outArrayOfNodes, PR_FALSE);
       if (NS_FAILED(res)) return res;
     }
   }
+
+  // if there is only one node in the array, and it is a list, div, or blockquote,
+  // then look inside of it until we find inner list or content.
+  res = LookInsideDivBQandList(*outArrayOfNodes);
+  return res;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// LookInsideDivBQandList: 
+//                       
+nsresult 
+nsHTMLEditRules::LookInsideDivBQandList(nsISupportsArray *aNodeArray)
+{
+  // if there is only one node in the array, and it is a list, div, or blockquote,
+  // then look inside of it until we find inner list or content.
+  nsresult res = NS_OK;
+  PRUint32 listCount;
+  aNodeArray->Count(&listCount);
+  if (listCount == 1)
+  {
+    nsCOMPtr isupports = dont_AddRef(aNodeArray->ElementAt(0));
+    nsCOMPtr curNode( do_QueryInterface(isupports) );
+    
+    while (nsHTMLEditUtils::IsDiv(curNode)
+           || nsHTMLEditUtils::IsList(curNode)
+           || nsHTMLEditUtils::IsBlockquote(curNode))
+    {
+      // dive as long as there is only one child, and it is a list, div, blockquote
+      PRUint32 numChildren;
+      res = mHTMLEditor->CountEditableChildren(curNode, numChildren);
+      if (NS_FAILED(res)) return res;
+      
+      if (numChildren == 1)
+      {
+        // keep diving
+        nsCOMPtr  tmpNode = nsEditor::GetChildAt(curNode, 0);
+        if (nsHTMLEditUtils::IsDiv(tmpNode)
+            || nsHTMLEditUtils::IsList(tmpNode)
+            || nsHTMLEditUtils::IsBlockquote(tmpNode))
+        {
+          // check editablility XXX floppy moose
+          curNode = tmpNode;
+        }
+        else break;
+      }
+      else break;
+    }
+    // we've found innermost list/blockquote/div: 
+    // replace the one node in the array with these nodes
+    aNodeArray->RemoveElementAt(0);
+    if ((nsHTMLEditUtils::IsDiv(curNode) || nsHTMLEditUtils::IsBlockquote(curNode)))
+    {
+      res = GetInnerContent(curNode, aNodeArray, PR_FALSE, PR_FALSE);
+    }
+    else
+    {
+      nsCOMPtr isuports (do_QueryInterface(curNode));
+      res = aNodeArray->AppendElement(isuports);
+    }
+  }
   return res;
 }
 
@@ -4769,6 +4912,144 @@ nsHTMLEditRules::MakeBlockquote(nsISupportsArray *arrayOfNodes)
 
 
 
+///////////////////////////////////////////////////////////////////////////
+// RemoveBlockStyle:  make the list nodes have no special block type.  
+//                       
+nsresult 
+nsHTMLEditRules::RemoveBlockStyle(nsISupportsArray *arrayOfNodes)
+{
+  // intent of this routine is to be used for converting to/from
+  // headers, paragraphs, pre, and address.  Those blocks
+  // that pretty much just contain inline things...
+  
+  if (!arrayOfNodes) return NS_ERROR_NULL_POINTER;
+  nsresult res = NS_OK;
+  
+  nsCOMPtr curNode, curParent, curBlock, firstNode, lastNode;
+  PRInt32 offset;
+  PRUint32 listCount;
+    
+  arrayOfNodes->Count(&listCount);
+  
+  PRUint32 i;
+  for (i=0; i isupports  = dont_AddRef(arrayOfNodes->ElementAt(i));
+    curNode = do_QueryInterface(isupports);
+    res = nsEditor::GetNodeLocation(curNode, address_of(curParent), &offset);
+    if (NS_FAILED(res)) return res;
+    nsAutoString curNodeTag;
+    nsEditor::GetTagString(curNode, curNodeTag);
+    curNodeTag.ToLowerCase();
+ 
+    // if curNode is a address, p, header, address, or pre, remove it 
+    if ((curNodeTag.EqualsWithConversion("pre")) || 
+        (curNodeTag.EqualsWithConversion("p"))   ||
+        (curNodeTag.EqualsWithConversion("h1"))  ||
+        (curNodeTag.EqualsWithConversion("h2"))  ||
+        (curNodeTag.EqualsWithConversion("h3"))  ||
+        (curNodeTag.EqualsWithConversion("h4"))  ||
+        (curNodeTag.EqualsWithConversion("h5"))  ||
+        (curNodeTag.EqualsWithConversion("h6"))  ||
+        (curNodeTag.EqualsWithConversion("address")))
+    {
+      // process any partial progress saved
+      if (curBlock)
+      {
+        res = RemovePartOfBlock(curBlock, firstNode, lastNode);
+        if (NS_FAILED(res)) return res;
+        curBlock = 0;  firstNode = 0;  lastNode = 0;
+      }
+      // remove curent block
+      res = mHTMLEditor->RemoveBlockContainer(curNode); 
+      if (NS_FAILED(res)) return res;
+    }
+    else if ((curNodeTag.EqualsWithConversion("table"))      || 
+             (curNodeTag.EqualsWithConversion("tbody"))      ||
+             (curNodeTag.EqualsWithConversion("tr"))         ||
+             (curNodeTag.EqualsWithConversion("td"))         ||
+             (curNodeTag.EqualsWithConversion("ol"))         ||
+             (curNodeTag.EqualsWithConversion("ul"))         ||
+             (curNodeTag.EqualsWithConversion("dl"))         ||
+             (curNodeTag.EqualsWithConversion("li"))         ||
+             (curNodeTag.EqualsWithConversion("blockquote")) ||
+             (curNodeTag.EqualsWithConversion("div"))) 
+    {
+      // process any partial progress saved
+      if (curBlock)
+      {
+        res = RemovePartOfBlock(curBlock, firstNode, lastNode);
+        if (NS_FAILED(res)) return res;
+        curBlock = 0;  firstNode = 0;  lastNode = 0;
+      }
+      // recursion time
+      nsCOMPtr childArray;
+      res = GetChildNodesForOperation(curNode, address_of(childArray));
+      if (NS_FAILED(res)) return res;
+      res = RemoveBlockStyle(childArray);
+      if (NS_FAILED(res)) return res;
+    }
+    else if (IsInlineNode(curNode))
+    {
+      if (curBlock)
+      {
+        // if so, is this node a descendant?
+        if (nsHTMLEditUtils::IsDescendantOf(curNode, curBlock))
+        {
+          lastNode = curNode;
+          continue;  // then we dont need to do anything different for this node
+        }
+        else
+        {
+          // otherwise, we have progressed beyond end of curBlock,
+          // so lets handle it now.  We need to remove the portion of 
+          // curBlock that contains [firstNode - lastNode].
+          res = RemovePartOfBlock(curBlock, firstNode, lastNode);
+          if (NS_FAILED(res)) return res;
+          curBlock = 0;  firstNode = 0;  lastNode = 0;
+          // fall out and handle curNode
+        }
+      }
+      curBlock = mHTMLEditor->GetBlockNodeParent(curNode);
+      if ((curNodeTag.EqualsWithConversion("pre")) || 
+          (curNodeTag.EqualsWithConversion("p"))   ||
+          (curNodeTag.EqualsWithConversion("h1"))  ||
+          (curNodeTag.EqualsWithConversion("h2"))  ||
+          (curNodeTag.EqualsWithConversion("h3"))  ||
+          (curNodeTag.EqualsWithConversion("h4"))  ||
+          (curNodeTag.EqualsWithConversion("h5"))  ||
+          (curNodeTag.EqualsWithConversion("h6"))  ||
+          (curNodeTag.EqualsWithConversion("address")))
+      {
+        firstNode = curNode;  
+        lastNode = curNode;
+      }
+      else
+        curBlock = 0;  // not a block kind that we care about.
+    }
+    else
+    { // some node that is already sans block style.  skip over it and
+      // process any partial progress saved
+      if (curBlock)
+      {
+        res = RemovePartOfBlock(curBlock, firstNode, lastNode);
+        if (NS_FAILED(res)) return res;
+        curBlock = 0;  firstNode = 0;  lastNode = 0;
+      }
+    }
+  }
+  // process any partial progress saved
+  if (curBlock)
+  {
+    res = RemovePartOfBlock(curBlock, firstNode, lastNode);
+    if (NS_FAILED(res)) return res;
+    curBlock = 0;  firstNode = 0;  lastNode = 0;
+  }
+  return res;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////
 // ApplyBlockStyle:  do whatever it takes to make the list of nodes into 
 //                   one or more blocks of type blockTag.  
@@ -4786,15 +5067,8 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab
   nsCOMPtr curNode, curParent, curBlock, newBlock;
   PRInt32 offset;
   PRUint32 listCount;
-  PRBool bNoParent = PR_FALSE;
-  
-  // we special case an empty tag name to mean "remove block parents".
-  // This is used for the "normal" paragraph style in mail-compose
   nsString tString(*aBlockTag);////MJUDGE SCC NEED HELP
-  if (tString.IsEmpty() || 
-    !Compare(tString,NS_LITERAL_STRING("normal"),nsCaseInsensitiveStringComparator())) 
-    bNoParent = PR_TRUE;
-  
+
   arrayOfNodes->Count(&listCount);
   
   PRUint32 i;
@@ -4810,13 +5084,13 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab
     curNodeTag.ToLowerCase();
  
     // is it already the right kind of block?
-    if (!bNoParent && curNodeTag == *aBlockTag)
+    if (curNodeTag == *aBlockTag)
     {
       curBlock = 0;  // forget any previous block used for previous inline nodes
       continue;  // do nothing to this block
     }
         
-    // if curNode is a mozdiv, p, header, address, or pre, replace 
+    // if curNode is a address, p, header, address, or pre, replace 
     // it with a new block of correct type.
     // xxx floppy moose: pre cant hold everything the others can
     if (nsHTMLEditUtils::IsMozDiv(curNode)     ||
@@ -4831,17 +5105,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab
         (curNodeTag.EqualsWithConversion("address")))
     {
       curBlock = 0;  // forget any previous block used for previous inline nodes
-      if (bNoParent)
-      {
-        // make sure we have a normal br at end of block
-        res = AddTerminatingBR(curNode);
-        if (NS_FAILED(res)) return res;
-        res = mHTMLEditor->RemoveContainer(curNode); 
-      }
-      else
-      {
-        res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag);
-      }
+      res = mHTMLEditor->ReplaceContainer(curNode, address_of(newBlock), *aBlockTag);
       if (NS_FAILED(res)) return res;
     }
     else if ((curNodeTag.EqualsWithConversion("table"))      || 
@@ -4850,9 +5114,10 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab
              (curNodeTag.EqualsWithConversion("td"))         ||
              (curNodeTag.EqualsWithConversion("ol"))         ||
              (curNodeTag.EqualsWithConversion("ul"))         ||
+             (curNodeTag.EqualsWithConversion("dl"))         ||
              (curNodeTag.EqualsWithConversion("li"))         ||
              (curNodeTag.EqualsWithConversion("blockquote")) ||
-             (curNodeTag.EqualsWithConversion("div")))  // div's other than mozdivs
+             (curNodeTag.EqualsWithConversion("div"))) 
     {
       curBlock = 0;  // forget any previous block used for previous inline nodes
       // recursion time
@@ -4867,11 +5132,8 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab
     else if (curNodeTag.EqualsWithConversion("br"))
     {
       curBlock = 0;  // forget any previous block used for previous inline nodes
-      if (!bNoParent)
-      {
-        res = mHTMLEditor->DeleteNode(curNode);
-        if (NS_FAILED(res)) return res;
-      }
+      res = mHTMLEditor->DeleteNode(curNode);
+      if (NS_FAILED(res)) return res;
     }
         
     
@@ -4882,7 +5144,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadab
     // arrayOfNodes is contructed, but some additional logic should
     // be added here if that should change
     
-    else if (IsInlineNode(curNode) && !bNoParent)
+    else if (IsInlineNode(curNode))
     {
       // if curNode is a non editable, drop it if we are going to 
       if (!Compare(tString,NS_LITERAL_STRING("pre"),nsCaseInsensitiveStringComparator()) 
@@ -4958,43 +5220,6 @@ nsHTMLEditRules::SplitAsNeeded(const nsAReadableString *aTag,
   return res;
 }      
 
-///////////////////////////////////////////////////////////////////////////
-// AddTerminatingBR:  place an ordinary br node at the end of aBlock,   
-//                  if it doens't already have one.  If it has a moz-BR,
-//                  simply convertit to a normal br.  
-nsresult 
-nsHTMLEditRules::AddTerminatingBR(nsIDOMNode *aBlock)
-{
-  if (!aBlock) return NS_ERROR_NULL_POINTER;
-  nsCOMPtr last;
-  nsresult res = mHTMLEditor->GetLastEditableLeaf(aBlock, address_of(last));
-  if (last && nsTextEditUtils::IsBreak(last))
-  {
-    if (nsTextEditUtils::IsMozBR(last))
-    {
-      // need to convert a br
-      nsCOMPtr elem = do_QueryInterface(last);
-      res = mHTMLEditor->RemoveAttribute(elem, NS_ConvertASCIItoUCS2("type")); 
-      if (NS_FAILED(res)) return res;
-    }
-    else // we have what we want, we're done
-    {
-      return res;
-    }
-  }
-  else // need to add a br
-  {
-    PRUint32 len;
-    nsCOMPtr brNode;
-    res = mHTMLEditor->GetLengthOfDOMNode(aBlock, len);
-    if (NS_FAILED(res)) return res;
-    res = mHTMLEditor->CreateBR(aBlock, len, address_of(brNode));
-    if (NS_FAILED(res)) return res;
-  }
-  return res;
-}
-
-
 ///////////////////////////////////////////////////////////////////////////
 // JoinNodesSmart:  join two nodes, doing whatever makes sense for their  
 //                  children (which often means joining them, too).
@@ -5744,9 +5969,7 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
   if (!nsHTMLEditUtils::IsList(curParPar)
       && nsHTMLEditUtils::IsListItem(curNode)) 
   {
-    res = AddTerminatingBR(curNode);
-    if (NS_FAILED(res)) return res;
-    res = mHTMLEditor->RemoveContainer(curNode);
+    res = mHTMLEditor->RemoveBlockContainer(curNode);
     if (NS_FAILED(res)) return res;
     *aOutOfList = PR_TRUE;
   }
@@ -5788,7 +6011,7 @@ nsHTMLEditRules::RemoveListStructure(nsIDOMNode *aList)
     aList->GetFirstChild(getter_AddRefs(child));
   }
   // delete the now-empty list
-  res = mHTMLEditor->DeleteNode(aList);
+  res = mHTMLEditor->RemoveBlockContainer(aList);
   if (NS_FAILED(res)) return res;
 
   return res;
@@ -6128,12 +6351,3 @@ nsHTMLEditRules::DidDeleteSelection(nsISelection *aSelection)
 {
   return NS_OK;
 }
-
-
-
-
-
-
-
-
-
diff --git a/editor/libeditor/html/nsHTMLEditRules.h b/editor/libeditor/html/nsHTMLEditRules.h
index 8a499f5c4f37..4a770413e472 100644
--- a/editor/libeditor/html/nsHTMLEditRules.h
+++ b/editor/libeditor/html/nsHTMLEditRules.h
@@ -122,6 +122,9 @@ protected:
   nsresult ReturnInParagraph(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);
   nsresult ReturnInListItem(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
   nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection);
+  nsresult RemovePartOfBlock(nsIDOMNode *curBlockQuote, 
+                             nsIDOMNode *firstBQChild, 
+                             nsIDOMNode *lastBQChild);
   nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr *outList, const nsAReadableString& aListType, const nsAReadableString& aItemType);
   nsresult CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc);
   nsresult IsEmptyBlock(nsIDOMNode *aNode, 
@@ -155,12 +158,14 @@ protected:
   nsresult GetListActionNodes(nsCOMPtr *outArrayOfNodes, PRBool aEntireList, PRBool aDontTouchContent=PR_FALSE);
   nsresult GetDefinitionListItemTypes(nsIDOMNode *aNode, PRBool &aDT, PRBool &aDD);
   nsresult GetParagraphFormatNodes(nsCOMPtr *outArrayOfNodes, PRBool aDontTouchContent=PR_FALSE);
+  nsresult LookInsideDivBQandList(nsISupportsArray *aNodeArray);
   nsresult BustUpInlinesAtRangeEndpoints(nsRangeStore &inRange);
   nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode, 
                                    nsCOMPtr *outArrayOfNodes);
   nsCOMPtr GetHighestInlineParent(nsIDOMNode* aNode);
   nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, 
                                    nsVoidArray *inTransitionArray);
+  nsresult RemoveBlockStyle(nsISupportsArray *arrayOfNodes);
   nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsAReadableString *aBlockTag);
   nsresult MakeBlockquote(nsISupportsArray *arrayOfNodes);
   nsresult SplitAsNeeded(const nsAReadableString *aTag, nsCOMPtr *inOutParent, PRInt32 *inOutOffset);
diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp
index ec9ac4dfaf96..419f8e4637ff 100644
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -4095,6 +4095,103 @@ nsHTMLEditor::SetSelectionAtDocumentStart(nsISelection *aSelection)
 #pragma mark -
 #endif
 
+///////////////////////////////////////////////////////////////////////////
+// RemoveBlockContainer: remove inNode, reparenting it's children into their
+//                  the parent of inNode.  In addition, INSERT ANY BR's NEEDED
+//                  TO PRESERVE IDENTITY OF REMOVED BLOCK.
+//
+nsresult
+nsHTMLEditor::RemoveBlockContainer(nsIDOMNode *inNode)
+{
+  if (!inNode)
+    return NS_ERROR_NULL_POINTER;
+  nsresult res;
+  nsCOMPtr sibling, child, unused;
+  
+  // Two possibilities: the container cold be empty of editable content.
+  // If that is the case, we need to compare what is before and after inNode
+  // to determine if we need a br.
+  // Or it could not be empty, in which case we have to compare previous
+  // sibling and first child to determine if we need a leading br,
+  // and compare following sibling and last child to determine if we need a
+  // trailing br.
+  
+  res = GetFirstEditableChild(inNode, address_of(child));
+  if (NS_FAILED(res)) return res;
+  
+  if (child)  // the case of inNode not being empty
+  {
+    // we need a br at start unless:
+    // 1) previous sibling of inNode is a block, OR
+    // 2) previous sibling of inNode is a br, OR
+    // 3) first child of inNode is a block OR
+    // 4) either is null
+    
+    res = GetPriorHTMLSibling(inNode, address_of(sibling));
+    if (NS_FAILED(res)) return res;
+    if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
+    {
+      res = GetFirstEditableChild(inNode, address_of(child));
+      if (NS_FAILED(res)) return res;
+      if (child && !IsBlockNode(child))
+      {
+        // insert br node
+        res = CreateBR(inNode, 0, address_of(unused));
+        if (NS_FAILED(res)) return res;
+      }
+    }
+    
+    // we need a br at end unless:
+    // 1) following sibling of inNode is a block, OR
+    // 2) last child of inNode is a block, OR
+    // 3) last child of inNode is a block OR
+    // 4) either is null
+
+    res = GetNextHTMLSibling(inNode, address_of(sibling));
+    if (NS_FAILED(res)) return res;
+    if (sibling && !IsBlockNode(sibling))
+    {
+      res = GetLastEditableChild(inNode, address_of(child));
+      if (NS_FAILED(res)) return res;
+      if (child && !IsBlockNode(child) && !nsTextEditUtils::IsBreak(child))
+      {
+        // insert br node
+        PRUint32 len;
+        res = GetLengthOfDOMNode(inNode, len);
+        if (NS_FAILED(res)) return res;
+        res = CreateBR(inNode, (PRInt32)len, address_of(unused));
+        if (NS_FAILED(res)) return res;
+      }
+    }
+  }
+  else  // the case of inNode being empty
+  {
+    // we need a br at start unless:
+    // 1) previous sibling of inNode is a block, OR
+    // 2) previous sibling of inNode is a br, OR
+    // 3) following sibling of inNode is a block, OR
+    // 4) following sibling of inNode is a br OR
+    // 5) either is null
+    res = GetPriorHTMLSibling(inNode, address_of(sibling));
+    if (NS_FAILED(res)) return res;
+    if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
+    {
+      res = GetNextHTMLSibling(inNode, address_of(sibling));
+      if (NS_FAILED(res)) return res;
+      if (sibling && !IsBlockNode(sibling) && !nsTextEditUtils::IsBreak(sibling))
+      {
+        // insert br node
+        res = CreateBR(inNode, 0, address_of(unused));
+        if (NS_FAILED(res)) return res;
+      }
+    }
+  }
+    
+  // now remove container
+  return RemoveContainer(inNode);
+}
+
+
 ///////////////////////////////////////////////////////////////////////////
 // GetPriorHTMLSibling: returns the previous editable sibling, if there is
 //                   one within the parent
@@ -4163,7 +4260,7 @@ nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNo
   {
     res = node->GetNextSibling(getter_AddRefs(temp));
     if (NS_FAILED(res)) return res;
-    if (!temp) return NS_ERROR_FAILURE;
+    if (!temp) return NS_OK;  // return null sibling
     // if it's editable, we're done
     if (IsEditable(temp)) break;
     // otherwise try again
diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h
index eced4d50cece..f2170f286953 100644
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -642,6 +642,7 @@ protected:
   PRBool HasMatchingAttributes(nsIDOMNode *aNode1, 
                                nsIDOMNode *aNode2);
 
+  nsresult RemoveBlockContainer(nsIDOMNode *inNode);
   nsresult GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode);
   nsresult GetPriorHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode);
   nsresult GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode);