diff --git a/editor/base/nsEditor.cpp b/editor/base/nsEditor.cpp index 1bd657f3e99d..5156be6550e7 100644 --- a/editor/base/nsEditor.cpp +++ b/editor/base/nsEditor.cpp @@ -148,6 +148,7 @@ nsEditor::nsEditor() , mTxnStartNode(nsnull) , mTxnStartOffset(0) , mShouldTxnSetSelection(PR_TRUE) +, mBodyElement(nsnull) , mInIMEMode(PR_FALSE) , mIMETextRangeList(nsnull) , mIMETextNode(nsnull) @@ -1556,13 +1557,21 @@ nsEditor::ForceCompositionEnd() NS_IMETHODIMP nsEditor::GetBodyElement(nsIDOMElement **aBodyElement) { - nsresult result; + nsresult result = NS_OK; if (!aBodyElement) return NS_ERROR_NULL_POINTER; *aBodyElement = 0; + if (mBodyElement) + { + // if we have cached the body element, use that + *aBodyElement = mBodyElement; + NS_ADDREF(*aBodyElement); + return result; + } + NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak"); if (!mDocWeak) return NS_ERROR_NOT_INITIALIZED; @@ -1596,6 +1605,7 @@ nsEditor::GetBodyElement(nsIDOMElement **aBodyElement) nsCOMPtr bodyElement = do_QueryInterface(node); if (bodyElement) { + mBodyElement = do_QueryInterface(bodyElement); *aBodyElement = bodyElement; // A "getter" method should always addref NS_ADDREF(*aBodyElement); diff --git a/editor/base/nsEditor.h b/editor/base/nsEditor.h index 2241d5f4a3ff..5ddaecf8003c 100644 --- a/editor/base/nsEditor.h +++ b/editor/base/nsEditor.h @@ -649,6 +649,7 @@ protected: nsCOMPtr mTxnStartNode; // saved selection info to pass to placeholder at init time PRInt32 mTxnStartOffset; // " " " " PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns + nsCOMPtr mBodyElement; // cached body node // // data necessary to build IME transactions // diff --git a/editor/base/nsHTMLEditRules.cpp b/editor/base/nsHTMLEditRules.cpp index 5459b046bc01..61210c5608e4 100644 --- a/editor/base/nsHTMLEditRules.cpp +++ b/editor/base/nsHTMLEditRules.cpp @@ -128,6 +128,12 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) if (!mActionNesting) { mDocChangeRange = nsnull; // clear out our accounting of what changed + // turn off caret + nsCOMPtr pres; + mEditor->GetPresShell(getter_AddRefs(pres)); + if (pres) pres->SetCaretEnabled(PR_FALSE); + // check that selection is in subtree defined by body node + ConfirmSelectionInBody(); } mActionNesting++; return NS_OK; @@ -145,6 +151,7 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) nsresult res = NS_OK; if (!--mActionNesting) { + ConfirmSelectionInBody(); if (action == nsEditor::kOpIgnore) return NS_OK; nsCOMPtrselection; @@ -180,7 +187,13 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) if (NS_FAILED(res)) return res; // detect empty doc res = CreateBogusNodeIfNeeded(selection); + + // turn on caret + nsCOMPtr pres; + mEditor->GetPresShell(getter_AddRefs(pres)); + if (pres) pres->SetCaretEnabled(PR_TRUE); } + return res; } @@ -306,6 +319,11 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); if (NS_FAILED(res)) return res; + // dont put text in places that cant have it + nsAutoString textTag = "__moz_text"; + if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, textTag)) + return NS_ERROR_FAILURE; + // split any mailcites in the way if (mFlags & nsIHTMLEditor::eEditorMailMask) { @@ -362,44 +380,17 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (NS_FAILED(res)) return res; } } - -/* // if we are right after a moz br, delete it and make a new moz div - PRBool needMozDiv = PR_FALSE; - if (priorNode && IsBreak(priorNode) && HasMozAttr(priorNode) - && (blockParent == mEditor->GetBlockNodeParent(priorNode))) - { - res = mEditor->DeleteNode(priorNode); - if (NS_FAILED(res)) return res; - // but we only need to make a moz div if we weren't in a listitem - if (!IsListItem(blockParent)) - needMozDiv = PR_TRUE; - } - - // if we are directly in a body or (non-moz) div, create a moz-div. - // Also creat one if we detected a prior moz br (see above). - res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - if (needMozDiv || IsBody(selNode) || IsNormalDiv(selNode)) - { - // wrap things up in a moz-div - nsCOMPtr mozDiv; - res = CreateMozDiv(selNode, selOffset, &mozDiv); - if (NS_FAILED(res)) return res; - // put selection in it - res = aSelection->Collapse(mozDiv, 0); - if (NS_FAILED(res)) return res; - } -*/} + } char nbspStr[2] = {nbsp, 0}; PRBool bCancel; nsString theString(*inString); // copy instring for now if(aAction == kInsertTextIME) { - // special case for IME. We need this to : - // a) handle null strings, which are meaningful for IME - // b) prevent the string from being broken into substrings, - // which can happen in non-IME processing below. + // special case for IME. We need this to : + // a) handle null strings, which are meaningful for IME + // b) prevent the string from being broken into substrings, + // which can happen in non-IME processing below. // I should probably convert runs of spaces and tabs here as well res = DoTextInsertion(aSelection, &bCancel, &theString, typeInState); } @@ -421,6 +412,7 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (NS_FAILED(res)) return res; res = DoTextInsertion(aSelection, &bCancel, outString, typeInState); } +#if 0 // is it a solo space? else if (partialString == " ") { @@ -435,6 +427,7 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (NS_FAILED(res)) return res; res = DoTextInsertion(aSelection, &bCancel, outString, typeInState); } +#endif // is it a solo return? else if (partialString == "\n") { @@ -528,41 +521,6 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P if (!blockParent) return NS_ERROR_FAILURE; -/* // break action depends on type of block - PRBool bIsMozDiv = IsMozDiv(blockParent); - PRBool bIsNormalDiv = IsNormalDiv(blockParent); - - // body or normal div: insert a normal br - if (IsBody(blockParent) || bIsNormalDiv) - { - nsCOMPtr brNode; - res = mEditor->InsertBR(&brNode); - if (NS_FAILED(res)) return res; - *aHandled = PR_TRUE; - } - else if (bIsMozDiv) - { - // split it - PRInt32 newOffset; - res = mEditor->SplitNodeDeep( blockParent, node, offset, &newOffset); - if (NS_FAILED(res)) return res; - nsCOMPtr parent; - blockParent->GetParentNode(getter_AddRefs(parent)); - nsCOMPtr rightDiv = nsEditor::GetChildAt(parent, newOffset); - if (!rightDiv || !IsMozDiv(rightDiv)) return NS_ERROR_FAILURE; - // put the trailer br at the end of the lefthand mozdiv - nsCOMPtr leftDiv; - res = GetPriorHTMLSibling(rightDiv, &leftDiv); - if (NS_FAILED(res)) return res; - if (!leftDiv || !IsMozDiv(leftDiv)) return NS_ERROR_FAILURE; - res = AddTrailerBR(leftDiv); - if (NS_FAILED(res)) return res; - // put selection at beginning of righthand mozdiv - res = aSelection->Collapse(rightDiv, 0); - if (NS_FAILED(res)) return res; - *aHandled = PR_TRUE; - } -*/ // headers: close (or split) header else if (IsHeader(blockParent)) { @@ -677,14 +635,14 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, if (mEditor->NodesSameType(node, priorNode)) { // if so, join them! - nsCOMPtr topParent; - priorNode->GetParentNode(getter_AddRefs(topParent)); - *aHandled = PR_TRUE; - res = JoinNodesSmart(priorNode,node,&selNode,&selOffset); - if (NS_FAILED(res)) return res; - // fix up selection - res = aSelection->Collapse(selNode,selOffset); - return res; + nsCOMPtr topParent; + priorNode->GetParentNode(getter_AddRefs(topParent)); + *aHandled = PR_TRUE; + res = JoinNodesSmart(priorNode,node,&selNode,&selOffset); + if (NS_FAILED(res)) return res; + // fix up selection + res = aSelection->Collapse(selNode,selOffset); + return res; } } } @@ -775,14 +733,14 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, if (mEditor->NodesSameType(node, nextNode)) { // if so, join them! - nsCOMPtr topParent; - nextNode->GetParentNode(getter_AddRefs(topParent)); - *aHandled = PR_TRUE; - res = JoinNodesSmart(nextNode,node,&selNode,&selOffset); - if (NS_FAILED(res)) return res; - // fix up selection - res = aSelection->Collapse(selNode,selOffset); - return res; + nsCOMPtr topParent; + nextNode->GetParentNode(getter_AddRefs(topParent)); + *aHandled = PR_TRUE; + res = JoinNodesSmart(nextNode,node,&selNode,&selOffset); + if (NS_FAILED(res)) return res; + // fix up selection + res = aSelection->Collapse(selNode,selOffset); + return res; } } } @@ -881,18 +839,18 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, res = GetPriorHTMLNode(nodeToDelete, &brNode); if (IsBreak(brNode)) { - // is brNode also a descendant of same block? - nsCOMPtr block, brBlock; - block = mEditor->GetBlockNodeParent(nodeToDelete); - brBlock = mEditor->GetBlockNodeParent(brNode); - if (block == brBlock) - { - // delete both breaks - res = mEditor->DeleteNode(brNode); - if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(nodeToDelete); + // is brNode also a descendant of same block? + nsCOMPtr block, brBlock; + block = mEditor->GetBlockNodeParent(nodeToDelete); + brBlock = mEditor->GetBlockNodeParent(brNode); + if (block == brBlock) + { + // delete both breaks + res = mEditor->DeleteNode(brNode); + if (NS_FAILED(res)) return res; + res = mEditor->DeleteNode(nodeToDelete); *aHandled = PR_TRUE; - return res; + return res; } // else fall through } @@ -901,8 +859,8 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, // adjust selection to be right after it res = aSelection->Collapse(node, offset+1); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(nodeToDelete); - *aHandled = PR_TRUE; + res = mEditor->DeleteNode(nodeToDelete); + *aHandled = PR_TRUE; return res; } } @@ -2951,13 +2909,13 @@ nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection, // nsresult nsHTMLEditRules::ReturnInParagraph(nsIDOMSelection *aSelection, - nsIDOMNode *aHeader, + nsIDOMNode *aPara, nsIDOMNode *aNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled) { - if (!aSelection || !aHeader || !aNode || !aCancel || !aHandled) + if (!aSelection || !aPara || !aNode || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; } *aCancel = PR_FALSE; *aHandled = PR_FALSE; @@ -2984,18 +2942,18 @@ nsHTMLEditRules::ReturnInParagraph(nsIDOMSelection *aSelection, // just fall out to default of inserting a BR return res; } - if (IsBreak(sibling)) + if (IsBreak(sibling) && !HasMozAttr(sibling)) { PRInt32 newOffset; *aCancel = PR_TRUE; + // split the paragraph + res = mEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset); + if (NS_FAILED(res)) return res; // get rid of the break res = mEditor->DeleteNode(sibling); if (NS_FAILED(res)) return res; - // split the paragraph - res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset); - if (NS_FAILED(res)) return res; - // position selection inside textnode - res = aSelection->Collapse(aNode,0); + // position selection inside right hand para + res = aSelection->Collapse(aPara,0); } // else just fall out to default of inserting a BR return res; @@ -3011,18 +2969,18 @@ nsHTMLEditRules::ReturnInParagraph(nsIDOMSelection *aSelection, // just fall out to default of inserting a BR return res; } - if (IsBreak(sibling)) + if (IsBreak(sibling) && !HasMozAttr(sibling)) { PRInt32 newOffset; *aCancel = PR_TRUE; + // split the paragraph + res = mEditor->SplitNodeDeep(aPara, aNode, aOffset, &newOffset); + if (NS_FAILED(res)) return res; // get rid of the break res = mEditor->DeleteNode(sibling); if (NS_FAILED(res)) return res; - // split the paragraph - res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset); - if (NS_FAILED(res)) return res; - // position selection inside textnode - res = aSelection->Collapse(aNode,0); + // position selection inside right hand para + res = aSelection->Collapse(aPara,0); } // else just fall out to default of inserting a BR return res; @@ -3031,9 +2989,36 @@ nsHTMLEditRules::ReturnInParagraph(nsIDOMSelection *aSelection, // just fall out to default of inserting a BR return res; } - - // not in a text node. are we next to BR's? - // moose XXX + else + { + // not in a text node. + // is there a BR prior to it? + nsCOMPtr nearNode; + res = GetPriorHTMLNode(aNode, aOffset, &nearNode); + if (NS_FAILED(res)) return res; + if (!nearNode || !IsBreak(nearNode) || HasMozAttr(nearNode)) + { + // is there a BR after to it? + res = GetNextHTMLNode(aNode, aOffset, &nearNode); + if (NS_FAILED(res)) return res; + if (!nearNode || !IsBreak(nearNode) || HasMozAttr(nearNode)) + { + // just fall out to default of inserting a BR + return res; + } + } + // else remove sibling br and split para + PRInt32 newOffset; + *aCancel = PR_TRUE; + // split the paragraph + res = mEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset); + if (NS_FAILED(res)) return res; + // get rid of the break + res = mEditor->DeleteNode(nearNode); + if (NS_FAILED(res)) return res; + // selection to beginning of right hand para + aSelection->Collapse(aPara,0); + } return res; } @@ -3103,18 +3088,18 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, // if we fail, then that means we need toinsert a break res = AdjustSelection(aSelection, nsIEditor::eNext); if (NS_FAILED(res)) return res; - // get the selection location - nsCOMPtr selNode; - PRInt32 selOffset; - res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); - if (NS_FAILED(res)) return res; + // get the selection location + nsCOMPtr selNode; + PRInt32 selOffset; + res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; - // are we in a text node? - nsCOMPtr textNode = do_QueryInterface(selNode); - if (!textNode) - { - // time to insert a break - nsCOMPtr brNode; + // are we in a text node? + nsCOMPtr textNode = do_QueryInterface(selNode); + if (!textNode) + { + // time to insert a break + nsCOMPtr brNode; res = CreateMozBR(selNode, selOffset, &brNode); if (NS_FAILED(res)) return res; res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); @@ -3131,18 +3116,6 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset); if (NS_FAILED(res)) return res; res = aSelection->Collapse(aListItem,0); -#if 0 - // insert a moz-br if new list item is empty - PRBool isEmptyNode; - nsCOMPtr brNode; - res = IsEmptyNode( aListItem, &isEmptyNode); - if (NS_FAILED(res)) return res; - if (isEmptyNode) - { - res = CreateMozBR(aListItem, 0, &brNode); - if (NS_FAILED(res)) return res; - } -#endif return res; } @@ -3666,7 +3639,7 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect if (!aSelection) return NS_ERROR_NULL_POINTER; // if the selection isn't collapsed, do nothing. - // moose: one thing to do instead ischeck for the case of + // moose: one thing to do instead is check for the case of // only a single break selected, and collapse it. Good thing? Beats me. PRBool bCollapsed; nsresult res = aSelection->GetIsCollapsed(&bCollapsed); @@ -3674,14 +3647,26 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect if (!bCollapsed) return res; // get the (collapsed) selection location - nsCOMPtr selNode; + nsCOMPtr selNode, temp; PRInt32 selOffset; res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); if (NS_FAILED(res)) return res; - + temp = selNode; + + // are we in an editable node? + while (!mEditor->IsEditable(selNode)) + { + // scan up the tree until we find an editable place to be + res = nsEditor::GetNodeLocation(temp, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + if (!selNode) return NS_ERROR_FAILURE; + temp = selNode; + } + // are we in a text node? nsCOMPtr textNode = do_QueryInterface(selNode); - if (textNode) return NS_OK; // we LIKE it when we are in a text node. that RULZ + if (textNode) + return NS_OK; // we LIKE it when we are in a text node. that RULZ // do we need to insert a special mozBR? We do if we are: // 1) in a collapsed selection AND @@ -3698,11 +3683,11 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect if (NS_FAILED(res)) return res; if (bIsLast) { - // is nearNode also a descendant of same block? - nsCOMPtr block, nearBlock; - if (mEditor->IsBlockNode(selNode)) block = selNode; - else block = mEditor->GetBlockNodeParent(selNode); - nearBlock = mEditor->GetBlockNodeParent(nearNode); + // is nearNode also a descendant of same block? + nsCOMPtr block, nearBlock; + if (mEditor->IsBlockNode(selNode)) block = selNode; + else block = mEditor->GetBlockNodeParent(selNode); + nearBlock = mEditor->GetBlockNodeParent(nearNode); if (block == nearBlock) { // need to insert special moz BR. Why? Because if we don't @@ -3711,10 +3696,10 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect nsCOMPtr brNode; res = CreateMozBR(selNode, selOffset, &brNode); if (NS_FAILED(res)) return res; - res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - res = aSelection->Collapse(selNode,selOffset+1); - if (NS_FAILED(res)) return res; + res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(selNode,selOffset+1); + if (NS_FAILED(res)) return res; } } else @@ -3723,16 +3708,37 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect // with a mozBR already exiting after it. In this case we have to // move the selection to after the mozBR so it will show up on the // empty line. - nsCOMPtr nextNode; - res = GetNextHTMLNode(nearNode, &nextNode); - if (NS_FAILED(res)) return res; - if (IsMozBR(nextNode)) - { - res = nsEditor::GetNodeLocation(nextNode, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - res = aSelection->Collapse(selNode,selOffset+1); - if (NS_FAILED(res)) return res; - } + nsCOMPtr nextNode; + res = GetNextHTMLNode(nearNode, &nextNode); + if (NS_FAILED(res)) return res; + if (nextNode && IsMozBR(nextNode)) + { + res = nsEditor::GetNodeLocation(nextNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(selNode,selOffset+1); + if (NS_FAILED(res)) return res; + } + else + { + // alright, not that either. But there's one more squirrel to feed: + // the br might be right in front of a new block (ie,: + // text
  1. list item
) + // in this case we also need moz-br. + res = GetNextHTMLSibling(nearNode, &nextNode); + if (NS_FAILED(res)) return res; + if (nextNode && mEditor->IsBlockNode(nextNode)) + { + // need to insert special moz BR. Why? Because if we don't + // the user will see no new line for the break. + nsCOMPtr brNode; + res = CreateMozBR(selNode, selOffset, &brNode); + if (NS_FAILED(res)) return res; + res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(selNode,selOffset+1); + if (NS_FAILED(res)) return res; + } + } } } @@ -3755,6 +3761,7 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect dir = aAction; } // is the nearnode a text node? + if (!nearNode) return NS_OK; // couldn't find a near text node textNode = do_QueryInterface(nearNode); if (!textNode) return NS_ERROR_UNEXPECTED; PRInt32 offset = 0; @@ -3931,21 +3938,21 @@ nsHTMLEditRules::DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aS if (runStr != newStr) { // delete the original whitespace run - EditTxn *txn; + EditTxn *txn; // note 1: we are not telling edit listeners about these because they don't care // note 2: we are not wrapping these in a placeholder because we know they already are res = mEditor->CreateTxnForDeleteText(aTextNode, aStart+runStart, runEnd-runStart, (DeleteTextTxn**)&txn); if (NS_FAILED(res)) return res; - if (!txn) return NS_ERROR_OUT_OF_MEMORY; + if (!txn) return NS_ERROR_OUT_OF_MEMORY; res = mEditor->Do(txn); if (NS_FAILED(res)) return res; // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); // insert the new run - res = mEditor->CreateTxnForInsertText(newStr, aTextNode, aStart+runStart, (InsertTextTxn**)&txn); + res = mEditor->CreateTxnForInsertText(newStr, aTextNode, aStart+runStart, (InsertTextTxn**)&txn); if (NS_FAILED(res)) return res; - if (!txn) return NS_ERROR_OUT_OF_MEMORY; + if (!txn) return NS_ERROR_OUT_OF_MEMORY; res = mEditor->Do(txn); // The transaction system (if any) has taken ownwership of txns. NS_IF_RELEASE(txn); @@ -4078,36 +4085,68 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList) } -// not needed for now - leaving around in case we go back to it -#if 0 nsresult -nsHTMLEditRules::AddTrailerBR(nsIDOMNode *aNode) +nsHTMLEditRules::ConfirmSelectionInBody() { - // check parms - if (!aNode) return NS_ERROR_NULL_POINTER; - nsresult res = NS_OK; - // comfirm that this node is a type that we add trailer br's to: - // td, th, li - if (IsListItem(aNode)) // add more types later + nsCOMPtr bodyElement; + nsCOMPtr bodyNode; + + // get the body + res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_UNEXPECTED; + bodyNode = do_QueryInterface(bodyElement); + + // get the selection + nsCOMPtrselection; + res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + // get the selection start location + nsCOMPtr selNode, temp, parent; + PRInt32 selOffset; + res = mEditor->GetStartNodeAndOffset(selection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + temp = selNode; + + // check that selNode is inside body + while (temp && !IsBody(temp)) { - PRUint32 count; - nsCOMPtr brNode; - res = mEditor->GetLengthOfDOMNode(aNode, count); - if (NS_FAILED(res)) return res; - res = mEditor->CreateBR(aNode, count, &brNode); - if (NS_FAILED(res)) return res; - // give it special trailer attr - nsCOMPtr brElem = do_QueryInterface(brNode); - res = mEditor->SetAttribute(brElem, "type", "_moz_trailer"); + res = temp->GetParentNode(getter_AddRefs(parent)); + temp = parent; } - else + + // if we aren't in the body, force the issue + if (!temp) { - NS_NOTREACHED("editor error: nsHTMLEditRules::AddTrailerBR() called on bad node type"); +// uncomment this to see when we get bad selections +// NS_NOTREACHED("selection not in body"); + selection->Collapse(bodyNode,0); } - return res; + + // get the selection end location + res = mEditor->GetEndNodeAndOffset(selection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + temp = selNode; + + // check that selNode is inside body + while (temp && !IsBody(temp)) + { + res = temp->GetParentNode(getter_AddRefs(parent)); + temp = parent; + } + + // if we aren't in the body, force the issue + if (!temp) + { +// uncomment this to see when we get bad selections +// NS_NOTREACHED("selection not in body"); + selection->Collapse(bodyNode,0); + } + } -#endif + nsresult nsHTMLEditRules::UpdateDocChangeRange(nsIDOMRange *aRange) diff --git a/editor/base/nsHTMLEditRules.h b/editor/base/nsHTMLEditRules.h index 51aac544b995..d1f348e568eb 100644 --- a/editor/base/nsHTMLEditRules.h +++ b/editor/base/nsHTMLEditRules.h @@ -156,6 +156,7 @@ protected: nsresult UpdateDocChangeRange(nsIDOMRange *aRange); nsresult ConvertWhitespace(const nsString & inString, nsString & outString); + nsresult ConfirmSelectionInBody(); // removed from use: #if 0 diff --git a/editor/base/nsHTMLEditor.cpp b/editor/base/nsHTMLEditor.cpp index 18f5ee596644..340c7486fd96 100644 --- a/editor/base/nsHTMLEditor.cpp +++ b/editor/base/nsHTMLEditor.cpp @@ -3639,43 +3639,48 @@ NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation() NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation) { nsAutoEditBatch beginBatching(this); - nsCOMPtr newNode; - nsAutoString tag("blockquote"); - nsresult res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (NS_FAILED(res)) return res; - if (!newNode) return NS_ERROR_NULL_POINTER; - // Try to set type=cite. Ignore it if this fails. - nsCOMPtr newElement (do_QueryInterface(newNode)); - if (newElement) - { - nsAutoString type ("type"); - nsAutoString cite ("cite"); - newElement->SetAttribute(type, cite); - } - - // Set the selection to the underneath the node we just inserted: + // get selection nsCOMPtr selection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res) || !selection) - { -#ifdef DEBUG_akkana - printf("Can't get selection!"); -#endif - } + nsresult res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - res = selection->Collapse(newNode, 0); - if (NS_FAILED(res)) + // give rules a chance to handle or cancel + nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement); + PRBool cancel, handled; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (cancel) return NS_OK; // rules canceled the operation + if (!handled) { -#ifdef DEBUG_akkana - printf("Couldn't collapse"); -#endif - // XXX: error result: should res be returned here? - } + nsCOMPtr newNode; + nsAutoString tag("blockquote"); + res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (NS_FAILED(res)) return res; + if (!newNode) return NS_ERROR_NULL_POINTER; - res = Paste(); + // Try to set type=cite. Ignore it if this fails. + nsCOMPtr newElement (do_QueryInterface(newNode)); + if (newElement) + { + nsAutoString type ("type"); + nsAutoString cite ("cite"); + newElement->SetAttribute(type, cite); + } + + // Set the selection to the underneath the node we just inserted: + res = selection->Collapse(newNode, 0); + if (NS_FAILED(res)) + { +#ifdef DEBUG_akkana + printf("Couldn't collapse"); +#endif + // XXX: error result: should res be returned here? + } + + res = Paste(); + } return res; } @@ -3800,42 +3805,53 @@ nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText, nsAutoEditBatch beginBatching(this); - // Wrap the inserted quote in a
 so it won't be wrapped:
-  nsCOMPtr preNode;
-  nsAutoString tag("pre");
-  rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
-
-  // If this succeeded, then set selection inside the pre
-  // so the inserted text will end up there.
-  // If it failed, we don't care what the return value was,
-  // but we'll fall through and try to insert the text anyway.
+  // get selection
   nsCOMPtr selection;
   rv = GetSelection(getter_AddRefs(selection));
-  if (NS_SUCCEEDED(rv) && preNode)
-  {
-    if (NS_SUCCEEDED(rv) && selection)
-      selection->Collapse(preNode, 0);
-  }
+  if (NS_FAILED(rv)) return rv;
+  if (!selection) return NS_ERROR_NULL_POINTER;
 
-  rv = InsertText(quotedStuff);
-  if (aNodeInserted)
+  // give rules a chance to handle or cancel
+  nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
+  PRBool cancel, handled;
+  rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  if (NS_FAILED(rv)) return rv;
+  if (cancel) return NS_OK; // rules canceled the operation
+  if (!handled)
   {
-    if (NS_SUCCEEDED(rv))
+    // Wrap the inserted quote in a 
 so it won't be wrapped:
+    nsCOMPtr preNode;
+    nsAutoString tag("pre");
+    rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
+    
+    // If this succeeded, then set selection inside the pre
+    // so the inserted text will end up there.
+    // If it failed, we don't care what the return value was,
+    // but we'll fall through and try to insert the text anyway.
+    if (NS_SUCCEEDED(rv) && preNode)
     {
-      *aNodeInserted = preNode;
-      NS_IF_ADDREF(*aNodeInserted);
+      selection->Collapse(preNode, 0);
+    }
+
+    rv = InsertText(quotedStuff);
+    if (aNodeInserted)
+    {
+      if (NS_SUCCEEDED(rv))
+      {
+        *aNodeInserted = preNode;
+        NS_IF_ADDREF(*aNodeInserted);
+      }
+    }
+
+    // Set the selection to just after the inserted node:
+    if (NS_SUCCEEDED(rv) && preNode)
+    {
+      nsCOMPtr parent;
+      PRInt32 offset;
+      if (NS_SUCCEEDED(GetNodeLocation(preNode, &parent, &offset)) && parent)
+        selection->Collapse(parent, offset+1);
     }
   }
-
-  // Set the selection to just after the inserted node:
-  if (NS_SUCCEEDED(rv) && preNode)
-  {
-    nsCOMPtr parent;
-    PRInt32 offset;
-    if (NS_SUCCEEDED(GetNodeLocation(preNode, &parent, &offset)) && parent)
-      selection->Collapse(parent, offset+1);
-  }
-
   return rv;
 }
 
