From c64143d01228d2b8ca2bbc5b4ecbcfad783ed2be Mon Sep 17 00:00:00 2001 From: "jfrancis%netscape.com" Date: Mon, 22 Oct 2001 06:22:52 +0000 Subject: [PATCH] fixes following bugs: 77902: toggling from normal to source view and back in composer can leave source in normal view. 101645: big/small tags get seperately wrapped around br nodes 81315: drag and drop doesn't behave same as copy paste 90759: ascii spaces don't behave in IME mode 96328: cant outdent certain indented text 58629: some mail messages cannot be accurately quoted on reply 93088/93477: forward deletion broken 46290: relative font size controls dont play nice with absolute font size r=various; sr=kin git-svn-id: svn://10.0.0.236/trunk@105922 18797224-902f-48f8-a5cc-f745e15eee43 --- .../libeditor/html/nsHTMLDataTransfer.cpp | 169 ++++++++++++++++-- .../editor/libeditor/html/nsHTMLEditRules.cpp | 109 +++++------ .../editor/libeditor/html/nsHTMLEditRules.h | 4 + .../editor/libeditor/html/nsHTMLEditor.cpp | 2 +- mozilla/editor/libeditor/html/nsHTMLEditor.h | 8 +- .../libeditor/html/nsHTMLEditorStyle.cpp | 156 ++++++++++++++-- .../editor/libeditor/html/nsWSRunObject.cpp | 48 +++-- 7 files changed, 391 insertions(+), 105 deletions(-) diff --git a/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp b/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp index 0bc6cf9b54e..37949c89e9b 100644 --- a/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp @@ -176,6 +176,69 @@ static nsCOMPtr GetTableParent(nsIDOMNode* aNode) } +NS_IMETHODIMP nsHTMLEditor::LoadHTML(const nsAReadableString & aInString) +{ + nsAutoString charset; + return LoadHTMLWithCharset(aInString, charset); +} + +NS_IMETHODIMP nsHTMLEditor::LoadHTMLWithCharset(const nsAReadableString & aInputString, const nsAReadableString & aCharset) +{ + nsresult res = NS_OK; + if (!mRules) return NS_ERROR_NOT_INITIALIZED; + + // force IME commit; set up rules sniffing and batching + ForceCompositionEnd(); + nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpHTMLLoad, nsIEditor::eNext); + + // Get selection + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + // Delete Selection + res = DeleteSelection(eNone); + if (NS_FAILED(res)) return res; + + // Get the first range in the selection, for context: + nsCOMPtr range, clone; + res = selection->GetRangeAt(0, getter_AddRefs(range)); + NS_ENSURE_SUCCESS(res, res); + if (!range) + return NS_ERROR_NULL_POINTER; + nsCOMPtr nsrange (do_QueryInterface(range)); + if (!nsrange) + return NS_ERROR_NO_INTERFACE; + + // create fragment for pasted html + nsCOMPtr docfrag; + { + res = nsrange->CreateContextualFragment(aInputString, getter_AddRefs(docfrag)); + NS_ENSURE_SUCCESS(res, res); + } + // put the fragment into the document + nsCOMPtr parent, junk; + res = range->GetStartContainer(getter_AddRefs(parent)); + NS_ENSURE_SUCCESS(res, res); + if (!parent) + return NS_ERROR_NULL_POINTER; + PRInt32 childOffset; + res = range->GetStartOffset(&childOffset); + NS_ENSURE_SUCCESS(res, res); + + nsCOMPtr nodeToInsert; + docfrag->GetFirstChild(getter_AddRefs(nodeToInsert)); + while (nodeToInsert) + { + res = InsertNode(nodeToInsert, parent, childOffset++); + NS_ENSURE_SUCCESS(res, res); + docfrag->GetFirstChild(getter_AddRefs(nodeToInsert)); + } + return res; +} + + NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsAReadableString & aInString) { nsAutoString charset; @@ -761,6 +824,44 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) if (NS_FAILED(rv)) return rv; if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail? + // get additional html copy hints, if present + nsAutoString contextStr, infoStr; + nsCOMPtr contextDataObj, infoDataObj; + PRUint32 contextLen, infoLen; + nsCOMPtr textDataObj; + + nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER); + contextTrans->AddDataFlavor(kHTMLContext); + dragSession->GetData(contextTrans, i); + contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen); + + nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER); + infoTrans->AddDataFlavor(kHTMLInfo); + dragSession->GetData(infoTrans, i); + infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen); + + if (contextDataObj) + { + PRUnichar* text = nsnull; + textDataObj = do_QueryInterface(contextDataObj); + textDataObj->ToString ( &text ); + contextStr.Assign ( text, contextLen / 2 ); + if (text) + nsMemory::Free(text); + } + + if (infoDataObj) + { + PRUnichar* text = nsnull; + textDataObj = do_QueryInterface(infoDataObj); + textDataObj->ToString ( &text ); + infoStr.Assign ( text, infoLen / 2 ); + if (text) + nsMemory::Free(text); + } + if ( doPlaceCaret ) { // check if the user pressed the key to force a copy rather than a move @@ -898,7 +999,7 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromDrop(nsIDOMEvent* aDropEvent) doPlaceCaret = PR_FALSE; } - rv = InsertFromTransferable(trans, nsAutoString(), nsAutoString()); + rv = InsertFromTransferable(trans, contextStr, infoStr); } return rv; @@ -1022,16 +1123,28 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) if (NS_FAILED(rv)) return rv; // grab a string - nsAutoString buffer; - rv = docEncoder->EncodeToString(buffer); - if (NS_FAILED(rv)) return rv; + nsAutoString buffer, parents, info; + + if (!bIsPlainTextControl) + { + // encode the selection as html with contextual info + rv = docEncoder->EncodeToStringWithContext(buffer, parents, info); + if (NS_FAILED(rv)) return rv; + } + else + { + // encode the selection + rv = docEncoder->EncodeToString(buffer); + if (NS_FAILED(rv)) return rv; + } // if we have an empty string, we're done; otherwise continue if ( !buffer.IsEmpty() ) { - nsCOMPtr dataWrapper = do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID); - NS_ENSURE_TRUE(dataWrapper, NS_ERROR_FAILURE); + nsCOMPtr dataWrapper, contextWrapper, infoWrapper; + dataWrapper = do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID); + NS_ENSURE_TRUE(dataWrapper, NS_ERROR_FAILURE); rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.get()) ); if (NS_FAILED(rv)) return rv; @@ -1040,9 +1153,23 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) // Add the unicode flavor to the transferable rv = trans->AddDataFlavor(kUnicodeMime); if (NS_FAILED(rv)) return rv; + + // QI the data object an |nsISupports| so that when the transferable holds + // onto it, it will addref the correct interface. + nsCOMPtr genericDataObj ( do_QueryInterface(dataWrapper) ); + rv = trans->SetTransferData(kUnicodeMime, genericDataObj, buffer.Length() * 2); + if (NS_FAILED(rv)) return rv; } else { + contextWrapper = do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID); + NS_ENSURE_TRUE(contextWrapper, NS_ERROR_FAILURE); + infoWrapper = do_CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID); + NS_ENSURE_TRUE(infoWrapper, NS_ERROR_FAILURE); + + contextWrapper->SetData ( NS_CONST_CAST(PRUnichar*,parents.get()) ); + infoWrapper->SetData ( NS_CONST_CAST(PRUnichar*,info.get()) ); + rv = trans->AddDataFlavor(kHTMLMime); if (NS_FAILED(rv)) return rv; @@ -1051,14 +1178,26 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) rv = trans->SetConverter(htmlConverter); if (NS_FAILED(rv)) return rv; - } - // QI the data object an |nsISupports| so that when the transferable holds - // onto it, it will addref the correct interface. - nsCOMPtr nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) ); - rv = trans->SetTransferData(bIsPlainTextControl ? kUnicodeMime : kHTMLMime, - nsisupportsDataWrapper, buffer.Length() * 2); - if (NS_FAILED(rv)) return rv; + nsCOMPtr genericDataObj ( do_QueryInterface(dataWrapper) ); + rv = trans->SetTransferData(kHTMLMime, genericDataObj, buffer.Length() * 2); + if (NS_FAILED(rv)) return rv; + + if (parents.Length()) + { + // Add the htmlcontext DataFlavor to the transferable + trans->AddDataFlavor(kHTMLContext); + genericDataObj = do_QueryInterface(contextWrapper); + trans->SetTransferData(kHTMLContext, genericDataObj, parents.Length()*2); + } + if (info.Length()) + { + // Add the htmlinfo DataFlavor to the transferable + trans->AddDataFlavor(kHTMLInfo); + genericDataObj = do_QueryInterface(infoWrapper); + trans->SetTransferData(kHTMLInfo, genericDataObj, info.Length()*2); + } + } /* add the transferable to the array */ rv = transferableArray->AppendElement(trans); @@ -1067,7 +1206,7 @@ NS_IMETHODIMP nsHTMLEditor::DoDrag(nsIDOMEvent *aDragEvent) /* invoke drag */ unsigned int flags; // in some cases we'll want to cut rather than copy... hmmmmm... - flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE; + flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE; rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags); if (NS_FAILED(rv)) return rv; @@ -1481,7 +1620,7 @@ nsHTMLEditor::InsertAsCitedQuotation(const nsAReadableString & aQuotedText, } if (aInsertHTML) - res = InsertHTMLWithCharset(aQuotedText, aCharset); + res = LoadHTMLWithCharset(aQuotedText, aCharset); else res = InsertText(aQuotedText); // XXX ignore charset diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp index 80dfaceb2bf..bf767629bf4 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp @@ -403,7 +403,9 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection // (action == nsEditor::kOpInsertIMEText) || (action == nsHTMLEditor::kOpInsertElement) || (action == nsHTMLEditor::kOpInsertQuotation) || - (action == nsEditor::kOpInsertNode)) + (action == nsEditor::kOpInsertNode) || + (action == nsHTMLEditor::kOpHTMLPaste || + (action == nsHTMLEditor::kOpHTMLLoad))) { res = ReplaceNewlines(mDocChangeRange); } @@ -431,7 +433,8 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection (action == nsEditor::kOpInsertIMEText) || (action == nsEditor::kOpDeleteSelection) || (action == nsEditor::kOpInsertBreak) || - (action == nsHTMLEditor::kOpHTMLPaste)) + (action == nsHTMLEditor::kOpHTMLPaste || + (action == nsHTMLEditor::kOpHTMLLoad))) { res = AdjustSelection(selection, aDirection); if (NS_FAILED(res)) return res; @@ -760,9 +763,18 @@ nsHTMLEditRules::GetIndentState(PRBool *aCanIndent, PRBool *aCanOutdent) return NS_ERROR_FAILURE; *aCanIndent = PR_TRUE; *aCanOutdent = PR_FALSE; - + + // get selection + nsCOMPtrselection; + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + nsCOMPtr selPriv(do_QueryInterface(selection)); + if (!selPriv) + return NS_ERROR_FAILURE; + + // contruct a list of nodes to act on. nsCOMPtr arrayOfNodes; - nsresult res = GetListActionNodes(address_of(arrayOfNodes), PR_FALSE, PR_TRUE); + res = GetNodesFromSelection(selection, kIndent, address_of(arrayOfNodes), PR_TRUE); if (NS_FAILED(res)) return res; // examine nodes in selection for blockquotes or list elements; @@ -1082,7 +1094,8 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (aAction == kInsertTextIME) { - res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc); + nsWSRunObject wsObj(mHTMLEditor, selNode, selOffset); + res = wsObj.InsertText(*inString, address_of(selNode), &selOffset, doc); if (NS_FAILED(res)) return res; } else // aAction == kInsertText @@ -1183,7 +1196,6 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, // is it a tab? if (subStr.Equals(tabStr)) { -// res = mHTMLEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc); res = wsObj.InsertText(tabString, address_of(curNode), &curOffset, doc); if (NS_FAILED(res)) return res; pos++; @@ -1191,14 +1203,12 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, // is it a return? else if (subStr.Equals(newlineStr)) { -// res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone); res = wsObj.InsertBreak(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone); if (NS_FAILED(res)) return res; pos++; } else { -// res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc); res = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc); if (NS_FAILED(res)) return res; } @@ -2513,14 +2523,10 @@ nsHTMLEditRules::WillMakeBasicBlock(nsISelection *aSelection, nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); *aHandled = PR_TRUE; - nsCOMPtr arrayOfRanges; - res = GetPromotedRanges(aSelection, address_of(arrayOfRanges), kMakeBasicBlock); - if (NS_FAILED(res)) return res; - - // use these ranges to contruct a list of nodes to act on. + // contruct a list of nodes to act on. nsCOMPtr arrayOfNodes; - res = GetNodesForOperation(arrayOfRanges, address_of(arrayOfNodes), kMakeBasicBlock); - if (NS_FAILED(res)) return res; + res = GetNodesFromSelection(aSelection, kMakeBasicBlock, address_of(arrayOfNodes)); + if (NS_FAILED(res)) return res; nsString tString(*aBlockType); @@ -2702,13 +2708,8 @@ nsHTMLEditRules::WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool * // this basically just expands the range to include the immediate // block parent, and then further expands to include any ancestors // whose children are all in the range - - res = GetPromotedRanges(aSelection, address_of(arrayOfRanges), kIndent); + res = GetNodesFromSelection(aSelection, kIndent, address_of(arrayOfNodes)); if (NS_FAILED(res)) return res; - - // use these ranges to contruct a list of nodes to act on. - res = GetNodesForOperation(arrayOfRanges, address_of(arrayOfNodes), kIndent); - if (NS_FAILED(res)) return res; } // if nothing visible in list, make an empty block @@ -2847,17 +2848,10 @@ nsHTMLEditRules::WillOutdent(nsISelection *aSelection, PRBool *aCancel, PRBool * // this basically just expands the range to include the immediate // block parent, and then further expands to include any ancestors // whose children are all in the range - - nsCOMPtr arrayOfRanges; - res = GetPromotedRanges(aSelection, address_of(arrayOfRanges), kOutdent); - 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; - + res = GetNodesFromSelection(aSelection, kOutdent, address_of(arrayOfNodes)); + if (NS_FAILED(res)) return res; + // Ok, now go through all the nodes and remove a level of blockquoting, // or whatever is appropriate. Wohoo! @@ -3284,15 +3278,9 @@ nsHTMLEditRules::WillAlign(nsISelection *aSelection, // block parent, and then further expands to include any ancestors // whose children are all in the range *aHandled = PR_TRUE; - - nsCOMPtr arrayOfRanges; - res = GetPromotedRanges(aSelection, address_of(arrayOfRanges), kAlign); - 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), kAlign); - if (NS_FAILED(res)) return res; + res = GetNodesFromSelection(aSelection, kAlign, address_of(arrayOfNodes)); + if (NS_FAILED(res)) return res; // if we don't have any nodes, or we have only a single br, then we are // creating an empty alignment div. We have to do some different things for these. @@ -4432,12 +4420,8 @@ nsHTMLEditRules::GetListActionNodes(nsCOMPtr *outArrayOfNodes, } } - nsCOMPtr arrayOfRanges; - res = GetPromotedRanges(selection, address_of(arrayOfRanges), kMakeList); - if (NS_FAILED(res)) return res; - - // use these ranges to contruct a list of nodes to act on. - res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeList, aDontTouchContent); + // contruct a list of nodes to act on. + res = GetNodesFromSelection(selection, kMakeList, outArrayOfNodes, aDontTouchContent); if (NS_FAILED(res)) return res; // pre process our list of nodes... @@ -4564,14 +4548,10 @@ nsHTMLEditRules::GetParagraphFormatNodes(nsCOMPtr *outArrayOfN nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; - nsCOMPtr arrayOfRanges; - res = GetPromotedRanges(selection, address_of(arrayOfRanges), kMakeList); + // contruct a list of nodes to act on. + res = GetNodesFromSelection(selection, kMakeBasicBlock, outArrayOfNodes, aDontTouchContent); if (NS_FAILED(res)) return res; - - // use these ranges to contruct a list of nodes to act on. - res = GetNodesForOperation(arrayOfRanges, outArrayOfNodes, kMakeBasicBlock, aDontTouchContent); - if (NS_FAILED(res)) return res; - + // pre process our list of nodes... PRUint32 listCount; PRInt32 i; @@ -4746,6 +4726,31 @@ nsHTMLEditRules::GetHighestInlineParent(nsIDOMNode* aNode) return inlineNode; } + +/////////////////////////////////////////////////////////////////////////// +// GetNodesFromSelection: given a particular operation, construct a list +// of nodes from the selection that will be operated on. +// +nsresult +nsHTMLEditRules::GetNodesFromSelection(nsISelection *selection, + PRInt32 operation, + nsCOMPtr *arrayOfNodes, + PRBool dontTouchContent) +{ + if (!selection || !arrayOfNodes) return NS_ERROR_NULL_POINTER; + nsresult res; + + // promote selection ranges + nsCOMPtr arrayOfRanges; + res = GetPromotedRanges(selection, address_of(arrayOfRanges), operation); + if (NS_FAILED(res)) return res; + + // use these ranges to contruct a list of nodes to act on. + res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, operation, dontTouchContent); + return res; +} + + /////////////////////////////////////////////////////////////////////////// // MakeTransitionList: detect all the transitions in the array, where a // transition means that adjacent nodes in the array diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.h b/mozilla/editor/libeditor/html/nsHTMLEditRules.h index 9627d53ad62..2d364aa2f52 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.h @@ -171,6 +171,10 @@ protected: PRBool aDontTouchContent=PR_FALSE); nsresult GetChildNodesForOperation(nsIDOMNode *inNode, nsCOMPtr *outArrayOfNodes); + nsresult GetNodesFromSelection(nsISelection *selection, + PRInt32 operation, + nsCOMPtr *arrayOfNodes, + PRBool aDontTouchContent=PR_FALSE); 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); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index 1cf848b21bb..3e5a60d4a3e 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -1684,7 +1684,7 @@ nsHTMLEditor::RebuildDocumentFromSource(const nsAReadableString& aSourceString) nsReadingIterator endtotal; aSourceString.EndReading(endtotal); - res = InsertHTML(Substring(beginbody,endtotal)); + res = LoadHTML(Substring(beginbody,endtotal)); if (NS_FAILED(res)) return res; selection->Collapse(bodyElement, 0); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index 0c3e5703452..65aef6919c3 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -95,7 +95,8 @@ public: kOpInsertQuotation = 3009, kOpSetTextProperty = 3010, kOpRemoveTextProperty = 3011, - kOpHTMLPaste = 3012 + kOpHTMLPaste = 3012, + kOpHTMLLoad = 3013 }; @@ -140,6 +141,9 @@ public: NS_IMETHOD InsertHTML(const nsAReadableString &aInputString); NS_IMETHOD InsertHTMLWithCharset(const nsAReadableString& aInputString, const nsAReadableString& aCharset); + NS_IMETHOD LoadHTML(const nsAReadableString &aInputString); + NS_IMETHOD LoadHTMLWithCharset(const nsAReadableString& aInputString, + const nsAReadableString& aCharset); NS_IMETHOD RebuildDocumentFromSource(const nsAReadableString& aSourceString); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); @@ -621,6 +625,8 @@ protected: PRInt32 aEndOffset); nsresult RelativeFontChangeOnNode( PRInt32 aSizeChange, nsIDOMNode *aNode); + nsresult RelativeFontChangeHelper( PRInt32 aSizeChange, + nsIDOMNode *aNode); /* helper routines for inline style */ nsresult SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, diff --git a/mozilla/editor/libeditor/html/nsHTMLEditorStyle.cpp b/mozilla/editor/libeditor/html/nsHTMLEditorStyle.cpp index 944ec5d7139..9ebcf088570 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditorStyle.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditorStyle.cpp @@ -336,6 +336,28 @@ nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, if (NS_FAILED(res)) return res; } + // look for siblings that are correct type of node + nsCOMPtr sibling; + GetPriorHTMLSibling(node, address_of(sibling)); + if (sibling && NodeIsType(sibling, aProperty) && + HasAttrVal(sibling, aAttribute, aValue) && + IsOnlyAttribute(sibling, aAttribute) ) + { + // previous sib is already right kind of inline node; slide this over into it + res = MoveNode(node, sibling, -1); + return res; + } + sibling = nsnull; + GetNextHTMLSibling(node, address_of(sibling)); + if (sibling && NodeIsType(sibling, aProperty) && + HasAttrVal(sibling, aAttribute, aValue) && + IsOnlyAttribute(sibling, aAttribute) ) + { + // following sib is already right kind of inline node; slide this over into it + res = MoveNode(node, sibling, 0); + return res; + } + // reparent the node inside inline node with appropriate {attribute,value} res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); return res; @@ -558,9 +580,9 @@ nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, // then process the node itself if ( !aChildrenOnly && - ((aProperty && NodeIsType(aNode, aProperty)) || // node is prop we asked for + (aProperty && NodeIsType(aNode, aProperty) || // node is prop we asked for (aProperty == nsIEditProperty::href && nsHTMLEditUtils::IsLink(aNode))) || // but check for link ( tmp, node = do_QueryInterface(aTextNode); - + // do we need to split the text node? PRUint32 textLen; aTextNode->GetLength(&textLen); @@ -1427,12 +1456,98 @@ nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, if (NS_FAILED(res)) return res; } - // reparent the node inside font node with appropriate relative size + // look for siblings that are correct type of node + nsCOMPtr sibling; + GetPriorHTMLSibling(node, address_of(sibling)); + if (sibling && NodeIsType(sibling, NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small"))) + { + // previous sib is already right kind of inline node; slide this over into it + res = MoveNode(node, sibling, -1); + return res; + } + sibling = nsnull; + GetNextHTMLSibling(node, address_of(sibling)); + if (sibling && NodeIsType(sibling, NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small"))) + { + // following sib is already right kind of inline node; slide this over into it + res = MoveNode(node, sibling, 0); + return res; + } + + // else reparent the node inside font node with appropriate relative size res = InsertContainerAbove(node, address_of(tmp), NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small")); return res; } +nsresult +nsHTMLEditor::RelativeFontChangeHelper( PRInt32 aSizeChange, + nsIDOMNode *aNode) +{ + /* This routine looks for all the font nodes in the tree rooted by aNode, + including aNode itself, looking for font nodes that have the size attr + set. Any such nodes need to have big or small put inside them, since + they override any big/small that are above them. + */ + + // Can only change font size by + or - 1 + if ( !( (aSizeChange==1) || (aSizeChange==-1) ) ) + return NS_ERROR_ILLEGAL_VALUE; + if (!aNode) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK; + nsAutoString tag; + if (aSizeChange == 1) tag.AssignWithConversion("big"); + else tag.AssignWithConversion("small"); + nsCOMPtr childNodes; + PRInt32 j; + PRUint32 childCount; + nsCOMPtr childNode; + nsAutoString attr; + attr.AssignWithConversion("size"); + + // if this is a font node with size, put big/small inside it + if (NodeIsType(aNode, nsIEditProperty::font) && HasAttr(aNode, &attr)) + { + // cycle through children and adjust relative font size + res = aNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + if (childNodes) + { + childNodes->GetLength(&childCount); + for (j=childCount-1; j>=0; j--) + { + res = childNodes->Item(j, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = RelativeFontChangeOnNode(aSizeChange, childNode); + if (NS_FAILED(res)) return res; + } + } + } + } + + childNodes = nsnull; + // now cycle through the children. + res = aNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + if (childNodes) + { + childNodes->GetLength(&childCount); + for (j=childCount-1; j>=0; j--) + { + res = childNodes->Item(j, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode)) + { + res = RelativeFontChangeHelper(aSizeChange, childNode); + if (NS_FAILED(res)) return res; + } + } + } + return res; +} + + nsresult nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, nsIDOMNode *aNode) @@ -1448,16 +1563,13 @@ nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, if (aSizeChange == 1) tag.Assign(NS_LITERAL_STRING("big")); else tag.Assign(NS_LITERAL_STRING("small")); - // is this node a text node? - if (IsTextNode(aNode)) - { - res = InsertContainerAbove(aNode, address_of(tmp), tag); - return res; - } // is it the opposite of what we want? if ( ((aSizeChange == 1) && nsHTMLEditUtils::IsSmall(aNode)) || ((aSizeChange == -1) && nsHTMLEditUtils::IsBig(aNode)) ) { + // first populate any nested font tags that have the size attr set + res = RelativeFontChangeHelper(aSizeChange, aNode); + if (NS_FAILED(res)) return res; // in that case, just remove this node and pull up the children res = RemoveContainer(aNode); return res; @@ -1465,7 +1577,29 @@ nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, // can it be put inside a "big" or "small"? if (TagCanContain(tag, aNode)) { + // first populate any nested font tags that have the size attr set + res = RelativeFontChangeHelper(aSizeChange, aNode); + if (NS_FAILED(res)) return res; // ok, chuck it in. + // first look at siblings of aNode for matching bigs or smalls. + // if we find one, move aNode into it. + nsCOMPtr sibling; + GetPriorHTMLSibling(aNode, address_of(sibling)); + if (sibling && NodeIsType(sibling, NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small"))) + { + // previous sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, sibling, -1); + return res; + } + sibling = nsnull; + GetNextHTMLSibling(aNode, address_of(sibling)); + if (sibling && NodeIsType(sibling, NS_ConvertASCIItoUCS2(aSizeChange==1 ? "big" : "small"))) + { + // following sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, sibling, 0); + return res; + } + // else insert it above aNode res = InsertContainerAbove(aNode, address_of(tmp), tag); return res; } @@ -1481,7 +1615,7 @@ nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, PRInt32 j; PRUint32 childCount; childNodes->GetLength(&childCount); - for (j=0 ; j < (PRInt32)childCount; j++) + for (j=childCount-1; j>=0; j--) { nsCOMPtr childNode; res = childNodes->Item(j, getter_AddRefs(childNode)); diff --git a/mozilla/editor/libeditor/html/nsWSRunObject.cpp b/mozilla/editor/libeditor/html/nsWSRunObject.cpp index fd33a7d0fcc..e51db7f6a6e 100644 --- a/mozilla/editor/libeditor/html/nsWSRunObject.cpp +++ b/mozilla/editor/libeditor/html/nsWSRunObject.cpp @@ -242,11 +242,11 @@ nsWSRunObject::InsertBreak(nsCOMPtr *aInOutParent, // convert run to nbsp. WSPoint thePoint; res = GetCharAfter(*aInOutParent, *aInOutOffset, &thePoint); - if ( (NS_SUCCEEDED(res)) && (nsCRT::IsAsciiSpace(thePoint.mChar)) ) + if ( (NS_SUCCEEDED(res)) && thePoint.mTextNode && (nsCRT::IsAsciiSpace(thePoint.mChar)) ) { WSPoint prevPoint; res = GetCharBefore(thePoint, &prevPoint); - if ( (NS_FAILED(res)) || (!nsCRT::IsAsciiSpace(prevPoint.mChar)) ) + if ( (NS_FAILED(res)) || (prevPoint.mTextNode && !nsCRT::IsAsciiSpace(prevPoint.mChar)) ) { // we are at start of non-nbsps. convert to a single nbsp. res = ConvertToNBSP(thePoint); @@ -372,7 +372,7 @@ nsWSRunObject::InsertText(const nsAReadableString& aStringToInsert, { WSPoint wspoint; res = GetCharBefore(*aInOutParent, *aInOutOffset, &wspoint); - if (NS_SUCCEEDED(res) && nsCRT::IsAsciiSpace(wspoint.mChar)) + if (NS_SUCCEEDED(res) && wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) { theString.SetCharAt(nbsp, 0); } @@ -403,7 +403,7 @@ nsWSRunObject::InsertText(const nsAReadableString& aStringToInsert, { WSPoint wspoint; res = GetCharAfter(*aInOutParent, *aInOutOffset, &wspoint); - if (NS_SUCCEEDED(res) && nsCRT::IsAsciiSpace(wspoint.mChar)) + if (NS_SUCCEEDED(res) && wspoint.mTextNode && nsCRT::IsAsciiSpace(wspoint.mChar)) { theString.SetCharAt(nbsp, lastCharIndex); } @@ -455,6 +455,7 @@ nsWSRunObject::DeleteWSBackward() WSPoint point; res = GetCharBefore(mNode, mOffset, &point); NS_ENSURE_SUCCESS(res, res); + if (!point.mTextNode) return NS_OK; // nothing to delete // callers job to insure that previous char is really ws. // If it is normal ws, we need to delete the whole run @@ -498,6 +499,7 @@ nsWSRunObject::DeleteWSForward() WSPoint point; res = GetCharAfter(mNode, mOffset, &point); NS_ENSURE_SUCCESS(res, res); + if (!point.mTextNode) return NS_OK; // nothing to delete // callers job to insure that next char is really ws. // If it is normal ws, we need to delete the whole run @@ -555,8 +557,7 @@ nsWSRunObject::PriorVisibleNode(nsIDOMNode *aNode, if (run->mType == eNormalWS) { WSPoint point; - res = GetCharBefore(aNode, aOffset, &point); - NS_ENSURE_SUCCESS(res, res); + GetCharBefore(aNode, aOffset, &point); if (point.mTextNode) { *outVisNode = do_QueryInterface(point.mTextNode); @@ -597,7 +598,7 @@ nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode, PRInt32 *outVisOffset, PRInt16 *outType) { - // Find first visible thing before the point. position outVisNode/outVisOffset + // Find first visible thing after the point. position outVisNode/outVisOffset // just _before_ that thing. If we don't find anything return end of ws. if (!aNode || !outVisNode || !outVisOffset || !outType) return NS_ERROR_NULL_POINTER; @@ -611,8 +612,7 @@ nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode, if (run->mType == eNormalWS) { WSPoint point; - res = GetCharAfter(aNode, aOffset, &point); - NS_ENSURE_SUCCESS(res, res); + GetCharAfter(aNode, aOffset, &point); if (point.mTextNode) { *outVisNode = do_QueryInterface(point.mTextNode); @@ -1458,7 +1458,7 @@ nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject) // make sure leading char of following ws is an nbsp, so that it will show up WSPoint point; aEndObject->GetCharAfter(aEndObject->mNode, aEndObject->mOffset, &point); - if (nsCRT::IsAsciiSpace(point.mChar)) + if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) { res = ConvertToNBSP(point); NS_ENSURE_SUCCESS(res, res); @@ -1480,7 +1480,7 @@ nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject) // make sure trailing char of starting ws is an nbsp, so that it will show up WSPoint point; GetCharBefore(mNode, mOffset, &point); - if (nsCRT::IsAsciiSpace(point.mChar)) + if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) { nsCOMPtr wsStartNode, wsEndNode; PRInt32 wsStartOffset, wsEndOffset; @@ -1518,7 +1518,7 @@ nsWSRunObject::PrepareToSplitAcrossBlocksPriv() // make sure leading char of following ws is an nbsp, so that it will show up WSPoint point; GetCharAfter(mNode, mOffset, &point); - if (nsCRT::IsAsciiSpace(point.mChar)) + if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) { res = ConvertToNBSP(point); NS_ENSURE_SUCCESS(res, res); @@ -1531,7 +1531,7 @@ nsWSRunObject::PrepareToSplitAcrossBlocksPriv() // make sure trailing char of starting ws is an nbsp, so that it will show up WSPoint point; GetCharBefore(mNode, mOffset, &point); - if (nsCRT::IsAsciiSpace(point.mChar)) + if (point.mTextNode && nsCRT::IsAsciiSpace(point.mChar)) { nsCOMPtr wsStartNode, wsEndNode; PRInt32 wsStartOffset, wsEndOffset; @@ -1696,7 +1696,7 @@ nsWSRunObject::GetCharAfter(WSPoint &aPoint, WSPoint *outPoint) nsCOMPtr isupps(do_QueryInterface(aPoint.mTextNode)); PRInt32 idx = mNodeArray->IndexOf(isupps); - if (idx == -1) return NS_ERROR_FAILURE; + if (idx == -1) return NS_OK; // can't find point, but it's not an error PRUint32 numNodes; mNodeArray->Count(&numNodes); @@ -1718,7 +1718,6 @@ nsWSRunObject::GetCharAfter(WSPoint &aPoint, WSPoint *outPoint) outPoint->mOffset = 0; outPoint->mChar = GetCharAt(outPoint->mTextNode, 0); } - else return NS_ERROR_FAILURE; return NS_OK; } @@ -1735,7 +1734,7 @@ nsWSRunObject::GetCharBefore(WSPoint &aPoint, WSPoint *outPoint) nsresult res = NS_OK; nsCOMPtr isupps(do_QueryInterface(aPoint.mTextNode)); PRInt32 idx = mNodeArray->IndexOf(isupps); - if (idx == -1) return NS_ERROR_FAILURE; + if (idx == -1) return NS_OK; // can't find point, but it's not an error if (aPoint.mOffset != 0) { @@ -1757,8 +1756,7 @@ nsWSRunObject::GetCharBefore(WSPoint &aPoint, WSPoint *outPoint) outPoint->mChar = GetCharAt(outPoint->mTextNode, len-1); } } - else return NS_ERROR_FAILURE; - return res; + return NS_OK; } nsresult @@ -1829,7 +1827,7 @@ nsWSRunObject::GetAsciiWSBounds(PRInt16 aDir, nsIDOMNode *aNode, PRInt32 aOffset endOffset = point.mOffset; tmp = point; res = GetCharAfter(tmp, &point); - if (NS_FAILED(res)) break; + if (NS_FAILED(res) || !point.mTextNode) break; } } } @@ -1855,7 +1853,7 @@ nsWSRunObject::GetAsciiWSBounds(PRInt16 aDir, nsIDOMNode *aNode, PRInt32 aOffset startOffset = point.mOffset; tmp = point; res = GetCharBefore(tmp, &point); - if (NS_FAILED(res)) break; + if (NS_FAILED(res) || !point.mTextNode) break; } } } @@ -2091,12 +2089,12 @@ nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun) // first check for trailing nbsp nsresult res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint); - if (NS_SUCCEEDED(res) && thePoint.mChar == nbsp) + if (NS_SUCCEEDED(res) && thePoint.mTextNode && thePoint.mChar == nbsp) { // now check that what is to the left of it is compatible with replacing nbsp with space WSPoint prevPoint; res = GetCharBefore(thePoint, &prevPoint); - if (NS_SUCCEEDED(res)) + if (NS_SUCCEEDED(res) && prevPoint.mTextNode) { if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = PR_TRUE; } @@ -2141,11 +2139,11 @@ nsWSRunObject::CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aO WSPoint thePoint; PRBool canConvert = PR_FALSE; nsresult res = GetCharBefore(aNode, aOffset, &thePoint); - if (NS_SUCCEEDED(res) && thePoint.mChar == nbsp) + if (NS_SUCCEEDED(res) && thePoint.mTextNode && thePoint.mChar == nbsp) { WSPoint prevPoint; res = GetCharBefore(thePoint, &prevPoint); - if (NS_SUCCEEDED(res)) + if (NS_SUCCEEDED(res) && prevPoint.mTextNode) { if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) canConvert = PR_TRUE; } @@ -2186,7 +2184,7 @@ nsWSRunObject::CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOf WSPoint nextPoint, tmp=thePoint; tmp.mOffset++; // we want to be after thePoint res = GetCharAfter(tmp, &nextPoint); - if (NS_SUCCEEDED(res)) + if (NS_SUCCEEDED(res) && nextPoint.mTextNode) { if (!nsCRT::IsAsciiSpace(nextPoint.mChar)) canConvert = PR_TRUE; }