diff --git a/mozilla/editor/base/nsHTMLDataTransfer.cpp b/mozilla/editor/base/nsHTMLDataTransfer.cpp index 0b329f6503a..1e3d4a22b5f 100644 --- a/mozilla/editor/base/nsHTMLDataTransfer.cpp +++ b/mozilla/editor/base/nsHTMLDataTransfer.cpp @@ -312,151 +312,53 @@ nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsAReadableString & parentNode = temp; } - // build up list of parents of first node in lst that are either: - // lists, or tables. - nsCOMPtr isup = dont_AddRef(nodeList->ElementAt(0)); - nsCOMPtr pNode( do_QueryInterface(isup) ); - nsCOMPtr listAndTableArray; - res = NS_NewISupportsArray(getter_AddRefs(listAndTableArray)); + // build up list of parents of first node in list that are either + // lists or tables. First examine front of paste node list. + nsCOMPtr startListAndTableArray; + res = GetListAndTableParents(PR_FALSE, nodeList, address_of(startListAndTableArray)); NS_ENSURE_SUCCESS(res, res); - while (pNode) - { - if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode)) - { - isup = do_QueryInterface(pNode); - listAndTableArray->AppendElement(isup); - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } // remember number of lists and tables above us - PRUint32 listAndTableParents; PRInt32 highWaterMark = -1; - listAndTableArray->Count(&listAndTableParents); - - PRUint32 listCount, j; - if (listAndTableParents) + if (startListAndTableArray->ElementAt(0)) { - // scan insertion list for table elements (other than table). - nodeList->Count(&listCount); - for (j=0; j isupports = dont_AddRef(nodeList->ElementAt(j)); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - - NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); - if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode)) - { - nsCOMPtr theTable = GetTableParent(curNode); - if (theTable) - { - nsCOMPtr isupTable(do_QueryInterface(theTable)); - PRInt32 indexT = listAndTableArray->IndexOf(isupTable); - if (indexT >= 0) - { - highWaterMark = indexT; - if ((PRUint32)highWaterMark == listAndTableParents-1) break; - } - else - { - break; - } - } - } - if (nsHTMLEditUtils::IsListItem(curNode)) - { - nsCOMPtr theList = GetListParent(curNode); - if (theList) - { - nsCOMPtr isupList(do_QueryInterface(theList)); - PRInt32 indexL = listAndTableArray->IndexOf(isupList); - if (indexL >= 0) - { - highWaterMark = indexL; - if ((PRUint32)highWaterMark == listAndTableParents-1) break; - } - else - { - break; - } - } - } - } + res = DiscoverPartialListsAndTables(nodeList, startListAndTableArray, &highWaterMark); + NS_ENSURE_SUCCESS(res, res); } + // if we have pieces of tables or lists to be inserted, let's force the paste // to deal with table elements right away, so that it doesn't orphan some // table or list contents outside the table or list. if (highWaterMark >= 0) { - nsCOMPtr isupports = dont_AddRef(listAndTableArray->ElementAt(highWaterMark)); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - nsCOMPtr replaceNode, tmp; - if (nsHTMLEditUtils::IsTable(curNode)) - { - // look upward from curNode for a piece of this table - isup = dont_AddRef(nodeList->ElementAt(0)); - pNode = do_QueryInterface(isup); - while (pNode) - { - if (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode)) - { - nsCOMPtr tableP = GetTableParent(pNode); - if (tableP == curNode) - { - replaceNode = pNode; - break; - } - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - } - else // list case - { - // look upward from curNode for a piece of this list - isup = dont_AddRef(nodeList->ElementAt(0)); - pNode = do_QueryInterface(isup); - while (pNode) - { - if (nsHTMLEditUtils::IsListItem(pNode)) - { - nsCOMPtr listP = GetListParent(pNode); - if (listP == curNode) - { - replaceNode = pNode; - break; - } - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - } - - if (replaceNode) - { - isupports = do_QueryInterface(replaceNode); - nodeList->ReplaceElementAt(isupports, 0); - // postprocess list to remove any descendants of this node - // so that we dont insert them twice. - do - { - isupports = dont_AddRef(nodeList->ElementAt(1)); - tmp = do_QueryInterface(isupports); - if (tmp && nsHTMLEditUtils::IsDescendantOf(tmp, replaceNode)) - nodeList->RemoveElementAt(1); - else - break; - } while(tmp); - } + res = ReplaceOrphanedStructure(PR_FALSE, nodeList, startListAndTableArray, highWaterMark); + NS_ENSURE_SUCCESS(res, res); } + // Now go through the same process again for the end of the paste node list. + nsCOMPtr endListAndTableArray; + res = GetListAndTableParents(PR_TRUE, nodeList, address_of(endListAndTableArray)); + NS_ENSURE_SUCCESS(res, res); + highWaterMark = -1; + + // remember number of lists and tables above us + if (endListAndTableArray->ElementAt(0)) + { + res = DiscoverPartialListsAndTables(nodeList, endListAndTableArray, &highWaterMark); + NS_ENSURE_SUCCESS(res, res); + } + + // don't orphan partial list or table structure + if (highWaterMark >= 0) + { + res = ReplaceOrphanedStructure(PR_TRUE, nodeList, endListAndTableArray, highWaterMark); + NS_ENSURE_SUCCESS(res, res); + } + // Loop over the node list and paste the nodes: PRBool bDidInsert = PR_FALSE; nsCOMPtr lastInsertNode, insertedContextParent; + PRUint32 listCount, j; nodeList->Count(&listCount); for (j=0; jGetFirstChild(getter_AddRefs(child)); while (child) { - InsertNodeAtPoint(child, parentNode, offsetOfNewNode, PR_TRUE); + res = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, PR_TRUE); if (NS_SUCCEEDED(res)) { bDidInsert = PR_TRUE; - lastInsertNode = curNode; + lastInsertNode = child; offsetOfNewNode++; } curNode->GetFirstChild(getter_AddRefs(child)); } } + // give the user a hand on list insertion. if they have + // a list on the clipboard, and are trying to insert + // into a list or list item, insert the appropriate children instead, + // ie, merge the lists instead of pasting in a sublist. + else if (nsHTMLEditUtils::IsList(curNode) && + (nsHTMLEditUtils::IsList(parentNode) || nsHTMLEditUtils::IsListItem(parentNode)) ) + { + nsCOMPtr child, tmp; + curNode->GetFirstChild(getter_AddRefs(child)); + while (child) + { + if (nsHTMLEditUtils::IsListItem(child) || nsHTMLEditUtils::IsList(child)) + { + res = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + lastInsertNode = child; + offsetOfNewNode++; + } + } + else + { + curNode->RemoveChild(child, getter_AddRefs(tmp)); + } + curNode->GetFirstChild(getter_AddRefs(child)); + } + } else { // try to insert - res = InsertNodeAtPoint(curNode, parentNode, offsetOfNewNode, PR_TRUE); + res = InsertNodeAtPoint(curNode, address_of(parentNode), &offsetOfNewNode, PR_TRUE); if (NS_SUCCEEDED(res)) { bDidInsert = PR_TRUE; @@ -513,7 +443,7 @@ nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsAReadableString & curNode->GetParentNode(getter_AddRefs(parent)); if (parent && !nsTextEditUtils::IsBody(parent)) { - res = InsertNodeAtPoint(parent, parentNode, offsetOfNewNode, PR_TRUE); + res = InsertNodeAtPoint(parent, address_of(parentNode), &offsetOfNewNode, PR_TRUE); if (NS_SUCCEEDED(res)) { bDidInsert = PR_TRUE; @@ -1611,4 +1541,205 @@ nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode, return res; } +nsresult +nsHTMLEditor::GetListAndTableParents(PRBool aEnd, + nsISupportsArray *aListOfNodes, + nsCOMPtr *outArray) +{ + NS_ENSURE_TRUE(aListOfNodes, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(outArray, NS_ERROR_NULL_POINTER); + + PRUint32 listCount; + aListOfNodes->Count(&listCount); + if (listCount <= 0) + return NS_ERROR_FAILURE; // no empty lists, please + + // build up list of parents of first (or last) node in list + // that are either lists, or tables. + PRUint32 idx = 0; + if (aEnd) idx = listCount-1; + + nsCOMPtr isup = dont_AddRef(aListOfNodes->ElementAt(idx)); + nsCOMPtr pNode( do_QueryInterface(isup) ); + nsCOMPtr listAndTableArray; + nsresult res = NS_NewISupportsArray(getter_AddRefs(listAndTableArray)); + NS_ENSURE_SUCCESS(res, res); + while (pNode) + { + if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode)) + { + isup = do_QueryInterface(pNode); + listAndTableArray->AppendElement(isup); + } + nsCOMPtr parent; + pNode->GetParentNode(getter_AddRefs(parent)); + pNode = parent; + } + *outArray = listAndTableArray; + return NS_OK; +} +nsresult +nsHTMLEditor::DiscoverPartialListsAndTables(nsISupportsArray *aPasteNodes, + nsISupportsArray *aListsAndTables, + PRInt32 *outHighWaterMark) +{ + NS_ENSURE_TRUE(aPasteNodes, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aListsAndTables, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(outHighWaterMark, NS_ERROR_NULL_POINTER); + + *outHighWaterMark = -1; + PRUint32 listAndTableParents; + aListsAndTables->Count(&listAndTableParents); + + // scan insertion list for table elements (other than table). + PRUint32 listCount, j; + aPasteNodes->Count(&listCount); + for (j=0; j isupports = dont_AddRef(aPasteNodes->ElementAt(j)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); + if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode)) + { + nsCOMPtr theTable = GetTableParent(curNode); + if (theTable) + { + nsCOMPtr isupTable(do_QueryInterface(theTable)); + PRInt32 indexT = aListsAndTables->IndexOf(isupTable); + if (indexT >= 0) + { + *outHighWaterMark = indexT; + if ((PRUint32)*outHighWaterMark == listAndTableParents-1) break; + } + else + { + break; + } + } + } + if (nsHTMLEditUtils::IsListItem(curNode)) + { + nsCOMPtr theList = GetListParent(curNode); + if (theList) + { + nsCOMPtr isupList(do_QueryInterface(theList)); + PRInt32 indexL = aListsAndTables->IndexOf(isupList); + if (indexL >= 0) + { + *outHighWaterMark = indexL; + if ((PRUint32)*outHighWaterMark == listAndTableParents-1) break; + } + else + { + break; + } + } + } + } + return NS_OK; +} + +nsresult +nsHTMLEditor::ScanForListAndTableStructure( PRBool aEnd, + nsISupportsArray *aNodes, + nsIDOMNode *aListOrTable, + nsCOMPtr *outReplaceNode) +{ + NS_ENSURE_TRUE(aNodes, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aListOrTable, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(outReplaceNode, NS_ERROR_NULL_POINTER); + + *outReplaceNode = 0; + + // look upward from first/last paste node for a piece of this list/table + PRUint32 listCount, idx = 0; + aNodes->Count(&listCount); + if (aEnd) idx = listCount-1; + PRBool bList = nsHTMLEditUtils::IsList(aListOrTable); + + nsCOMPtr isup = dont_AddRef(aNodes->ElementAt(idx)); + nsCOMPtr pNode = do_QueryInterface(isup); + nsCOMPtr originalNode = pNode; + while (pNode) + { + if ( (bList && nsHTMLEditUtils::IsListItem(pNode)) || + (!bList && (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode))) ) + { + nsCOMPtr structureNode; + if (bList) structureNode = GetListParent(pNode); + else structureNode = GetTableParent(pNode); + if (structureNode == aListOrTable) + { + if (pNode == originalNode) + break; // we are starting right off with a list item of the list + *outReplaceNode = pNode; + break; + } + } + nsCOMPtr parent; + pNode->GetParentNode(getter_AddRefs(parent)); + pNode = parent; + } + return NS_OK; +} + +nsresult +nsHTMLEditor::ReplaceOrphanedStructure(PRBool aEnd, + nsISupportsArray *aNodeArray, + nsISupportsArray *aListAndTableArray, + PRInt32 aHighWaterMark) +{ + NS_ENSURE_TRUE(aNodeArray, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aListAndTableArray, NS_ERROR_NULL_POINTER); + + nsCOMPtr isupports = dont_AddRef(aListAndTableArray->ElementAt(aHighWaterMark)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + NS_ENSURE_TRUE(curNode, NS_ERROR_NULL_POINTER); + + nsCOMPtr replaceNode, originalNode, tmp; + + // find substructure of list or table that must be included in paste. + nsresult res = ScanForListAndTableStructure(aEnd, aNodeArray, + curNode, address_of(replaceNode)); + NS_ENSURE_SUCCESS(res, res); + + // if we found substructure, paste it instead of it's descendants + if (replaceNode) + { + // postprocess list to remove any descendants of this node + // so that we dont insert them twice. + do + { + isupports = GetArrayEndpoint(aEnd, aNodeArray); + if (!isupports) break; + tmp = do_QueryInterface(isupports); + if (tmp && nsHTMLEditUtils::IsDescendantOf(tmp, replaceNode)) + aNodeArray->RemoveElement(isupports); + else + break; + } while(tmp); + + // now replace the removed nodes with the structural parent + isupports = do_QueryInterface(replaceNode); + if (aEnd) aNodeArray->AppendElement(isupports); + else aNodeArray->InsertElementAt(isupports, 0); + } + return NS_OK; +} + +nsISupports* nsHTMLEditor::GetArrayEndpoint(PRBool aEnd, nsISupportsArray *aNodeArray) +{ + if (aEnd) + { + PRUint32 listCount; + aNodeArray->Count(&listCount); + if (listCount <= 0) return nsnull; + else return aNodeArray->ElementAt(listCount-1); + } + else + { + return aNodeArray->ElementAt(0); + } +} diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp index 70efd18058d..ec9ac4dfaf9 100644 --- a/mozilla/editor/base/nsHTMLEditor.cpp +++ b/mozilla/editor/base/nsHTMLEditor.cpp @@ -1779,7 +1779,7 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe } #endif - res = InsertNodeAtPoint(node, parentSelectedNode, offsetForInsert, PR_FALSE); + res = InsertNodeAtPoint(node, address_of(parentSelectedNode), &offsetForInsert, PR_FALSE); NS_ENSURE_SUCCESS(res, res); // Set caret after element, but check for special case // of inserting table-related elements: set in first cell instead @@ -1792,23 +1792,39 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe return res; } + +/* + InsertNodeAtPoint: attempts to insert aNode into the document, at a point specified by + {*ioParent,*ioOffset}. Checks with strict dtd to see if containment is allowed. If not + allowed, will attempt to find a parent in the parent heirarchy of *ioParent that will + accept aNode as a child. If such a parent is found, will split the document tree from + {*ioParent,*ioOffset} up to parent, and then insert aNode. ioParent & ioOffset are then + adjusted to point to the actual location that aNode was inserted at. aNoEmptyNodes + specifies if the splitting process is allowed to reslt in empty nodes. + nsIDOMNode *aNode node to insert + nsCOMPtr *ioParent insertion parent + PRInt32 *ioOffset insertion offset + PRBool aNoEmptyNodes splitting can result in empty nodes? +*/ nsresult nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aOffset, + nsCOMPtr *ioParent, + PRInt32 *ioOffset, PRBool aNoEmptyNodes) { NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); - NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(ioParent, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(*ioParent, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(ioOffset, NS_ERROR_NULL_POINTER); nsresult res = NS_OK; nsAutoString tagName; aNode->GetNodeName(tagName); tagName.ToLowerCase(); - nsCOMPtr parent = aParent; - nsCOMPtr topChild = aParent; + nsCOMPtr parent = *ioParent; + nsCOMPtr topChild = *ioParent; nsCOMPtr tmp; - PRInt32 offsetOfInsert = aOffset; + PRInt32 offsetOfInsert = *ioOffset; // Search up the parent chain to find a suitable container while (!CanContainTag(parent, tagName)) @@ -1826,9 +1842,11 @@ nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, if (parent != topChild) { // we need to split some levels above the original selection parent - res = SplitNodeDeep(topChild, aParent, aOffset, &offsetOfInsert, aNoEmptyNodes); + res = SplitNodeDeep(topChild, *ioParent, *ioOffset, &offsetOfInsert, aNoEmptyNodes); if (NS_FAILED(res)) return res; + *ioParent = parent; + *ioOffset = offsetOfInsert; } // Now we can insert the new node res = InsertNode(aNode, parent, offsetOfInsert); diff --git a/mozilla/editor/base/nsHTMLEditor.h b/mozilla/editor/base/nsHTMLEditor.h index 3758dd5b818..eced4d50cec 100644 --- a/mozilla/editor/base/nsHTMLEditor.h +++ b/mozilla/editor/base/nsHTMLEditor.h @@ -351,10 +351,10 @@ public: /* ------------ Utility Routines, not part of public API -------------- */ NS_IMETHOD TypedText(const nsAReadableString& aString, PRInt32 aAction); - nsresult InsertNodeAtPoint(nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aOffset, - PRBool aNoEmptyNodes); + nsresult InsertNodeAtPoint( nsIDOMNode *aNode, + nsCOMPtr *ioParent, + PRInt32 *ioOffset, + PRBool aNoEmptyNodes); /** returns the absolute position of the end points of aSelection @@ -568,6 +568,21 @@ protected: nsCOMPtr *outNodeList, PRInt32 aRangeStartHint, PRInt32 aRangeEndHint); + nsresult GetListAndTableParents( PRBool aEnd, + nsISupportsArray *aListOfNodes, + nsCOMPtr *outArray); + nsresult DiscoverPartialListsAndTables( nsISupportsArray *aPasteNodes, + nsISupportsArray *aListsAndTables, + PRInt32 *outHighWaterMark); + nsresult ScanForListAndTableStructure(PRBool aEnd, + nsISupportsArray *aNodes, + nsIDOMNode *aListOrTable, + nsCOMPtr *outReplaceNode); + nsresult ReplaceOrphanedStructure( PRBool aEnd, + nsISupportsArray *aNodeArray, + nsISupportsArray *aListAndTableArray, + PRInt32 aHighWaterMark); + nsISupports* GetArrayEndpoint(PRBool aEnd, nsISupportsArray *aNodeArray); /** simple utility to handle any error with event listener allocation or registration */ void HandleEventListenerError(); diff --git a/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp b/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp index 0b329f6503a..1e3d4a22b5f 100644 --- a/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLDataTransfer.cpp @@ -312,151 +312,53 @@ nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsAReadableString & parentNode = temp; } - // build up list of parents of first node in lst that are either: - // lists, or tables. - nsCOMPtr isup = dont_AddRef(nodeList->ElementAt(0)); - nsCOMPtr pNode( do_QueryInterface(isup) ); - nsCOMPtr listAndTableArray; - res = NS_NewISupportsArray(getter_AddRefs(listAndTableArray)); + // build up list of parents of first node in list that are either + // lists or tables. First examine front of paste node list. + nsCOMPtr startListAndTableArray; + res = GetListAndTableParents(PR_FALSE, nodeList, address_of(startListAndTableArray)); NS_ENSURE_SUCCESS(res, res); - while (pNode) - { - if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode)) - { - isup = do_QueryInterface(pNode); - listAndTableArray->AppendElement(isup); - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } // remember number of lists and tables above us - PRUint32 listAndTableParents; PRInt32 highWaterMark = -1; - listAndTableArray->Count(&listAndTableParents); - - PRUint32 listCount, j; - if (listAndTableParents) + if (startListAndTableArray->ElementAt(0)) { - // scan insertion list for table elements (other than table). - nodeList->Count(&listCount); - for (j=0; j isupports = dont_AddRef(nodeList->ElementAt(j)); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - - NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); - if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode)) - { - nsCOMPtr theTable = GetTableParent(curNode); - if (theTable) - { - nsCOMPtr isupTable(do_QueryInterface(theTable)); - PRInt32 indexT = listAndTableArray->IndexOf(isupTable); - if (indexT >= 0) - { - highWaterMark = indexT; - if ((PRUint32)highWaterMark == listAndTableParents-1) break; - } - else - { - break; - } - } - } - if (nsHTMLEditUtils::IsListItem(curNode)) - { - nsCOMPtr theList = GetListParent(curNode); - if (theList) - { - nsCOMPtr isupList(do_QueryInterface(theList)); - PRInt32 indexL = listAndTableArray->IndexOf(isupList); - if (indexL >= 0) - { - highWaterMark = indexL; - if ((PRUint32)highWaterMark == listAndTableParents-1) break; - } - else - { - break; - } - } - } - } + res = DiscoverPartialListsAndTables(nodeList, startListAndTableArray, &highWaterMark); + NS_ENSURE_SUCCESS(res, res); } + // if we have pieces of tables or lists to be inserted, let's force the paste // to deal with table elements right away, so that it doesn't orphan some // table or list contents outside the table or list. if (highWaterMark >= 0) { - nsCOMPtr isupports = dont_AddRef(listAndTableArray->ElementAt(highWaterMark)); - nsCOMPtr curNode( do_QueryInterface(isupports) ); - nsCOMPtr replaceNode, tmp; - if (nsHTMLEditUtils::IsTable(curNode)) - { - // look upward from curNode for a piece of this table - isup = dont_AddRef(nodeList->ElementAt(0)); - pNode = do_QueryInterface(isup); - while (pNode) - { - if (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode)) - { - nsCOMPtr tableP = GetTableParent(pNode); - if (tableP == curNode) - { - replaceNode = pNode; - break; - } - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - } - else // list case - { - // look upward from curNode for a piece of this list - isup = dont_AddRef(nodeList->ElementAt(0)); - pNode = do_QueryInterface(isup); - while (pNode) - { - if (nsHTMLEditUtils::IsListItem(pNode)) - { - nsCOMPtr listP = GetListParent(pNode); - if (listP == curNode) - { - replaceNode = pNode; - break; - } - } - nsCOMPtr parent; - pNode->GetParentNode(getter_AddRefs(parent)); - pNode = parent; - } - } - - if (replaceNode) - { - isupports = do_QueryInterface(replaceNode); - nodeList->ReplaceElementAt(isupports, 0); - // postprocess list to remove any descendants of this node - // so that we dont insert them twice. - do - { - isupports = dont_AddRef(nodeList->ElementAt(1)); - tmp = do_QueryInterface(isupports); - if (tmp && nsHTMLEditUtils::IsDescendantOf(tmp, replaceNode)) - nodeList->RemoveElementAt(1); - else - break; - } while(tmp); - } + res = ReplaceOrphanedStructure(PR_FALSE, nodeList, startListAndTableArray, highWaterMark); + NS_ENSURE_SUCCESS(res, res); } + // Now go through the same process again for the end of the paste node list. + nsCOMPtr endListAndTableArray; + res = GetListAndTableParents(PR_TRUE, nodeList, address_of(endListAndTableArray)); + NS_ENSURE_SUCCESS(res, res); + highWaterMark = -1; + + // remember number of lists and tables above us + if (endListAndTableArray->ElementAt(0)) + { + res = DiscoverPartialListsAndTables(nodeList, endListAndTableArray, &highWaterMark); + NS_ENSURE_SUCCESS(res, res); + } + + // don't orphan partial list or table structure + if (highWaterMark >= 0) + { + res = ReplaceOrphanedStructure(PR_TRUE, nodeList, endListAndTableArray, highWaterMark); + NS_ENSURE_SUCCESS(res, res); + } + // Loop over the node list and paste the nodes: PRBool bDidInsert = PR_FALSE; nsCOMPtr lastInsertNode, insertedContextParent; + PRUint32 listCount, j; nodeList->Count(&listCount); for (j=0; jGetFirstChild(getter_AddRefs(child)); while (child) { - InsertNodeAtPoint(child, parentNode, offsetOfNewNode, PR_TRUE); + res = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, PR_TRUE); if (NS_SUCCEEDED(res)) { bDidInsert = PR_TRUE; - lastInsertNode = curNode; + lastInsertNode = child; offsetOfNewNode++; } curNode->GetFirstChild(getter_AddRefs(child)); } } + // give the user a hand on list insertion. if they have + // a list on the clipboard, and are trying to insert + // into a list or list item, insert the appropriate children instead, + // ie, merge the lists instead of pasting in a sublist. + else if (nsHTMLEditUtils::IsList(curNode) && + (nsHTMLEditUtils::IsList(parentNode) || nsHTMLEditUtils::IsListItem(parentNode)) ) + { + nsCOMPtr child, tmp; + curNode->GetFirstChild(getter_AddRefs(child)); + while (child) + { + if (nsHTMLEditUtils::IsListItem(child) || nsHTMLEditUtils::IsList(child)) + { + res = InsertNodeAtPoint(child, address_of(parentNode), &offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + lastInsertNode = child; + offsetOfNewNode++; + } + } + else + { + curNode->RemoveChild(child, getter_AddRefs(tmp)); + } + curNode->GetFirstChild(getter_AddRefs(child)); + } + } else { // try to insert - res = InsertNodeAtPoint(curNode, parentNode, offsetOfNewNode, PR_TRUE); + res = InsertNodeAtPoint(curNode, address_of(parentNode), &offsetOfNewNode, PR_TRUE); if (NS_SUCCEEDED(res)) { bDidInsert = PR_TRUE; @@ -513,7 +443,7 @@ nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsAReadableString & curNode->GetParentNode(getter_AddRefs(parent)); if (parent && !nsTextEditUtils::IsBody(parent)) { - res = InsertNodeAtPoint(parent, parentNode, offsetOfNewNode, PR_TRUE); + res = InsertNodeAtPoint(parent, address_of(parentNode), &offsetOfNewNode, PR_TRUE); if (NS_SUCCEEDED(res)) { bDidInsert = PR_TRUE; @@ -1611,4 +1541,205 @@ nsresult nsHTMLEditor::CreateListOfNodesToPaste(nsIDOMNode *aFragmentAsNode, return res; } +nsresult +nsHTMLEditor::GetListAndTableParents(PRBool aEnd, + nsISupportsArray *aListOfNodes, + nsCOMPtr *outArray) +{ + NS_ENSURE_TRUE(aListOfNodes, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(outArray, NS_ERROR_NULL_POINTER); + + PRUint32 listCount; + aListOfNodes->Count(&listCount); + if (listCount <= 0) + return NS_ERROR_FAILURE; // no empty lists, please + + // build up list of parents of first (or last) node in list + // that are either lists, or tables. + PRUint32 idx = 0; + if (aEnd) idx = listCount-1; + + nsCOMPtr isup = dont_AddRef(aListOfNodes->ElementAt(idx)); + nsCOMPtr pNode( do_QueryInterface(isup) ); + nsCOMPtr listAndTableArray; + nsresult res = NS_NewISupportsArray(getter_AddRefs(listAndTableArray)); + NS_ENSURE_SUCCESS(res, res); + while (pNode) + { + if (nsHTMLEditUtils::IsList(pNode) || nsHTMLEditUtils::IsTable(pNode)) + { + isup = do_QueryInterface(pNode); + listAndTableArray->AppendElement(isup); + } + nsCOMPtr parent; + pNode->GetParentNode(getter_AddRefs(parent)); + pNode = parent; + } + *outArray = listAndTableArray; + return NS_OK; +} +nsresult +nsHTMLEditor::DiscoverPartialListsAndTables(nsISupportsArray *aPasteNodes, + nsISupportsArray *aListsAndTables, + PRInt32 *outHighWaterMark) +{ + NS_ENSURE_TRUE(aPasteNodes, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aListsAndTables, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(outHighWaterMark, NS_ERROR_NULL_POINTER); + + *outHighWaterMark = -1; + PRUint32 listAndTableParents; + aListsAndTables->Count(&listAndTableParents); + + // scan insertion list for table elements (other than table). + PRUint32 listCount, j; + aPasteNodes->Count(&listCount); + for (j=0; j isupports = dont_AddRef(aPasteNodes->ElementAt(j)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); + if (nsHTMLEditUtils::IsTableElement(curNode) && !nsHTMLEditUtils::IsTable(curNode)) + { + nsCOMPtr theTable = GetTableParent(curNode); + if (theTable) + { + nsCOMPtr isupTable(do_QueryInterface(theTable)); + PRInt32 indexT = aListsAndTables->IndexOf(isupTable); + if (indexT >= 0) + { + *outHighWaterMark = indexT; + if ((PRUint32)*outHighWaterMark == listAndTableParents-1) break; + } + else + { + break; + } + } + } + if (nsHTMLEditUtils::IsListItem(curNode)) + { + nsCOMPtr theList = GetListParent(curNode); + if (theList) + { + nsCOMPtr isupList(do_QueryInterface(theList)); + PRInt32 indexL = aListsAndTables->IndexOf(isupList); + if (indexL >= 0) + { + *outHighWaterMark = indexL; + if ((PRUint32)*outHighWaterMark == listAndTableParents-1) break; + } + else + { + break; + } + } + } + } + return NS_OK; +} + +nsresult +nsHTMLEditor::ScanForListAndTableStructure( PRBool aEnd, + nsISupportsArray *aNodes, + nsIDOMNode *aListOrTable, + nsCOMPtr *outReplaceNode) +{ + NS_ENSURE_TRUE(aNodes, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aListOrTable, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(outReplaceNode, NS_ERROR_NULL_POINTER); + + *outReplaceNode = 0; + + // look upward from first/last paste node for a piece of this list/table + PRUint32 listCount, idx = 0; + aNodes->Count(&listCount); + if (aEnd) idx = listCount-1; + PRBool bList = nsHTMLEditUtils::IsList(aListOrTable); + + nsCOMPtr isup = dont_AddRef(aNodes->ElementAt(idx)); + nsCOMPtr pNode = do_QueryInterface(isup); + nsCOMPtr originalNode = pNode; + while (pNode) + { + if ( (bList && nsHTMLEditUtils::IsListItem(pNode)) || + (!bList && (nsHTMLEditUtils::IsTableElement(pNode) && !nsHTMLEditUtils::IsTable(pNode))) ) + { + nsCOMPtr structureNode; + if (bList) structureNode = GetListParent(pNode); + else structureNode = GetTableParent(pNode); + if (structureNode == aListOrTable) + { + if (pNode == originalNode) + break; // we are starting right off with a list item of the list + *outReplaceNode = pNode; + break; + } + } + nsCOMPtr parent; + pNode->GetParentNode(getter_AddRefs(parent)); + pNode = parent; + } + return NS_OK; +} + +nsresult +nsHTMLEditor::ReplaceOrphanedStructure(PRBool aEnd, + nsISupportsArray *aNodeArray, + nsISupportsArray *aListAndTableArray, + PRInt32 aHighWaterMark) +{ + NS_ENSURE_TRUE(aNodeArray, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aListAndTableArray, NS_ERROR_NULL_POINTER); + + nsCOMPtr isupports = dont_AddRef(aListAndTableArray->ElementAt(aHighWaterMark)); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + NS_ENSURE_TRUE(curNode, NS_ERROR_NULL_POINTER); + + nsCOMPtr replaceNode, originalNode, tmp; + + // find substructure of list or table that must be included in paste. + nsresult res = ScanForListAndTableStructure(aEnd, aNodeArray, + curNode, address_of(replaceNode)); + NS_ENSURE_SUCCESS(res, res); + + // if we found substructure, paste it instead of it's descendants + if (replaceNode) + { + // postprocess list to remove any descendants of this node + // so that we dont insert them twice. + do + { + isupports = GetArrayEndpoint(aEnd, aNodeArray); + if (!isupports) break; + tmp = do_QueryInterface(isupports); + if (tmp && nsHTMLEditUtils::IsDescendantOf(tmp, replaceNode)) + aNodeArray->RemoveElement(isupports); + else + break; + } while(tmp); + + // now replace the removed nodes with the structural parent + isupports = do_QueryInterface(replaceNode); + if (aEnd) aNodeArray->AppendElement(isupports); + else aNodeArray->InsertElementAt(isupports, 0); + } + return NS_OK; +} + +nsISupports* nsHTMLEditor::GetArrayEndpoint(PRBool aEnd, nsISupportsArray *aNodeArray) +{ + if (aEnd) + { + PRUint32 listCount; + aNodeArray->Count(&listCount); + if (listCount <= 0) return nsnull; + else return aNodeArray->ElementAt(listCount-1); + } + else + { + return aNodeArray->ElementAt(0); + } +} diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index 70efd18058d..ec9ac4dfaf9 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -1779,7 +1779,7 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe } #endif - res = InsertNodeAtPoint(node, parentSelectedNode, offsetForInsert, PR_FALSE); + res = InsertNodeAtPoint(node, address_of(parentSelectedNode), &offsetForInsert, PR_FALSE); NS_ENSURE_SUCCESS(res, res); // Set caret after element, but check for special case // of inserting table-related elements: set in first cell instead @@ -1792,23 +1792,39 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe return res; } + +/* + InsertNodeAtPoint: attempts to insert aNode into the document, at a point specified by + {*ioParent,*ioOffset}. Checks with strict dtd to see if containment is allowed. If not + allowed, will attempt to find a parent in the parent heirarchy of *ioParent that will + accept aNode as a child. If such a parent is found, will split the document tree from + {*ioParent,*ioOffset} up to parent, and then insert aNode. ioParent & ioOffset are then + adjusted to point to the actual location that aNode was inserted at. aNoEmptyNodes + specifies if the splitting process is allowed to reslt in empty nodes. + nsIDOMNode *aNode node to insert + nsCOMPtr *ioParent insertion parent + PRInt32 *ioOffset insertion offset + PRBool aNoEmptyNodes splitting can result in empty nodes? +*/ nsresult nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aOffset, + nsCOMPtr *ioParent, + PRInt32 *ioOffset, PRBool aNoEmptyNodes) { NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); - NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(ioParent, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(*ioParent, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(ioOffset, NS_ERROR_NULL_POINTER); nsresult res = NS_OK; nsAutoString tagName; aNode->GetNodeName(tagName); tagName.ToLowerCase(); - nsCOMPtr parent = aParent; - nsCOMPtr topChild = aParent; + nsCOMPtr parent = *ioParent; + nsCOMPtr topChild = *ioParent; nsCOMPtr tmp; - PRInt32 offsetOfInsert = aOffset; + PRInt32 offsetOfInsert = *ioOffset; // Search up the parent chain to find a suitable container while (!CanContainTag(parent, tagName)) @@ -1826,9 +1842,11 @@ nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, if (parent != topChild) { // we need to split some levels above the original selection parent - res = SplitNodeDeep(topChild, aParent, aOffset, &offsetOfInsert, aNoEmptyNodes); + res = SplitNodeDeep(topChild, *ioParent, *ioOffset, &offsetOfInsert, aNoEmptyNodes); if (NS_FAILED(res)) return res; + *ioParent = parent; + *ioOffset = offsetOfInsert; } // Now we can insert the new node res = InsertNode(aNode, parent, offsetOfInsert); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index 3758dd5b818..eced4d50cec 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -351,10 +351,10 @@ public: /* ------------ Utility Routines, not part of public API -------------- */ NS_IMETHOD TypedText(const nsAReadableString& aString, PRInt32 aAction); - nsresult InsertNodeAtPoint(nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aOffset, - PRBool aNoEmptyNodes); + nsresult InsertNodeAtPoint( nsIDOMNode *aNode, + nsCOMPtr *ioParent, + PRInt32 *ioOffset, + PRBool aNoEmptyNodes); /** returns the absolute position of the end points of aSelection @@ -568,6 +568,21 @@ protected: nsCOMPtr *outNodeList, PRInt32 aRangeStartHint, PRInt32 aRangeEndHint); + nsresult GetListAndTableParents( PRBool aEnd, + nsISupportsArray *aListOfNodes, + nsCOMPtr *outArray); + nsresult DiscoverPartialListsAndTables( nsISupportsArray *aPasteNodes, + nsISupportsArray *aListsAndTables, + PRInt32 *outHighWaterMark); + nsresult ScanForListAndTableStructure(PRBool aEnd, + nsISupportsArray *aNodes, + nsIDOMNode *aListOrTable, + nsCOMPtr *outReplaceNode); + nsresult ReplaceOrphanedStructure( PRBool aEnd, + nsISupportsArray *aNodeArray, + nsISupportsArray *aListAndTableArray, + PRInt32 aHighWaterMark); + nsISupports* GetArrayEndpoint(PRBool aEnd, nsISupportsArray *aNodeArray); /** simple utility to handle any error with event listener allocation or registration */ void HandleEventListenerError();