@@ -3848,48 +3864,61 @@ nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
   nsAutoEditBatch beginBatching(this);
   nsCOMPtr newNode;
   nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
-  nsAutoString tag("blockquote");
-  nsresult res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
-  if (NS_FAILED(res)) return res;
-  if (!newNode) return NS_ERROR_NULL_POINTER;
 
-  // Try to set type=cite.  Ignore it if this fails.
-  nsCOMPtr newElement (do_QueryInterface(newNode));
+  // get selection
   nsCOMPtr selection;
-  res = GetSelection(getter_AddRefs(selection));
-  if (newElement)
+  nsresult res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res)) return res;
+  if (!selection) return NS_ERROR_NULL_POINTER;
+
+  // give rules a chance to handle or cancel
+  nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
+  PRBool cancel, handled;
+  res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  if (NS_FAILED(res)) return res;
+  if (cancel) return NS_OK; // rules canceled the operation
+  if (!handled)
   {
-    nsAutoString type ("type");
-    nsAutoString cite ("cite");
-    newElement->SetAttribute(type, cite);
+    nsAutoString tag("blockquote");
+    res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+    if (NS_FAILED(res)) return res;
+    if (!newNode) return NS_ERROR_NULL_POINTER;
 
-    if (aCitation.Length() > 0)
-      newElement->SetAttribute(cite, aCitation);
-
-    // Set the selection inside the blockquote so aQuotedText will go there:
-    if (NS_SUCCEEDED(res) && selection)
-      selection->Collapse(newNode, 0);
-  }
-
-  res = InsertHTMLWithCharset(aQuotedText, aCharset);
-  if (aNodeInserted)
-  {
-    if (NS_SUCCEEDED(res))
+    // Try to set type=cite.  Ignore it if this fails.
+    nsCOMPtr newElement (do_QueryInterface(newNode));
+    if (newElement)
     {
-      *aNodeInserted = newNode;
-      NS_IF_ADDREF(*aNodeInserted);
+      nsAutoString type ("type");
+      nsAutoString cite ("cite");
+      newElement->SetAttribute(type, cite);
+
+      if (aCitation.Length() > 0)
+        newElement->SetAttribute(cite, aCitation);
+
+      // Set the selection inside the blockquote so aQuotedText will go there:
+      if (NS_SUCCEEDED(res))
+        selection->Collapse(newNode, 0);
+    }
+
+    res = InsertHTMLWithCharset(aQuotedText, aCharset);
+    if (aNodeInserted)
+    {
+      if (NS_SUCCEEDED(res))
+      {
+        *aNodeInserted = newNode;
+        NS_IF_ADDREF(*aNodeInserted);
+      }
+    }
+
+    // Set the selection to just after the inserted node:
+    if (NS_SUCCEEDED(res) && newNode)
+    {
+      nsCOMPtr parent;
+      PRInt32 offset;
+      if (NS_SUCCEEDED(GetNodeLocation(newNode, &parent, &offset)) && parent)
+        selection->Collapse(parent, offset+1);
     }
   }
-
-  // Set the selection to just after the inserted node:
-  if (NS_SUCCEEDED(res) && newNode)
-  {
-    nsCOMPtr parent;
-    PRInt32 offset;
-    if (NS_SUCCEEDED(GetNodeLocation(newNode, &parent, &offset)) && parent)
-      selection->Collapse(parent, offset+1);
-  }
-
   return res;
 }
 
@@ -4228,6 +4257,17 @@ nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
 }
 
 
+PRBool 
+nsHTMLEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aTag)
+{
+  // CNavDTD gives some unwanted results.  We override them here.
+  // if parent is a list and tag is text, say "no". 
+  if (IsListNode(aParent) && (aTag == "__moz_text"))
+    return PR_FALSE;
+  // else fall thru
+  return nsEditor::CanContainTag(aParent, aTag);
+}
+
 
 #ifdef XP_MAC
 #pragma mark -
@@ -5266,6 +5306,8 @@ nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement)
           node = firstChild;
         }
       }
+# if 0 
+// I've ifdef'd this out because it isn't finished and I'm not sure what the intent is.
       PRInt32 offset = 0;
       nsCOMPtrlastChild;
       res = parent->GetLastChild(getter_AddRefs(lastChild));
@@ -5291,13 +5333,14 @@ nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement)
           // We have > 1 node, so set to end of content
         }
       }
+#endif
       // Set selection at beginning of deepest node
       // Should we set 
       nsCOMPtr selection;
       res = GetSelection(getter_AddRefs(selection));
-      if (NS_SUCCEEDED(res) && selection)
+      if (NS_SUCCEEDED(res) && selection && firstChild)
       {
-        res = selection->Collapse(parent, offset);
+        res = selection->Collapse(firstChild, 0);
         if (NS_SUCCEEDED(res))
           caretIsSet = PR_TRUE;
       }
diff --git a/editor/base/nsHTMLEditor.h b/editor/base/nsHTMLEditor.h
index bdfbd5ddd860..da9f8d795227 100644
--- a/editor/base/nsHTMLEditor.h
+++ b/editor/base/nsHTMLEditor.h
@@ -244,6 +244,8 @@ public:
   /* ------------ nsICSSLoaderObserver -------------- */
   NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
 
+  /* ------------ nsEditor overrides ---------------- */
+
   /** All editor operations which alter the doc should be prefaced
    *  with a call to StartOperation, naming the action and direction */
   NS_IMETHOD StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection);
@@ -252,6 +254,9 @@ public:
    *  with a call to EndOperation, naming the action and direction */
   NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection);
 
+  /** returns PR_TRUE if aParent can contain a child of type aTag */
+  PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);
+  
   /* ------------ Utility Routines, not part of public API -------------- */
   NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext);
 
diff --git a/editor/base/nsTextEditRules.cpp b/editor/base/nsTextEditRules.cpp
index bb82d30cf985..b2fd0be9d622 100644
--- a/editor/base/nsTextEditRules.cpp
+++ b/editor/base/nsTextEditRules.cpp
@@ -299,7 +299,7 @@ nsTextEditRules::WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel)
   // get the (collapsed) selection location
   res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
   if (NS_FAILED(res)) return res;
-  // get next node
+  // get prior node
   res = GetPriorHTMLNode(selNode, selOffset, &priorNode);
   if (NS_SUCCEEDED(res) && priorNode && IsMozBR(priorNode))    
   {
@@ -1014,8 +1014,6 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection,
   PRBool isCollapsed;
   aSelection->GetIsCollapsed(&isCollapsed);
   NS_ASSERTION(PR_TRUE==isCollapsed, "selection not collapsed after delete selection.");
-  // if the delete selection resulted in no content 
-  // insert a special bogus text node with a   character in it.
   if (NS_SUCCEEDED(res)) // only do this work if DeleteSelection completed successfully
   {
     // if we don't have an empty document, check the selection to see if any collapsing is necessary
@@ -1047,7 +1045,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection,
       {
         nsCOMPtrselectedNodeAsText;
         selectedNodeAsText = do_QueryInterface(selectedNode);
-        if (selectedNodeAsText)
+        if (selectedNodeAsText && mEditor->IsEditable(selectedNode))
         {
           nsCOMPtr siblingNode;
           selectedNode->GetPreviousSibling(getter_AddRefs(siblingNode));
@@ -1055,7 +1053,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection,
           {
             nsCOMPtrsiblingNodeAsText;
             siblingNodeAsText = do_QueryInterface(siblingNode);
-            if (siblingNodeAsText)
+            if (siblingNodeAsText && mEditor->IsEditable(siblingNode))
             {
               PRUint32 siblingLength; // the length of siblingNode before the join
               siblingNodeAsText->GetLength(&siblingLength);
@@ -1072,7 +1070,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection,
           {
             nsCOMPtrsiblingNodeAsText;
             siblingNodeAsText = do_QueryInterface(siblingNode);
-            if (siblingNodeAsText)
+            if (siblingNodeAsText && mEditor->IsEditable(siblingNode))
             {
               PRUint32 selectedNodeLength; // the length of siblingNode before the join
               selectedNodeAsText->GetLength(&selectedNodeLength);
diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp
index 1bd657f3e99d..5156be6550e7 100644
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -148,6 +148,7 @@ nsEditor::nsEditor()
 ,  mTxnStartNode(nsnull)
 ,  mTxnStartOffset(0)
 ,  mShouldTxnSetSelection(PR_TRUE)
+,  mBodyElement(nsnull)
 ,  mInIMEMode(PR_FALSE)
 ,  mIMETextRangeList(nsnull)
 ,  mIMETextNode(nsnull)
@@ -1556,13 +1557,21 @@ nsEditor::ForceCompositionEnd()
 NS_IMETHODIMP 
 nsEditor::GetBodyElement(nsIDOMElement **aBodyElement)
 {
-  nsresult result;
+  nsresult result = NS_OK;
 
   if (!aBodyElement)
     return NS_ERROR_NULL_POINTER;
 
   *aBodyElement = 0;
   
+  if (mBodyElement)
+  {
+    // if we have cached the body element, use that
+    *aBodyElement = mBodyElement;
+    NS_ADDREF(*aBodyElement);
+    return result;
+  }
+  
   NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
   if (!mDocWeak)
     return NS_ERROR_NOT_INITIALIZED;
@@ -1596,6 +1605,7 @@ nsEditor::GetBodyElement(nsIDOMElement **aBodyElement)
     nsCOMPtr bodyElement = do_QueryInterface(node);
     if (bodyElement)
     {
+      mBodyElement = do_QueryInterface(bodyElement);
       *aBodyElement = bodyElement;
       // A "getter" method should always addref
       NS_ADDREF(*aBodyElement);
diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h
index 2241d5f4a3ff..5ddaecf8003c 100644
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -649,6 +649,7 @@ protected:
   nsCOMPtr mTxnStartNode;  // saved selection info to pass to placeholder at init time
   PRInt32         mTxnStartOffset;     //  "  "  "  "
   PRBool          mShouldTxnSetSelection;  // turn off for conservative selection adjustment by txns
+  nsCOMPtr mBodyElement;    // cached body node
   //
   // data necessary to build IME transactions
   //
diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp
index 5459b046bc01..61210c5608e4 100644
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -128,6 +128,12 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
   if (!mActionNesting)
   {
     mDocChangeRange = nsnull;  // clear out our accounting of what changed
+    // turn off caret
+    nsCOMPtr pres;
+    mEditor->GetPresShell(getter_AddRefs(pres));
+    if (pres) pres->SetCaretEnabled(PR_FALSE);
+    // check that selection is in subtree defined by body node
+    ConfirmSelectionInBody();
   }
   mActionNesting++;
   return NS_OK;
@@ -145,6 +151,7 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
   nsresult res = NS_OK;
   if (!--mActionNesting)
   {
+    ConfirmSelectionInBody();
     if (action == nsEditor::kOpIgnore) return NS_OK;
     
     nsCOMPtrselection;
@@ -180,7 +187,13 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
     if (NS_FAILED(res)) return res;
     // detect empty doc
     res = CreateBogusNodeIfNeeded(selection);
+
+    // turn on caret
+    nsCOMPtr pres;
+    mEditor->GetPresShell(getter_AddRefs(pres));
+    if (pres) pres->SetCaretEnabled(PR_TRUE);
   }
+
   return res;
 }
 
@@ -306,6 +319,11 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
   res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
   if (NS_FAILED(res)) return res;
 
+  // dont put text in places that cant have it
+  nsAutoString textTag = "__moz_text";
+  if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, textTag))
+    return NS_ERROR_FAILURE;
+
   // split any mailcites in the way
   if (mFlags & nsIHTMLEditor::eEditorMailMask)
   {
@@ -362,44 +380,17 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
         if (NS_FAILED(res)) return res;
       }
     }
-  
-/*    // if we are right after a moz br, delete it and make a new moz div
-    PRBool needMozDiv = PR_FALSE;
-    if (priorNode && IsBreak(priorNode) && HasMozAttr(priorNode)  
-         && (blockParent == mEditor->GetBlockNodeParent(priorNode)))
-    {
-      res = mEditor->DeleteNode(priorNode);
-      if (NS_FAILED(res)) return res;
-      // but we only need to make a moz div if we weren't in a listitem
-      if (!IsListItem(blockParent)) 
-        needMozDiv = PR_TRUE;
-    }
-  
-    // if we are directly in a body or (non-moz) div, create a moz-div.
-    // Also creat one if we detected a prior moz br (see above).
-    res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
-    if (NS_FAILED(res)) return res;
-    if (needMozDiv || IsBody(selNode) || IsNormalDiv(selNode))
-    {
-      // wrap things up in a moz-div
-      nsCOMPtr mozDiv;
-      res = CreateMozDiv(selNode, selOffset, &mozDiv);
-      if (NS_FAILED(res)) return res;
-      // put selection in it
-      res = aSelection->Collapse(mozDiv, 0);
-      if (NS_FAILED(res)) return res;
-    }
-*/}
+  }
   
   char nbspStr[2] = {nbsp, 0};
   PRBool bCancel;  
   nsString theString(*inString);  // copy instring for now
   if(aAction == kInsertTextIME) 
   { 
-	 // special case for IME. We need this to :
-	 // a) handle null strings, which are meaningful for IME
-	 // b) prevent the string from being broken into substrings,
-	 //    which can happen in non-IME processing below.
+     // special case for IME. We need this to :
+     // a) handle null strings, which are meaningful for IME
+     // b) prevent the string from being broken into substrings,
+     //    which can happen in non-IME processing below.
      // I should probably convert runs of spaces and tabs here as well
      res = DoTextInsertion(aSelection, &bCancel, &theString, typeInState);
   }
@@ -421,6 +412,7 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
         if (NS_FAILED(res)) return res;
         res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
       }
+#if 0
       // is it a solo space?
       else if (partialString == " ")
       {
@@ -435,6 +427,7 @@ nsHTMLEditRules::WillInsertText(PRInt32          aAction,
         if (NS_FAILED(res)) return res;
         res = DoTextInsertion(aSelection, &bCancel, outString, typeInState);
       }
+#endif
       // is it a solo return?
       else if (partialString == "\n")
       {
@@ -528,41 +521,6 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P
     
   if (!blockParent) return NS_ERROR_FAILURE;
   
-/*  // break action depends on type of block
-  PRBool bIsMozDiv = IsMozDiv(blockParent);
-  PRBool bIsNormalDiv = IsNormalDiv(blockParent);
-  
-  // body or normal div: insert a normal br
-  if (IsBody(blockParent) || bIsNormalDiv)
-  {
-    nsCOMPtr brNode;
-    res = mEditor->InsertBR(&brNode);  
-    if (NS_FAILED(res)) return res;
-    *aHandled = PR_TRUE;
-  }
-  else if (bIsMozDiv)
-  {
-    // split it
-    PRInt32 newOffset;
-    res = mEditor->SplitNodeDeep( blockParent, node, offset, &newOffset);
-    if (NS_FAILED(res)) return res;
-    nsCOMPtr parent;
-    blockParent->GetParentNode(getter_AddRefs(parent));
-    nsCOMPtr  rightDiv = nsEditor::GetChildAt(parent, newOffset);
-    if (!rightDiv || !IsMozDiv(rightDiv)) return NS_ERROR_FAILURE;
-    // put the trailer br at the end of the lefthand mozdiv
-    nsCOMPtr  leftDiv;
-    res = GetPriorHTMLSibling(rightDiv, &leftDiv);
-    if (NS_FAILED(res)) return res;
-    if (!leftDiv || !IsMozDiv(leftDiv)) return NS_ERROR_FAILURE;
-    res = AddTrailerBR(leftDiv);
-    if (NS_FAILED(res)) return res;
-    // put selection at beginning of righthand mozdiv
-    res = aSelection->Collapse(rightDiv, 0);
-    if (NS_FAILED(res)) return res;
-    *aHandled = PR_TRUE;
-  }
-*/  
   // headers: close (or split) header
   else if (IsHeader(blockParent))
   {
@@ -677,14 +635,14 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
               if (mEditor->NodesSameType(node, priorNode))
               {
                 // if so, join them!
-	            nsCOMPtr topParent;
-	            priorNode->GetParentNode(getter_AddRefs(topParent));
-	            *aHandled = PR_TRUE;
-	            res = JoinNodesSmart(priorNode,node,&selNode,&selOffset);
-	            if (NS_FAILED(res)) return res;
-	            // fix up selection
-	            res = aSelection->Collapse(selNode,selOffset);
-	            return res;
+                nsCOMPtr topParent;
+                priorNode->GetParentNode(getter_AddRefs(topParent));
+                *aHandled = PR_TRUE;
+                res = JoinNodesSmart(priorNode,node,&selNode,&selOffset);
+                if (NS_FAILED(res)) return res;
+                // fix up selection
+                res = aSelection->Collapse(selNode,selOffset);
+                return res;
               }
             }
           }
@@ -775,14 +733,14 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
               if (mEditor->NodesSameType(node, nextNode))
               {
                 // if so, join them!
-	            nsCOMPtr topParent;
-	            nextNode->GetParentNode(getter_AddRefs(topParent));
-	            *aHandled = PR_TRUE;
-	            res = JoinNodesSmart(nextNode,node,&selNode,&selOffset);
-	            if (NS_FAILED(res)) return res;
-	            // fix up selection
-	            res = aSelection->Collapse(selNode,selOffset);
-	            return res;
+                nsCOMPtr topParent;
+                nextNode->GetParentNode(getter_AddRefs(topParent));
+                *aHandled = PR_TRUE;
+                res = JoinNodesSmart(nextNode,node,&selNode,&selOffset);
+                if (NS_FAILED(res)) return res;
+                // fix up selection
+                res = aSelection->Collapse(selNode,selOffset);
+                return res;
               }
             }
           }
@@ -881,18 +839,18 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
           res = GetPriorHTMLNode(nodeToDelete, &brNode);
           if (IsBreak(brNode))
           {
-			// is brNode also a descendant of same block?
-			nsCOMPtr block, brBlock;
-			block = mEditor->GetBlockNodeParent(nodeToDelete);
-			brBlock = mEditor->GetBlockNodeParent(brNode);
-			if (block == brBlock)
-			{
-			  // delete both breaks
-			  res = mEditor->DeleteNode(brNode);
-			  if (NS_FAILED(res)) return res;
-			  res = mEditor->DeleteNode(nodeToDelete);
+            // is brNode also a descendant of same block?
+            nsCOMPtr block, brBlock;
+            block = mEditor->GetBlockNodeParent(nodeToDelete);
+            brBlock = mEditor->GetBlockNodeParent(brNode);
+            if (block == brBlock)
+            {
+              // delete both breaks
+              res = mEditor->DeleteNode(brNode);
+              if (NS_FAILED(res)) return res;
+              res = mEditor->DeleteNode(nodeToDelete);
               *aHandled = PR_TRUE;
-			  return res;
+              return res;
             }
             // else fall through
           }
@@ -901,8 +859,8 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection,
         // adjust selection to be right after it
         res = aSelection->Collapse(node, offset+1);
         if (NS_FAILED(res)) return res;
-		res = mEditor->DeleteNode(nodeToDelete);
-		*aHandled = PR_TRUE;
+        res = mEditor->DeleteNode(nodeToDelete);
+        *aHandled = PR_TRUE;
         return res;
       }
     }
@@ -2951,13 +2909,13 @@ nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection,
 //                       
 nsresult 
 nsHTMLEditRules::ReturnInParagraph(nsIDOMSelection *aSelection, 
-                                   nsIDOMNode *aHeader, 
+                                   nsIDOMNode *aPara, 
                                    nsIDOMNode *aNode, 
                                    PRInt32 aOffset,
                                    PRBool *aCancel,
                                    PRBool *aHandled)
 {
-  if (!aSelection || !aHeader || !aNode || !aCancel || !aHandled) 
+  if (!aSelection || !aPara || !aNode || !aCancel || !aHandled) 
     { return NS_ERROR_NULL_POINTER; }
   *aCancel = PR_FALSE;
   *aHandled = PR_FALSE;
@@ -2984,18 +2942,18 @@ nsHTMLEditRules::ReturnInParagraph(nsIDOMSelection *aSelection,
         // just fall out to default of inserting a BR
         return res;
       }
-      if (IsBreak(sibling))
+      if (IsBreak(sibling) && !HasMozAttr(sibling))
       {
         PRInt32 newOffset;
         *aCancel = PR_TRUE;
+        // split the paragraph
+        res = mEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
+        if (NS_FAILED(res)) return res;
         // get rid of the break
         res = mEditor->DeleteNode(sibling);  
         if (NS_FAILED(res)) return res;
-        // split the paragraph
-        res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
-        if (NS_FAILED(res)) return res;
-        // position selection inside textnode
-        res = aSelection->Collapse(aNode,0);
+        // position selection inside right hand para
+        res = aSelection->Collapse(aPara,0);
       }
       // else just fall out to default of inserting a BR
       return res;
@@ -3011,18 +2969,18 @@ nsHTMLEditRules::ReturnInParagraph(nsIDOMSelection *aSelection,
         // just fall out to default of inserting a BR
         return res;
       }
-      if (IsBreak(sibling))
+      if (IsBreak(sibling) && !HasMozAttr(sibling))
       {
         PRInt32 newOffset;
         *aCancel = PR_TRUE;
+        // split the paragraph
+        res = mEditor->SplitNodeDeep(aPara, aNode, aOffset, &newOffset);
+        if (NS_FAILED(res)) return res;
         // get rid of the break
         res = mEditor->DeleteNode(sibling);  
         if (NS_FAILED(res)) return res;
-        // split the paragraph
-        res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
-        if (NS_FAILED(res)) return res;
-        // position selection inside textnode
-        res = aSelection->Collapse(aNode,0);
+        // position selection inside right hand para
+        res = aSelection->Collapse(aPara,0);
       }
       // else just fall out to default of inserting a BR
       return res;
@@ -3031,9 +2989,36 @@ nsHTMLEditRules::ReturnInParagraph(nsIDOMSelection *aSelection,
     // just fall out to default of inserting a BR
     return res;
   }
-  
-  // not in a text node.  are we next to BR's?
-  // moose XXX 
+  else
+  {
+    // not in a text node.  
+    // is there a BR prior to it?
+    nsCOMPtr nearNode;
+    res = GetPriorHTMLNode(aNode, aOffset, &nearNode);
+    if (NS_FAILED(res)) return res;
+    if (!nearNode || !IsBreak(nearNode) || HasMozAttr(nearNode)) 
+    {
+      // is there a BR after to it?
+      res = GetNextHTMLNode(aNode, aOffset, &nearNode);
+      if (NS_FAILED(res)) return res;
+      if (!nearNode || !IsBreak(nearNode) || HasMozAttr(nearNode)) 
+      {
+        // just fall out to default of inserting a BR
+        return res;
+      }
+    }
+    // else remove sibling br and split para
+    PRInt32 newOffset;
+    *aCancel = PR_TRUE;
+    // split the paragraph
+    res = mEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
+    if (NS_FAILED(res)) return res;
+    // get rid of the break
+    res = mEditor->DeleteNode(nearNode);  
+    if (NS_FAILED(res)) return res;
+    // selection to beginning of right hand para
+    aSelection->Collapse(aPara,0);
+  }
   
   return res;
 }
@@ -3103,18 +3088,18 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection,
       // if we fail, then that means we need toinsert a break
       res = AdjustSelection(aSelection, nsIEditor::eNext);
       if (NS_FAILED(res)) return res;
-	  // get the selection location
-	  nsCOMPtr selNode;
-	  PRInt32 selOffset;
-	  res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
-	  if (NS_FAILED(res)) return res;
+      // get the selection location
+      nsCOMPtr selNode;
+      PRInt32 selOffset;
+      res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
+      if (NS_FAILED(res)) return res;
 
-	  // are we in a text node? 
-	  nsCOMPtr textNode = do_QueryInterface(selNode);
-	  if (!textNode) 
-	  {
-	    // time to insert a break
-	    nsCOMPtr brNode;
+      // are we in a text node? 
+      nsCOMPtr textNode = do_QueryInterface(selNode);
+      if (!textNode) 
+      {
+        // time to insert a break
+        nsCOMPtr brNode;
         res = CreateMozBR(selNode, selOffset, &brNode);
         if (NS_FAILED(res)) return res;
         res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset);
@@ -3131,18 +3116,6 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection,
   res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
   if (NS_FAILED(res)) return res;
   res = aSelection->Collapse(aListItem,0);
-#if 0
-  // insert a moz-br if new list item is empty
-  PRBool isEmptyNode;
-  nsCOMPtr brNode;
-  res = IsEmptyNode( aListItem, &isEmptyNode);
-  if (NS_FAILED(res)) return res;
-  if (isEmptyNode)
-  {
-    res = CreateMozBR(aListItem, 0, &brNode);
-    if (NS_FAILED(res)) return res;
-  }
-#endif
   return res;
 }
 
@@ -3666,7 +3639,7 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect
   if (!aSelection) return NS_ERROR_NULL_POINTER;
   
   // if the selection isn't collapsed, do nothing.
-  // moose: one thing to do instead ischeck for the case of
+  // moose: one thing to do instead is check for the case of
   // only a single break selected, and collapse it.  Good thing?  Beats me.
   PRBool bCollapsed;
   nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
@@ -3674,14 +3647,26 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect
   if (!bCollapsed) return res;
 
   // get the (collapsed) selection location
-  nsCOMPtr selNode;
+  nsCOMPtr selNode, temp;
   PRInt32 selOffset;
   res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
   if (NS_FAILED(res)) return res;
-
+  temp = selNode;
+  
+  // are we in an editable node?
+  while (!mEditor->IsEditable(selNode))
+  {
+    // scan up the tree until we find an editable place to be
+    res = nsEditor::GetNodeLocation(temp, &selNode, &selOffset);
+    if (NS_FAILED(res)) return res;
+    if (!selNode) return NS_ERROR_FAILURE;
+    temp = selNode;
+  }
+  
   // are we in a text node? 
   nsCOMPtr textNode = do_QueryInterface(selNode);
-  if (textNode) return NS_OK; // we LIKE it when we are in a text node.  that RULZ
+  if (textNode) 
+    return NS_OK; // we LIKE it when we are in a text node.  that RULZ
   
   // do we need to insert a special mozBR?  We do if we are:
   // 1) in a collapsed selection AND
@@ -3698,11 +3683,11 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect
     if (NS_FAILED(res)) return res;
     if (bIsLast)
     {
-	  // is nearNode also a descendant of same block?
-	  nsCOMPtr block, nearBlock;
-	  if (mEditor->IsBlockNode(selNode)) block = selNode;
-	  else block = mEditor->GetBlockNodeParent(selNode);
-	  nearBlock = mEditor->GetBlockNodeParent(nearNode);
+      // is nearNode also a descendant of same block?
+      nsCOMPtr block, nearBlock;
+      if (mEditor->IsBlockNode(selNode)) block = selNode;
+      else block = mEditor->GetBlockNodeParent(selNode);
+      nearBlock = mEditor->GetBlockNodeParent(nearNode);
       if (block == nearBlock)
       {
         // need to insert special moz BR. Why?  Because if we don't
@@ -3711,10 +3696,10 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect
         nsCOMPtr brNode;
         res = CreateMozBR(selNode, selOffset, &brNode);
         if (NS_FAILED(res)) return res;
-		res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset);
-		if (NS_FAILED(res)) return res;
-		res = aSelection->Collapse(selNode,selOffset+1);
-		if (NS_FAILED(res)) return res;
+        res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset);
+        if (NS_FAILED(res)) return res;
+        res = aSelection->Collapse(selNode,selOffset+1);
+        if (NS_FAILED(res)) return res;
       }
     }
     else
@@ -3723,16 +3708,37 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect
       // with a mozBR already exiting after it.  In this case we have to
       // move the selection to after the mozBR so it will show up on the
       // empty line.
-	  nsCOMPtr nextNode;
-	  res = GetNextHTMLNode(nearNode, &nextNode);
-	  if (NS_FAILED(res)) return res;
-	  if (IsMozBR(nextNode))
-	  {
-		res = nsEditor::GetNodeLocation(nextNode, &selNode, &selOffset);
-		if (NS_FAILED(res)) return res;
-		res = aSelection->Collapse(selNode,selOffset+1);
-		if (NS_FAILED(res)) return res;
-	  }
+      nsCOMPtr nextNode;
+      res = GetNextHTMLNode(nearNode, &nextNode);
+      if (NS_FAILED(res)) return res;
+      if (nextNode && IsMozBR(nextNode))
+      {
+        res = nsEditor::GetNodeLocation(nextNode, &selNode, &selOffset);
+        if (NS_FAILED(res)) return res;
+        res = aSelection->Collapse(selNode,selOffset+1);
+        if (NS_FAILED(res)) return res;
+      }
+      else
+      {
+        // alright, not that either.  But there's one more squirrel to feed:
+        // the br might be right in front of a new block (ie,:
+        //  text
  1. list item
) + // in this case we also need moz-br. + res = GetNextHTMLSibling(nearNode, &nextNode); + if (NS_FAILED(res)) return res; + if (nextNode && mEditor->IsBlockNode(nextNode)) + { + // need to insert special moz BR. Why? Because if we don't + // the user will see no new line for the break. + nsCOMPtr brNode; + res = CreateMozBR(selNode, selOffset, &brNode); + if (NS_FAILED(res)) return res; + res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(selNode,selOffset+1); + if (NS_FAILED(res)) return res; + } + } } } @@ -3755,6 +3761,7 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect dir = aAction; } // is the nearnode a text node? + if (!nearNode) return NS_OK; // couldn't find a near text node textNode = do_QueryInterface(nearNode); if (!textNode) return NS_ERROR_UNEXPECTED; PRInt32 offset = 0; @@ -3931,21 +3938,21 @@ nsHTMLEditRules::DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aS if (runStr != newStr) { // delete the original whitespace run - EditTxn *txn; + EditTxn *txn; // note 1: we are not telling edit listeners about these because they don't care // note 2: we are not wrapping these in a placeholder because we know they already are res = mEditor->CreateTxnForDeleteText(aTextNode, aStart+runStart, runEnd-runStart, (DeleteTextTxn**)&txn); if (NS_FAILED(res)) return res; - if (!txn) return NS_ERROR_OUT_OF_MEMORY; + if (!txn) return NS_ERROR_OUT_OF_MEMORY; res = mEditor->Do(txn); if (NS_FAILED(res)) return res; // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); // insert the new run - res = mEditor->CreateTxnForInsertText(newStr, aTextNode, aStart+runStart, (InsertTextTxn**)&txn); + res = mEditor->CreateTxnForInsertText(newStr, aTextNode, aStart+runStart, (InsertTextTxn**)&txn); if (NS_FAILED(res)) return res; - if (!txn) return NS_ERROR_OUT_OF_MEMORY; + if (!txn) return NS_ERROR_OUT_OF_MEMORY; res = mEditor->Do(txn); // The transaction system (if any) has taken ownwership of txns. NS_IF_RELEASE(txn); @@ -4078,36 +4085,68 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList) } -// not needed for now - leaving around in case we go back to it -#if 0 nsresult -nsHTMLEditRules::AddTrailerBR(nsIDOMNode *aNode) +nsHTMLEditRules::ConfirmSelectionInBody() { - // check parms - if (!aNode) return NS_ERROR_NULL_POINTER; - nsresult res = NS_OK; - // comfirm that this node is a type that we add trailer br's to: - // td, th, li - if (IsListItem(aNode)) // add more types later + nsCOMPtr bodyElement; + nsCOMPtr bodyNode; + + // get the body + res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_UNEXPECTED; + bodyNode = do_QueryInterface(bodyElement); + + // get the selection + nsCOMPtrselection; + res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + // get the selection start location + nsCOMPtr selNode, temp, parent; + PRInt32 selOffset; + res = mEditor->GetStartNodeAndOffset(selection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + temp = selNode; + + // check that selNode is inside body + while (temp && !IsBody(temp)) { - PRUint32 count; - nsCOMPtr brNode; - res = mEditor->GetLengthOfDOMNode(aNode, count); - if (NS_FAILED(res)) return res; - res = mEditor->CreateBR(aNode, count, &brNode); - if (NS_FAILED(res)) return res; - // give it special trailer attr - nsCOMPtr brElem = do_QueryInterface(brNode); - res = mEditor->SetAttribute(brElem, "type", "_moz_trailer"); + res = temp->GetParentNode(getter_AddRefs(parent)); + temp = parent; } - else + + // if we aren't in the body, force the issue + if (!temp) { - NS_NOTREACHED("editor error: nsHTMLEditRules::AddTrailerBR() called on bad node type"); +// uncomment this to see when we get bad selections +// NS_NOTREACHED("selection not in body"); + selection->Collapse(bodyNode,0); } - return res; + + // get the selection end location + res = mEditor->GetEndNodeAndOffset(selection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + temp = selNode; + + // check that selNode is inside body + while (temp && !IsBody(temp)) + { + res = temp->GetParentNode(getter_AddRefs(parent)); + temp = parent; + } + + // if we aren't in the body, force the issue + if (!temp) + { +// uncomment this to see when we get bad selections +// NS_NOTREACHED("selection not in body"); + selection->Collapse(bodyNode,0); + } + } -#endif + nsresult nsHTMLEditRules::UpdateDocChangeRange(nsIDOMRange *aRange) diff --git a/editor/libeditor/html/nsHTMLEditRules.h b/editor/libeditor/html/nsHTMLEditRules.h index 51aac544b995..d1f348e568eb 100644 --- a/editor/libeditor/html/nsHTMLEditRules.h +++ b/editor/libeditor/html/nsHTMLEditRules.h @@ -156,6 +156,7 @@ protected: nsresult UpdateDocChangeRange(nsIDOMRange *aRange); nsresult ConvertWhitespace(const nsString & inString, nsString & outString); + nsresult ConfirmSelectionInBody(); // removed from use: #if 0 diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp index 18f5ee596644..340c7486fd96 100644 --- a/editor/libeditor/html/nsHTMLEditor.cpp +++ b/editor/libeditor/html/nsHTMLEditor.cpp @@ -3639,43 +3639,48 @@ NS_IMETHODIMP nsHTMLEditor::PasteAsQuotation() NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsString& aCitation) { nsAutoEditBatch beginBatching(this); - nsCOMPtr newNode; - nsAutoString tag("blockquote"); - nsresult res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); - if (NS_FAILED(res)) return res; - if (!newNode) return NS_ERROR_NULL_POINTER; - // Try to set type=cite. Ignore it if this fails. - nsCOMPtr newElement (do_QueryInterface(newNode)); - if (newElement) - { - nsAutoString type ("type"); - nsAutoString cite ("cite"); - newElement->SetAttribute(type, cite); - } - - // Set the selection to the underneath the node we just inserted: + // get selection nsCOMPtr selection; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res) || !selection) - { -#ifdef DEBUG_akkana - printf("Can't get selection!"); -#endif - } + nsresult res = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - res = selection->Collapse(newNode, 0); - if (NS_FAILED(res)) + // give rules a chance to handle or cancel + nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement); + PRBool cancel, handled; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; + if (cancel) return NS_OK; // rules canceled the operation + if (!handled) { -#ifdef DEBUG_akkana - printf("Couldn't collapse"); -#endif - // XXX: error result: should res be returned here? - } + nsCOMPtr newNode; + nsAutoString tag("blockquote"); + res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode)); + if (NS_FAILED(res)) return res; + if (!newNode) return NS_ERROR_NULL_POINTER; - res = Paste(); + // Try to set type=cite. Ignore it if this fails. + nsCOMPtr newElement (do_QueryInterface(newNode)); + if (newElement) + { + nsAutoString type ("type"); + nsAutoString cite ("cite"); + newElement->SetAttribute(type, cite); + } + + // Set the selection to the underneath the node we just inserted: + res = selection->Collapse(newNode, 0); + if (NS_FAILED(res)) + { +#ifdef DEBUG_akkana + printf("Couldn't collapse"); +#endif + // XXX: error result: should res be returned here? + } + + res = Paste(); + } return res; } @@ -3800,42 +3805,53 @@ nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText, nsAutoEditBatch beginBatching(this); - // Wrap the inserted quote in a
 so it won't be wrapped:
-  nsCOMPtr preNode;
-  nsAutoString tag("pre");
-  rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
-
-  // If this succeeded, then set selection inside the pre
-  // so the inserted text will end up there.
-  // If it failed, we don't care what the return value was,
-  // but we'll fall through and try to insert the text anyway.
+  // get selection
   nsCOMPtr selection;
   rv = GetSelection(getter_AddRefs(selection));
-  if (NS_SUCCEEDED(rv) && preNode)
-  {
-    if (NS_SUCCEEDED(rv) && selection)
-      selection->Collapse(preNode, 0);
-  }
+  if (NS_FAILED(rv)) return rv;
+  if (!selection) return NS_ERROR_NULL_POINTER;
 
-  rv = InsertText(quotedStuff);
-  if (aNodeInserted)
+  // give rules a chance to handle or cancel
+  nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
+  PRBool cancel, handled;
+  rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  if (NS_FAILED(rv)) return rv;
+  if (cancel) return NS_OK; // rules canceled the operation
+  if (!handled)
   {
-    if (NS_SUCCEEDED(rv))
+    // Wrap the inserted quote in a 
 so it won't be wrapped:
+    nsCOMPtr preNode;
+    nsAutoString tag("pre");
+    rv = DeleteSelectionAndCreateNode(tag, getter_AddRefs(preNode));
+    
+    // If this succeeded, then set selection inside the pre
+    // so the inserted text will end up there.
+    // If it failed, we don't care what the return value was,
+    // but we'll fall through and try to insert the text anyway.
+    if (NS_SUCCEEDED(rv) && preNode)
     {
-      *aNodeInserted = preNode;
-      NS_IF_ADDREF(*aNodeInserted);
+      selection->Collapse(preNode, 0);
+    }
+
+    rv = InsertText(quotedStuff);
+    if (aNodeInserted)
+    {
+      if (NS_SUCCEEDED(rv))
+      {
+        *aNodeInserted = preNode;
+        NS_IF_ADDREF(*aNodeInserted);
+      }
+    }
+
+    // Set the selection to just after the inserted node:
+    if (NS_SUCCEEDED(rv) && preNode)
+    {
+      nsCOMPtr parent;
+      PRInt32 offset;
+      if (NS_SUCCEEDED(GetNodeLocation(preNode, &parent, &offset)) && parent)
+        selection->Collapse(parent, offset+1);
     }
   }
-
-  // Set the selection to just after the inserted node:
-  if (NS_SUCCEEDED(rv) && preNode)
-  {
-    nsCOMPtr parent;
-    PRInt32 offset;
-    if (NS_SUCCEEDED(GetNodeLocation(preNode, &parent, &offset)) && parent)
-      selection->Collapse(parent, offset+1);
-  }
-
   return rv;
 }
 
@@ -3848,48 +3864,61 @@ nsHTMLEditor::InsertAsCitedQuotation(const nsString& aQuotedText,
   nsAutoEditBatch beginBatching(this);
   nsCOMPtr newNode;
   nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
-  nsAutoString tag("blockquote");
-  nsresult res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
-  if (NS_FAILED(res)) return res;
-  if (!newNode) return NS_ERROR_NULL_POINTER;
 
-  // Try to set type=cite.  Ignore it if this fails.
-  nsCOMPtr newElement (do_QueryInterface(newNode));
+  // get selection
   nsCOMPtr selection;
-  res = GetSelection(getter_AddRefs(selection));
-  if (newElement)
+  nsresult res = GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res)) return res;
+  if (!selection) return NS_ERROR_NULL_POINTER;
+
+  // give rules a chance to handle or cancel
+  nsTextRulesInfo ruleInfo(nsHTMLEditRules::kInsertElement);
+  PRBool cancel, handled;
+  res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  if (NS_FAILED(res)) return res;
+  if (cancel) return NS_OK; // rules canceled the operation
+  if (!handled)
   {
-    nsAutoString type ("type");
-    nsAutoString cite ("cite");
-    newElement->SetAttribute(type, cite);
+    nsAutoString tag("blockquote");
+    res = DeleteSelectionAndCreateNode(tag, getter_AddRefs(newNode));
+    if (NS_FAILED(res)) return res;
+    if (!newNode) return NS_ERROR_NULL_POINTER;
 
-    if (aCitation.Length() > 0)
-      newElement->SetAttribute(cite, aCitation);
-
-    // Set the selection inside the blockquote so aQuotedText will go there:
-    if (NS_SUCCEEDED(res) && selection)
-      selection->Collapse(newNode, 0);
-  }
-
-  res = InsertHTMLWithCharset(aQuotedText, aCharset);
-  if (aNodeInserted)
-  {
-    if (NS_SUCCEEDED(res))
+    // Try to set type=cite.  Ignore it if this fails.
+    nsCOMPtr newElement (do_QueryInterface(newNode));
+    if (newElement)
     {
-      *aNodeInserted = newNode;
-      NS_IF_ADDREF(*aNodeInserted);
+      nsAutoString type ("type");
+      nsAutoString cite ("cite");
+      newElement->SetAttribute(type, cite);
+
+      if (aCitation.Length() > 0)
+        newElement->SetAttribute(cite, aCitation);
+
+      // Set the selection inside the blockquote so aQuotedText will go there:
+      if (NS_SUCCEEDED(res))
+        selection->Collapse(newNode, 0);
+    }
+
+    res = InsertHTMLWithCharset(aQuotedText, aCharset);
+    if (aNodeInserted)
+    {
+      if (NS_SUCCEEDED(res))
+      {
+        *aNodeInserted = newNode;
+        NS_IF_ADDREF(*aNodeInserted);
+      }
+    }
+
+    // Set the selection to just after the inserted node:
+    if (NS_SUCCEEDED(res) && newNode)
+    {
+      nsCOMPtr parent;
+      PRInt32 offset;
+      if (NS_SUCCEEDED(GetNodeLocation(newNode, &parent, &offset)) && parent)
+        selection->Collapse(parent, offset+1);
     }
   }
-
-  // Set the selection to just after the inserted node:
-  if (NS_SUCCEEDED(res) && newNode)
-  {
-    nsCOMPtr parent;
-    PRInt32 offset;
-    if (NS_SUCCEEDED(GetNodeLocation(newNode, &parent, &offset)) && parent)
-      selection->Collapse(parent, offset+1);
-  }
-
   return res;
 }
 
@@ -4228,6 +4257,17 @@ nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
 }
 
 
+PRBool 
+nsHTMLEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aTag)
+{
+  // CNavDTD gives some unwanted results.  We override them here.
+  // if parent is a list and tag is text, say "no". 
+  if (IsListNode(aParent) && (aTag == "__moz_text"))
+    return PR_FALSE;
+  // else fall thru
+  return nsEditor::CanContainTag(aParent, aTag);
+}
+
 
 #ifdef XP_MAC
 #pragma mark -
@@ -5266,6 +5306,8 @@ nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement)
           node = firstChild;
         }
       }
+# if 0 
+// I've ifdef'd this out because it isn't finished and I'm not sure what the intent is.
       PRInt32 offset = 0;
       nsCOMPtrlastChild;
       res = parent->GetLastChild(getter_AddRefs(lastChild));
@@ -5291,13 +5333,14 @@ nsHTMLEditor::SetCaretInTableCell(nsIDOMElement* aElement)
           // We have > 1 node, so set to end of content
         }
       }
+#endif
       // Set selection at beginning of deepest node
       // Should we set 
       nsCOMPtr selection;
       res = GetSelection(getter_AddRefs(selection));
-      if (NS_SUCCEEDED(res) && selection)
+      if (NS_SUCCEEDED(res) && selection && firstChild)
       {
-        res = selection->Collapse(parent, offset);
+        res = selection->Collapse(firstChild, 0);
         if (NS_SUCCEEDED(res))
           caretIsSet = PR_TRUE;
       }
diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h
index bdfbd5ddd860..da9f8d795227 100644
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -244,6 +244,8 @@ public:
   /* ------------ nsICSSLoaderObserver -------------- */
   NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
 
+  /* ------------ nsEditor overrides ---------------- */
+
   /** All editor operations which alter the doc should be prefaced
    *  with a call to StartOperation, naming the action and direction */
   NS_IMETHOD StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection);
@@ -252,6 +254,9 @@ public:
    *  with a call to EndOperation, naming the action and direction */
   NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection);
 
+  /** returns PR_TRUE if aParent can contain a child of type aTag */
+  PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);
+  
   /* ------------ Utility Routines, not part of public API -------------- */
   NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext);
 
diff --git a/editor/libeditor/text/nsTextEditRules.cpp b/editor/libeditor/text/nsTextEditRules.cpp
index bb82d30cf985..b2fd0be9d622 100644
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -299,7 +299,7 @@ nsTextEditRules::WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel)
   // get the (collapsed) selection location
   res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
   if (NS_FAILED(res)) return res;
-  // get next node
+  // get prior node
   res = GetPriorHTMLNode(selNode, selOffset, &priorNode);
   if (NS_SUCCEEDED(res) && priorNode && IsMozBR(priorNode))    
   {
@@ -1014,8 +1014,6 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection,
   PRBool isCollapsed;
   aSelection->GetIsCollapsed(&isCollapsed);
   NS_ASSERTION(PR_TRUE==isCollapsed, "selection not collapsed after delete selection.");
-  // if the delete selection resulted in no content 
-  // insert a special bogus text node with a   character in it.
   if (NS_SUCCEEDED(res)) // only do this work if DeleteSelection completed successfully
   {
     // if we don't have an empty document, check the selection to see if any collapsing is necessary
@@ -1047,7 +1045,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection,
       {
         nsCOMPtrselectedNodeAsText;
         selectedNodeAsText = do_QueryInterface(selectedNode);
-        if (selectedNodeAsText)
+        if (selectedNodeAsText && mEditor->IsEditable(selectedNode))
         {
           nsCOMPtr siblingNode;
           selectedNode->GetPreviousSibling(getter_AddRefs(siblingNode));
@@ -1055,7 +1053,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection,
           {
             nsCOMPtrsiblingNodeAsText;
             siblingNodeAsText = do_QueryInterface(siblingNode);
-            if (siblingNodeAsText)
+            if (siblingNodeAsText && mEditor->IsEditable(siblingNode))
             {
               PRUint32 siblingLength; // the length of siblingNode before the join
               siblingNodeAsText->GetLength(&siblingLength);
@@ -1072,7 +1070,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection,
           {
             nsCOMPtrsiblingNodeAsText;
             siblingNodeAsText = do_QueryInterface(siblingNode);
-            if (siblingNodeAsText)
+            if (siblingNodeAsText && mEditor->IsEditable(siblingNode))
             {
               PRUint32 selectedNodeLength; // the length of siblingNode before the join
               selectedNodeAsText->GetLength(&selectedNodeLength);