diff --git a/mozilla/editor/base/nsEditorUtils.h b/mozilla/editor/base/nsEditorUtils.h index d9e1aa4bf72..9f715271bb4 100644 --- a/mozilla/editor/base/nsEditorUtils.h +++ b/mozilla/editor/base/nsEditorUtils.h @@ -186,4 +186,14 @@ class nsDOMSubtreeIterator : public nsDOMIterator nsresult Init(nsIDOMNode* aNode); }; +class nsTrivialFunctor : public nsBoolDomIterFunctor +{ + public: + virtual PRBool operator()(nsIDOMNode* aNode) // used to build list of all nodes iterator covers + { + return PR_TRUE; + } +}; + + #endif // nsEditorUtils_h__ diff --git a/mozilla/editor/base/nsHTMLEditRules.cpp b/mozilla/editor/base/nsHTMLEditRules.cpp index 9b056664a71..6c722f3d411 100644 --- a/mozilla/editor/base/nsHTMLEditRules.cpp +++ b/mozilla/editor/base/nsHTMLEditRules.cpp @@ -69,15 +69,6 @@ enum ********************************************************/ -class nsTrivialFunctor : public nsBoolDomIterFunctor -{ - public: - virtual PRBool operator()(nsIDOMNode* aNode) // used to build list of all nodes iterator covers - { - return PR_TRUE; - } -}; - class nsTableCellAndListItemFunctor : public nsBoolDomIterFunctor { public: @@ -213,9 +204,8 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) } // make a utility range for use by the listenter - res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(mUtilRange)); - if (NS_FAILED(res)) return res; + mUtilRange = do_CreateInstance(kRangeCID); + if (!mUtilRange) return NS_ERROR_NULL_POINTER; // pass over document and add any needed mozBRs // first turn off undo @@ -230,10 +220,11 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) { // temporarily turn off rules sniffing nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this); - res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(mDocChangeRange)); - if (NS_FAILED(res)) return res; - if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; + if (!mDocChangeRange) + { + mDocChangeRange = do_CreateInstance(kRangeCID); + if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; + } mDocChangeRange->SelectNode(bodyNode); res = ReplaceNewlines(mDocChangeRange); if (NS_FAILED(res)) return res; @@ -260,11 +251,9 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) if (!mActionNesting) { - mDocChangeRange = nsnull; // clear out our accounting of what changed - // total hack to clear mUtilRange. This is because there is the - // unposition call is not available thru nsIDOMRange.idl - nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(mUtilRange)); + if (mDocChangeRange) mDocChangeRange->Detach(); // clear out our accounting of what changed + if (mUtilRange) mUtilRange->Detach(); // ditto for mUtilRange. + // turn off caret nsCOMPtr selCon; mEditor->GetSelectionController(getter_AddRefs(selCon)); @@ -312,7 +301,18 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; - if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) + // do we have a real range to act on? + PRBool bDamagedRange = PR_FALSE; + if (mDocChangeRange) + { + nsCOMPtr rangeStartParent, rangeEndParent; + mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartParent)); + mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndParent)); + if (rangeStartParent && rangeEndParent) + bDamagedRange = PR_TRUE; + } + + if (bDamagedRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) { // dont let any txns in here move the selection around behind our back. // Note that this won't prevent explicit selection setting from working. @@ -991,14 +991,15 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, // the correct portion of the document. if (!mDocChangeRange) { - res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(mDocChangeRange)); - if (NS_FAILED(res)) return res; - if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; + mDocChangeRange = do_CreateInstance(kRangeCID); + if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; } res = mDocChangeRange->SetStart(selNode, selOffset); if (NS_FAILED(res)) return res; - res = mDocChangeRange->SetEnd(curNode, curOffset); + if (curNode) + res = mDocChangeRange->SetEnd(curNode, curOffset); + else + res = mDocChangeRange->SetEnd(selNode, selOffset); if (NS_FAILED(res)) return res; } return res; @@ -5001,11 +5002,8 @@ nsHTMLEditRules::RemoveEmptyNodes() if (NS_FAILED(res)) return res; // need an iterator - res = nsComponentManager::CreateInstance(kContentIteratorCID, - nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(res)) return res; + iter = do_CreateInstance(kContentIteratorCID); + if (!iter) return NS_ERROR_NULL_POINTER; // loop over iter and create list of empty containers do diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp index c0086676c7c..f1c862ec680 100644 --- a/mozilla/editor/base/nsHTMLEditor.cpp +++ b/mozilla/editor/base/nsHTMLEditor.cpp @@ -108,11 +108,9 @@ const PRUnichar nbsp = 160; -// HACK - CID for NavDTD until we can get at dtd via the document -// {a6cf9107-15b3-11d2-932e-00805f8add32} -#define NS_CNAVDTD_CID \ -{ 0xa6cf9107, 0x15b3, 0x11d2, { 0x93, 0x2e, 0x0, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } } -static NS_DEFINE_CID(kCNavDTDCID, NS_CNAVDTD_CID); +// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd +#define NS_CTRANSITIONAL_DTD_CID \ +{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } } static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); @@ -124,12 +122,17 @@ static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); +static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); // Drag & Drop, Clipboard Support static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID); static NS_DEFINE_CID(kCXIFFormatConverterCID, NS_XIFFORMATCONVERTER_CID); +// private clipboard data flavors for html copy/paste +#define kHTMLContext "text/_moz_htmlcontext" +#define kHTMLInfo "text/_moz_htmlinfo" + #if defined(NS_DEBUG) && defined(DEBUG_buster) static PRBool gNoisy = PR_FALSE; @@ -435,7 +438,7 @@ NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, // Set up a DTD XXX XXX // HACK: This should have happened in a document specific way // in nsEditor::Init(), but we dont' have a way to do that yet - result = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull, + result = nsComponentManager::CreateInstance(kCTransitionalDTDCID, nsnull, NS_GET_IID(nsIDTD), getter_AddRefs(mDTD)); if (!mDTD) result = NS_ERROR_FAILURE; if (NS_FAILED(result)) return result; @@ -1079,9 +1082,6 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, if (NS_FAILED(res)) return res; if ((startNode == endNode) && IsTextNode(startNode)) { - // MOOSE: workaround for selection bug: - //selection->ClearSelection(); - PRInt32 startOffset, endOffset; range->GetStartOffset(&startOffset); range->GetEndOffset(&endOffset); @@ -1141,9 +1141,6 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, if (NS_FAILED(res)) return res; } } - // MOOSE: workaround for selection bug: - //selection->ClearSelection(); - // first check the start parent of the range to see if it needs to // be seperately handled (it does if it's a text node, due to how the // subtree iterator works - it will not have reported it). @@ -2427,8 +2424,23 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) return InsertHTMLWithCharset(aInputString, charset); } -NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, - const nsString& aCharset) +nsresult nsHTMLEditor::InsertHTMLWithContext(const nsString& aInputString, const nsString& aContextStr, const nsString& aInfoStr) +{ + nsAutoString charset; + return InsertHTMLWithCharsetAndContext(aInputString, charset, aContextStr, aInfoStr); +} + + +NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, const nsString& aCharset) +{ + return InsertHTMLWithCharsetAndContext(aInputString, aCharset, nsAutoString(), nsAutoString()); +} + + +nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsString& aInputString, + const nsString& aCharset, + const nsString& aContextStr, + const nsString& aInfoStr) { // First, make sure there are no return chars in the document. // Bad things happen if you insert returns (instead of dom newlines, \n) @@ -2486,12 +2498,13 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, if (IsTextNode(parentNode)) { nsCOMPtr temp; - res = SplitNode(parentNode, offsetOfNewNode, getter_AddRefs(temp)); + res = SplitNodeDeep(parentNode, parentNode, offsetOfNewNode, &offsetOfNewNode); if (NS_FAILED(res)) return res; - res = GetNodeLocation(parentNode, &temp, &offsetOfNewNode); + res = parentNode->GetParentNode(getter_AddRefs(temp)); if (NS_FAILED(res)) return res; parentNode = temp; } + // Get the first range in the selection, for context: nsCOMPtr range; res = selection->GetRangeAt(0, getter_AddRefs(range)); @@ -2502,70 +2515,236 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, if (!nsrange) return NS_ERROR_NO_INTERFACE; + // create a dom document fragment that represents the structure to paste nsCOMPtr docfrag; res = nsrange->CreateContextualFragment(inputString, getter_AddRefs(docfrag)); - if (NS_FAILED(res)) - { -#ifdef DEBUG - printf("Couldn't create contextual fragment: error was %d\n", res); -#endif - return res; - } - -#if defined(DEBUG_akkana_verbose) - printf("============ Fragment dump :===========\n"); - - nsCOMPtr fragc (do_QueryInterface(docfrag)); - if (!fragc) - printf("Couldn't get fragment is nsIContent\n"); - else - fragc->List(stdout); -#endif - - // Insert the contents of the document fragment: + NS_ENSURE_SUCCESS(res, res); nsCOMPtr fragmentAsNode (do_QueryInterface(docfrag)); + res = StripFormattingNodes(fragmentAsNode); + NS_ENSURE_SUCCESS(res, res); - // Loop over the contents of the fragment: - nsCOMPtr child; - res = fragmentAsNode->GetFirstChild(getter_AddRefs(child)); - if (NS_FAILED(res)) + // if we have context info, create a fragment for that too + nsCOMPtr contextfrag; + nsCOMPtr contextLeaf; + if (aContextStr.Length()) { - printf("GetFirstChild failed!\n"); - return res; + res = nsrange->CreateContextualFragment(aContextStr, + getter_AddRefs(contextfrag)); + NS_ENSURE_SUCCESS(res, res); + nsCOMPtr contextAsNode (do_QueryInterface(contextfrag)); + res = StripFormattingNodes(contextAsNode); + NS_ENSURE_SUCCESS(res, res); + // cache the deepest leaf in the context + nsCOMPtr tmp = contextAsNode; + while (tmp) + { + contextLeaf = tmp; + contextLeaf->GetFirstChild(getter_AddRefs(tmp)); + } } - while (child) - { -#if defined(DEBUG_akkana_verbose) - printf("About to try to insert this node:\n"); - nsCOMPtr nodec (do_QueryInterface(child)); - if (nodec) nodec->List(stdout); - printf("-----\n"); -#endif - // Get the next sibling before inserting the node; - // when we insert the node, it moves into the main doc tree - // so we'll no longer be able to get the siblings in the doc frag. - nsCOMPtr nextSib; - child->GetNextSibling(getter_AddRefs(nextSib)); - // Ignore the return value, we'll check child when we loop around again. + + // get the infoString contents + nsAutoString numstr1, numstr2; + PRInt32 err, sep, rangeStartHint, rangeEndHint; + sep = aInfoStr.FindChar((PRUnichar)','); + aInfoStr.Left(numstr1, sep); + aInfoStr.Mid(numstr2, sep+1, -1); + rangeStartHint = numstr1.ToInteger(&err); + rangeEndHint = numstr2.ToInteger(&err); - // Now we can insert the node. - res = InsertNode(child, parentNode, offsetOfNewNode++); - if (NS_FAILED(res)) - break; - child = nextSib; + // make a list of what nodes in docFrag we need to move + + // First off create a range over the portion of docFrag indicated by + // the range hints. + nsCOMPtr docFragRange; + docFragRange = do_CreateInstance(kCRangeCID); + nsCOMPtr startParent, endParent, tmp; + PRInt32 endOffset; + startParent = fragmentAsNode; + while (rangeStartHint > 0) + { + startParent->GetFirstChild(getter_AddRefs(tmp)); + startParent = tmp; + rangeStartHint--; + NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); + } + endParent = fragmentAsNode; + while (rangeEndHint > 0) + { + endParent->GetLastChild(getter_AddRefs(tmp)); + endParent = tmp; + rangeEndHint--; + NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE); + } + res = GetLengthOfDOMNode(endParent, (PRUint32&)endOffset); + NS_ENSURE_SUCCESS(res, res); + + res = docFragRange->SetStart(startParent, 0); + NS_ENSURE_SUCCESS(res, res); + res = docFragRange->SetEnd(endParent, endOffset); + NS_ENSURE_SUCCESS(res, res); + + // now use a subtree iterator over the range to create a list of nodes + nsTrivialFunctor functor; + nsDOMSubtreeIterator iter; + nsCOMPtr nodeList; + res = NS_NewISupportsArray(getter_AddRefs(nodeList)); + NS_ENSURE_SUCCESS(res, res); + res = iter.Init(docFragRange); + NS_ENSURE_SUCCESS(res, res); + res = iter.AppendList(functor, nodeList); + NS_ENSURE_SUCCESS(res, res); + + + // Loop over the node list: + nsCOMPtr lastInsertNode; + PRBool bDidInsert = PR_FALSE; + PRUint32 listCount, j; + nodeList->Count(&listCount); + for (j=0; j isupports = nodeList->ElementAt(j); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + nsCOMPtr origCurNode = curNode; + + NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(!nsHTMLEditUtils::IsBody(curNode), NS_ERROR_FAILURE); + + // try to insert + res = InsertNodeAtPoint(curNode, parentNode, offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + lastInsertNode = curNode; + } + + // assume failure means no legal parent in the document heirarchy. + // try again with the parent of curNode in the paste heirarchy. + nsCOMPtr parent, tmp; + while (NS_FAILED(res) && curNode) + { + res = GetPasteNodeParent(curNode, fragmentAsNode, contextLeaf, &parent); + if (NS_SUCCEEDED(res) && parent && !nsHTMLEditUtils::IsBody(parent)) + { + parent->CloneNode(PR_FALSE,getter_AddRefs(tmp)); + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); + + res = InsertNodeAtPoint(tmp, parentNode, offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + lastInsertNode = tmp; + res = InsertNodeAtPoint(curNode, tmp, 0, PR_TRUE); + // if this failed then we have a paste heirarchy that is + // not compatible with our dtd. This is where we should add + // some smarts to fix up common invalid html we allow in the + // browser. for now just stop trying to paste this node. + if (NS_FAILED(res)) break; + } + else + { + curNode = parent; + } + } + } + if (bDidInsert) + { + res = GetNodeLocation(origCurNode, &parentNode, &offsetOfNewNode); + NS_ENSURE_SUCCESS(res, res); + offsetOfNewNode++; + } } - if (NS_FAILED(res)) - return res; // Now collapse the selection to the end of what we just inserted: - selection->Collapse(parentNode, offsetOfNewNode); + if (lastInsertNode) + { + nsCOMPtr leafNode; + PRUint32 len; + res = GetLastEditableLeaf(lastInsertNode, &leafNode); + NS_ENSURE_SUCCESS(res, res); + res = GetLengthOfDOMNode(leafNode, len); + selection->Collapse(leafNode, (PRInt32)len); + } } res = mRules->DidDoAction(selection, &ruleInfo, res); return res; } +nsresult +nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode) +{ + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); + + // use a contnet iterator over the node + nsTrivialFunctor functor; + nsDOMIterator iter; + nsCOMPtr nodeList; + nsresult res = NS_NewISupportsArray(getter_AddRefs(nodeList)); + NS_ENSURE_SUCCESS(res, res); + res = iter.Init(aNode); + NS_ENSURE_SUCCESS(res, res); + res = iter.AppendList(functor, nodeList); + NS_ENSURE_SUCCESS(res, res); + + // now go throught the list and remove anything that isn't editable + PRBool bDidInsert = PR_FALSE; + PRUint32 listCount, j; + nodeList->Count(&listCount); + for (j=0; j isupports = nodeList->ElementAt(j); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); + nsCOMPtr content = do_QueryInterface(curNode); + if (IsEmptyTextContent(content)) + { + nsCOMPtr parent, ignored; + PRInt32 offset; + GetNodeLocation(curNode, &parent, &offset); + if (parent) + { + res = parent->RemoveChild(curNode, getter_AddRefs(ignored)); + NS_ENSURE_SUCCESS(res, res); + } + } + } + return res; +} + +nsresult +nsHTMLEditor::GetPasteNodeParent(nsIDOMNode *aNode, + nsIDOMNode *aFragAsNode, + nsIDOMNode *aContextLeaf, + nsCOMPtr *outParentNode) +{ + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aFragAsNode, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(outParentNode, NS_ERROR_NULL_POINTER); + if (aNode == aFragAsNode) + { + if (aContextLeaf) + *outParentNode = aContextLeaf; + else + *outParentNode = nsnull; + return NS_OK; + } + nsresult res = aNode->GetParentNode(getter_AddRefs(*outParentNode)); + NS_ENSURE_SUCCESS(res, res); + if (*outParentNode == aFragAsNode) + { + if (aContextLeaf) + *outParentNode = aContextLeaf; + else + *outParentNode = nsnull; + return NS_OK; + } + return NS_OK; +} + // This is mostly like InsertHTMLWithCharset, // but we can't use that because it is selection-based and // the rules code won't let us edit under the node @@ -2851,6 +3030,8 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe if (!aElement) return NS_ERROR_NULL_POINTER; + nsCOMPtr node = do_QueryInterface(aElement); + ForceCompositionEnd(); nsAutoEditBatch beginBatching(this); nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); @@ -2885,7 +3066,6 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe // Named Anchor is a special case, // We collapse to insert element BEFORE the selection // For all other tags, we insert AFTER the selection - nsCOMPtr node = do_QueryInterface(aElement); if (IsNamedAnchorNode(node)) { selection->CollapseToStart(); @@ -2909,54 +3089,9 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe printf(" Offset: %d\n", offsetForInsert); } #endif - nsAutoString tagName; - aElement->GetNodeName(tagName); - tagName.ToLowerCase(); - nsCOMPtr parent = parentSelectedNode; - nsCOMPtr topChild = parentSelectedNode; - nsCOMPtr tmp; - nsAutoString parentTagName; - PRBool isRoot; - - // Search up the parent chain to find a suitable container - while (!CanContainTag(parent, tagName)) - { - // If the current parent is a root (body or table cell) - // then go no further - we can't insert - parent->GetNodeName(parentTagName); - res = IsRootTag(parentTagName, isRoot); - if (!NS_SUCCEEDED(res) || isRoot) - return NS_ERROR_FAILURE; - // Get the next parent - parent->GetParentNode(getter_AddRefs(tmp)); - if (!tmp) - return NS_ERROR_FAILURE; - topChild = parent; - parent = tmp; - } -#ifdef DEBUG_cmanske - { - nsAutoString name; - parent->GetNodeName(name); - printf("Parent node to insert under: "); - wprintf(name.GetUnicode()); - printf("\n"); - topChild->GetNodeName(name); - printf("TopChild to split: "); - wprintf(name.GetUnicode()); - printf("\n"); - } -#endif - if (parent != topChild) - { - // we need to split some levels above the original selection parent - res = SplitNodeDeep(topChild, parentSelectedNode, offsetForInsert, &offsetForInsert); - if (NS_FAILED(res)) - return res; - } - // Now we can insert the new node - res = InsertNode(aElement, parent, offsetForInsert); + res = InsertNodeAtPoint(node, 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 if (!SetCaretInTableCell(aElement)) @@ -2968,6 +3103,53 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe return res; } +nsresult +nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRBool aNoEmptyNodes) +{ + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER); + + nsresult res = NS_OK; + nsAutoString tagName; + aNode->GetNodeName(tagName); + tagName.ToLowerCase(); + nsCOMPtr parent = aParent; + nsCOMPtr topChild = aParent; + nsCOMPtr tmp; + nsAutoString parentTagName; + PRBool isRoot; + PRInt32 offsetOfInsert = aOffset; + + // Search up the parent chain to find a suitable container + while (!CanContainTag(parent, tagName)) + { + // If the current parent is a root (body or table cell) + // then go no further - we can't insert + parent->GetNodeName(parentTagName); + res = IsRootTag(parentTagName, isRoot); + NS_ENSURE_SUCCESS(res, res); + NS_ENSURE_TRUE(!isRoot, NS_ERROR_FAILURE); + // Get the next parent + parent->GetParentNode(getter_AddRefs(tmp)); + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); + topChild = parent; + parent = tmp; + } + if (parent != topChild) + { + // we need to split some levels above the original selection parent + res = SplitNodeDeep(topChild, aParent, aOffset, &offsetOfInsert, aNoEmptyNodes); + if (NS_FAILED(res)) + return res; + } + // Now we can insert the new node + res = InsertNode(aNode, parent, offsetOfInsert); + return res; +} + // XXX: error handling in this routine needs to be cleaned up! NS_IMETHODIMP nsHTMLEditor::DeleteSelectionAndCreateNode(const nsString& aTag, @@ -4895,7 +5077,7 @@ NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable) return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable) +NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, PRInt32 aSelectionType) { nsresult rv = NS_OK; char* bestFlavor = nsnull; @@ -4904,8 +5086,7 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) ) { nsAutoTxnsConserveSelection dontSpazMySelection(this); - nsAutoString stuffToPaste; - nsAutoString flavor; + nsAutoString flavor, stuffToPaste; flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals() #ifdef DEBUG_akkana printf("Got flavor [%s]\n", bestFlavor); @@ -4915,11 +5096,48 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); if (textDataObj && len > 0) { + nsAutoString contextStr, infoStr; PRUnichar* text = nsnull; + textDataObj->ToString ( &text ); stuffToPaste.Assign ( text, len / 2 ); + + // also get additional html copy hints, if present + NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv ); + if ( NS_FAILED(rv) ) + return rv; + + nsCOMPtr contextDataObj, infoDataObj; + PRUint32 contextLen, infoLen; + + nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER); + contextTrans->AddDataFlavor(kHTMLContext); + clipboard->GetData(contextTrans, aSelectionType); + contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen); + + nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER); + infoTrans->AddDataFlavor(kHTMLInfo); + clipboard->GetData(infoTrans, aSelectionType); + infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen); + + if (contextDataObj) + { + textDataObj = do_QueryInterface(contextDataObj); + textDataObj->ToString ( &text ); + contextStr.Assign ( text, contextLen / 2 ); + } + + if (infoDataObj) + { + textDataObj = do_QueryInterface(infoDataObj); + textDataObj->ToString ( &text ); + infoStr.Assign ( text, infoLen / 2 ); + } + nsAutoEditBatch beginBatching(this); - rv = InsertHTML(stuffToPaste); + rv = InsertHTMLWithContext(stuffToPaste, contextStr, infoStr); } } else if (flavor.EqualsWithConversion(kUnicodeMime)) @@ -5347,7 +5565,7 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType) // Get the Data from the clipboard if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable()) { - rv = InsertFromTransferable(trans); + rv = InsertFromTransferable(trans, aSelectionType); } } @@ -6294,8 +6512,9 @@ nsHTMLEditor::EndOperation() PRBool nsHTMLEditor::TagCanContainTag(const nsString &aParentTag, const nsString &aChildTag) { - // CNavDTD gives some unwanted results. We override them here. + // COtherDTD gives some unwanted results. We override them here. +/* if ( aParentTag.EqualsWithConversion("ol") || aParentTag.EqualsWithConversion("ul") ) { @@ -6323,6 +6542,7 @@ nsHTMLEditor::TagCanContainTag(const nsString &aParentTag, const nsString &aChil if (!mDTD->IsInlineElement(childTagEnum,parentTagEnum)) return PR_FALSE; } + */ // else fall thru return nsEditor::TagCanContainTag(aParentTag, aChildTag); } @@ -6703,75 +6923,6 @@ nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parent } } } - - // I dont know what is up with this, but there is no reason to split - // any node we happen to be inserting into. The code below (ifdef'd out) - // breaks InsertBreak(). - - -#if 0 - - /* if the selection is not a text node, split the parent node if necesary - and compute where to put the new node - */ - else - { // it's an interior node - nsCOMPtrparentChildList; - parentSelectedNode->GetChildNodes(getter_AddRefs(parentChildList)); - if ((NS_SUCCEEDED(result)) && parentChildList) - { - result = parentChildList->Item(offsetOfSelectedNode, getter_AddRefs(selectedNode)); - if ((NS_SUCCEEDED(result)) && selectedNode) - { - nsCOMPtrselectedNodeAsText; - selectedNodeAsText = do_QueryInterface(selectedNode); - nsCOMPtrchildList; - //CM: I added "result =" - result = selectedNode->GetChildNodes(getter_AddRefs(childList)); - if (NS_SUCCEEDED(result)) - { - if (childList) - { - childList->GetLength(&selectedNodeContentCount); - } - else - { - // This is the case where the collapsed selection offset - // points to an inline node with no children - // This must also be where the new node should be inserted - // and there is no splitting necessary - offsetOfNewNode = offsetOfSelectedNode; - return NS_OK; - } - } - else - { - return NS_ERROR_FAILURE; - } - if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) - { - nsCOMPtr newSiblingNode; - result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); - // now get the node's offset in it's parent, and insert the new tag there - if (NS_SUCCEEDED(result)) { - result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - } - } - else - { // determine where to insert the new node - if (0==offsetOfSelectedNode) { - offsetOfNewNode = 0; // insert new node as first child - } - else { // insert new node as last child - GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node - } - } - } - } - } -#endif - // Here's where the new node was inserted } else { @@ -7041,9 +7192,6 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) if (NS_FAILED(res)) return res; if ((startNode == endNode) && IsTextNode(startNode)) { - // MOOSE: workaround for selection bug: - //selection->ClearSelection(); - PRInt32 startOffset, endOffset; range->GetStartOffset(&startOffset); range->GetEndOffset(&endOffset); @@ -7098,9 +7246,6 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) iter->Next(); } - // MOOSE: workaround for selection bug: - //selection->ClearSelection(); - // now that we have the list, do the font size change on each node PRUint32 listCount; PRUint32 j; diff --git a/mozilla/editor/base/nsHTMLEditor.h b/mozilla/editor/base/nsHTMLEditor.h index d1d0f427c75..c5a0187349e 100644 --- a/mozilla/editor/base/nsHTMLEditor.h +++ b/mozilla/editor/base/nsHTMLEditor.h @@ -307,6 +307,11 @@ public: NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify); /* ------------ Utility Routines, not part of public API -------------- */ + nsresult InsertNodeAtPoint(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRBool aNoEmptyNodes); + NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext); /** returns the absolute position of the end points of aSelection @@ -499,8 +504,21 @@ protected: // factored methods for handling insertion of data from transferables (drag&drop or clipboard) NS_IMETHOD PrepareTransferable(nsITransferable **transferable); - NS_IMETHOD InsertFromTransferable(nsITransferable *transferable); - + NS_IMETHOD InsertFromTransferable(nsITransferable *transferable, + PRInt32 aSelectionType = nsnull); + nsresult InsertHTMLWithContext(const nsString& aInputString, + const nsString& aContextStr, + const nsString& aInfoStr); + nsresult InsertHTMLWithCharsetAndContext(const nsString& aInputString, + const nsString& aCharset, + const nsString& aContextStr, + const nsString& aInfoStr); + nsresult StripFormattingNodes(nsIDOMNode *aNode); + nsresult GetPasteNodeParent(nsIDOMNode *aNode, + nsIDOMNode *aFragAsNode, + nsIDOMNode *aContextLeaf, + nsCOMPtr *outParentNode); + /** simple utility to handle any error with event listener allocation or registration */ void HandleEventListenerError(); diff --git a/mozilla/editor/libeditor/base/nsEditorUtils.h b/mozilla/editor/libeditor/base/nsEditorUtils.h index d9e1aa4bf72..9f715271bb4 100644 --- a/mozilla/editor/libeditor/base/nsEditorUtils.h +++ b/mozilla/editor/libeditor/base/nsEditorUtils.h @@ -186,4 +186,14 @@ class nsDOMSubtreeIterator : public nsDOMIterator nsresult Init(nsIDOMNode* aNode); }; +class nsTrivialFunctor : public nsBoolDomIterFunctor +{ + public: + virtual PRBool operator()(nsIDOMNode* aNode) // used to build list of all nodes iterator covers + { + return PR_TRUE; + } +}; + + #endif // nsEditorUtils_h__ diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp index 9b056664a71..6c722f3d411 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp @@ -69,15 +69,6 @@ enum ********************************************************/ -class nsTrivialFunctor : public nsBoolDomIterFunctor -{ - public: - virtual PRBool operator()(nsIDOMNode* aNode) // used to build list of all nodes iterator covers - { - return PR_TRUE; - } -}; - class nsTableCellAndListItemFunctor : public nsBoolDomIterFunctor { public: @@ -213,9 +204,8 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) } // make a utility range for use by the listenter - res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(mUtilRange)); - if (NS_FAILED(res)) return res; + mUtilRange = do_CreateInstance(kRangeCID); + if (!mUtilRange) return NS_ERROR_NULL_POINTER; // pass over document and add any needed mozBRs // first turn off undo @@ -230,10 +220,11 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) { // temporarily turn off rules sniffing nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this); - res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(mDocChangeRange)); - if (NS_FAILED(res)) return res; - if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; + if (!mDocChangeRange) + { + mDocChangeRange = do_CreateInstance(kRangeCID); + if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; + } mDocChangeRange->SelectNode(bodyNode); res = ReplaceNewlines(mDocChangeRange); if (NS_FAILED(res)) return res; @@ -260,11 +251,9 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) if (!mActionNesting) { - mDocChangeRange = nsnull; // clear out our accounting of what changed - // total hack to clear mUtilRange. This is because there is the - // unposition call is not available thru nsIDOMRange.idl - nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(mUtilRange)); + if (mDocChangeRange) mDocChangeRange->Detach(); // clear out our accounting of what changed + if (mUtilRange) mUtilRange->Detach(); // ditto for mUtilRange. + // turn off caret nsCOMPtr selCon; mEditor->GetSelectionController(getter_AddRefs(selCon)); @@ -312,7 +301,18 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(res)) return res; - if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) + // do we have a real range to act on? + PRBool bDamagedRange = PR_FALSE; + if (mDocChangeRange) + { + nsCOMPtr rangeStartParent, rangeEndParent; + mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartParent)); + mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndParent)); + if (rangeStartParent && rangeEndParent) + bDamagedRange = PR_TRUE; + } + + if (bDamagedRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) { // dont let any txns in here move the selection around behind our back. // Note that this won't prevent explicit selection setting from working. @@ -991,14 +991,15 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, // the correct portion of the document. if (!mDocChangeRange) { - res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), - getter_AddRefs(mDocChangeRange)); - if (NS_FAILED(res)) return res; - if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; + mDocChangeRange = do_CreateInstance(kRangeCID); + if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; } res = mDocChangeRange->SetStart(selNode, selOffset); if (NS_FAILED(res)) return res; - res = mDocChangeRange->SetEnd(curNode, curOffset); + if (curNode) + res = mDocChangeRange->SetEnd(curNode, curOffset); + else + res = mDocChangeRange->SetEnd(selNode, selOffset); if (NS_FAILED(res)) return res; } return res; @@ -5001,11 +5002,8 @@ nsHTMLEditRules::RemoveEmptyNodes() if (NS_FAILED(res)) return res; // need an iterator - res = nsComponentManager::CreateInstance(kContentIteratorCID, - nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - if (NS_FAILED(res)) return res; + iter = do_CreateInstance(kContentIteratorCID); + if (!iter) return NS_ERROR_NULL_POINTER; // loop over iter and create list of empty containers do diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index c0086676c7c..f1c862ec680 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -108,11 +108,9 @@ const PRUnichar nbsp = 160; -// HACK - CID for NavDTD until we can get at dtd via the document -// {a6cf9107-15b3-11d2-932e-00805f8add32} -#define NS_CNAVDTD_CID \ -{ 0xa6cf9107, 0x15b3, 0x11d2, { 0x93, 0x2e, 0x0, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } } -static NS_DEFINE_CID(kCNavDTDCID, NS_CNAVDTD_CID); +// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd +#define NS_CTRANSITIONAL_DTD_CID \ +{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } } static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID); @@ -124,12 +122,17 @@ static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID); static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID); static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID); static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID); +static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID); // Drag & Drop, Clipboard Support static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID); static NS_DEFINE_CID(kCXIFFormatConverterCID, NS_XIFFORMATCONVERTER_CID); +// private clipboard data flavors for html copy/paste +#define kHTMLContext "text/_moz_htmlcontext" +#define kHTMLInfo "text/_moz_htmlinfo" + #if defined(NS_DEBUG) && defined(DEBUG_buster) static PRBool gNoisy = PR_FALSE; @@ -435,7 +438,7 @@ NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, // Set up a DTD XXX XXX // HACK: This should have happened in a document specific way // in nsEditor::Init(), but we dont' have a way to do that yet - result = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull, + result = nsComponentManager::CreateInstance(kCTransitionalDTDCID, nsnull, NS_GET_IID(nsIDTD), getter_AddRefs(mDTD)); if (!mDTD) result = NS_ERROR_FAILURE; if (NS_FAILED(result)) return result; @@ -1079,9 +1082,6 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, if (NS_FAILED(res)) return res; if ((startNode == endNode) && IsTextNode(startNode)) { - // MOOSE: workaround for selection bug: - //selection->ClearSelection(); - PRInt32 startOffset, endOffset; range->GetStartOffset(&startOffset); range->GetEndOffset(&endOffset); @@ -1141,9 +1141,6 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, if (NS_FAILED(res)) return res; } } - // MOOSE: workaround for selection bug: - //selection->ClearSelection(); - // first check the start parent of the range to see if it needs to // be seperately handled (it does if it's a text node, due to how the // subtree iterator works - it will not have reported it). @@ -2427,8 +2424,23 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) return InsertHTMLWithCharset(aInputString, charset); } -NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, - const nsString& aCharset) +nsresult nsHTMLEditor::InsertHTMLWithContext(const nsString& aInputString, const nsString& aContextStr, const nsString& aInfoStr) +{ + nsAutoString charset; + return InsertHTMLWithCharsetAndContext(aInputString, charset, aContextStr, aInfoStr); +} + + +NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, const nsString& aCharset) +{ + return InsertHTMLWithCharsetAndContext(aInputString, aCharset, nsAutoString(), nsAutoString()); +} + + +nsresult nsHTMLEditor::InsertHTMLWithCharsetAndContext(const nsString& aInputString, + const nsString& aCharset, + const nsString& aContextStr, + const nsString& aInfoStr) { // First, make sure there are no return chars in the document. // Bad things happen if you insert returns (instead of dom newlines, \n) @@ -2486,12 +2498,13 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, if (IsTextNode(parentNode)) { nsCOMPtr temp; - res = SplitNode(parentNode, offsetOfNewNode, getter_AddRefs(temp)); + res = SplitNodeDeep(parentNode, parentNode, offsetOfNewNode, &offsetOfNewNode); if (NS_FAILED(res)) return res; - res = GetNodeLocation(parentNode, &temp, &offsetOfNewNode); + res = parentNode->GetParentNode(getter_AddRefs(temp)); if (NS_FAILED(res)) return res; parentNode = temp; } + // Get the first range in the selection, for context: nsCOMPtr range; res = selection->GetRangeAt(0, getter_AddRefs(range)); @@ -2502,70 +2515,236 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, if (!nsrange) return NS_ERROR_NO_INTERFACE; + // create a dom document fragment that represents the structure to paste nsCOMPtr docfrag; res = nsrange->CreateContextualFragment(inputString, getter_AddRefs(docfrag)); - if (NS_FAILED(res)) - { -#ifdef DEBUG - printf("Couldn't create contextual fragment: error was %d\n", res); -#endif - return res; - } - -#if defined(DEBUG_akkana_verbose) - printf("============ Fragment dump :===========\n"); - - nsCOMPtr fragc (do_QueryInterface(docfrag)); - if (!fragc) - printf("Couldn't get fragment is nsIContent\n"); - else - fragc->List(stdout); -#endif - - // Insert the contents of the document fragment: + NS_ENSURE_SUCCESS(res, res); nsCOMPtr fragmentAsNode (do_QueryInterface(docfrag)); + res = StripFormattingNodes(fragmentAsNode); + NS_ENSURE_SUCCESS(res, res); - // Loop over the contents of the fragment: - nsCOMPtr child; - res = fragmentAsNode->GetFirstChild(getter_AddRefs(child)); - if (NS_FAILED(res)) + // if we have context info, create a fragment for that too + nsCOMPtr contextfrag; + nsCOMPtr contextLeaf; + if (aContextStr.Length()) { - printf("GetFirstChild failed!\n"); - return res; + res = nsrange->CreateContextualFragment(aContextStr, + getter_AddRefs(contextfrag)); + NS_ENSURE_SUCCESS(res, res); + nsCOMPtr contextAsNode (do_QueryInterface(contextfrag)); + res = StripFormattingNodes(contextAsNode); + NS_ENSURE_SUCCESS(res, res); + // cache the deepest leaf in the context + nsCOMPtr tmp = contextAsNode; + while (tmp) + { + contextLeaf = tmp; + contextLeaf->GetFirstChild(getter_AddRefs(tmp)); + } } - while (child) - { -#if defined(DEBUG_akkana_verbose) - printf("About to try to insert this node:\n"); - nsCOMPtr nodec (do_QueryInterface(child)); - if (nodec) nodec->List(stdout); - printf("-----\n"); -#endif - // Get the next sibling before inserting the node; - // when we insert the node, it moves into the main doc tree - // so we'll no longer be able to get the siblings in the doc frag. - nsCOMPtr nextSib; - child->GetNextSibling(getter_AddRefs(nextSib)); - // Ignore the return value, we'll check child when we loop around again. + + // get the infoString contents + nsAutoString numstr1, numstr2; + PRInt32 err, sep, rangeStartHint, rangeEndHint; + sep = aInfoStr.FindChar((PRUnichar)','); + aInfoStr.Left(numstr1, sep); + aInfoStr.Mid(numstr2, sep+1, -1); + rangeStartHint = numstr1.ToInteger(&err); + rangeEndHint = numstr2.ToInteger(&err); - // Now we can insert the node. - res = InsertNode(child, parentNode, offsetOfNewNode++); - if (NS_FAILED(res)) - break; - child = nextSib; + // make a list of what nodes in docFrag we need to move + + // First off create a range over the portion of docFrag indicated by + // the range hints. + nsCOMPtr docFragRange; + docFragRange = do_CreateInstance(kCRangeCID); + nsCOMPtr startParent, endParent, tmp; + PRInt32 endOffset; + startParent = fragmentAsNode; + while (rangeStartHint > 0) + { + startParent->GetFirstChild(getter_AddRefs(tmp)); + startParent = tmp; + rangeStartHint--; + NS_ENSURE_TRUE(startParent, NS_ERROR_FAILURE); + } + endParent = fragmentAsNode; + while (rangeEndHint > 0) + { + endParent->GetLastChild(getter_AddRefs(tmp)); + endParent = tmp; + rangeEndHint--; + NS_ENSURE_TRUE(endParent, NS_ERROR_FAILURE); + } + res = GetLengthOfDOMNode(endParent, (PRUint32&)endOffset); + NS_ENSURE_SUCCESS(res, res); + + res = docFragRange->SetStart(startParent, 0); + NS_ENSURE_SUCCESS(res, res); + res = docFragRange->SetEnd(endParent, endOffset); + NS_ENSURE_SUCCESS(res, res); + + // now use a subtree iterator over the range to create a list of nodes + nsTrivialFunctor functor; + nsDOMSubtreeIterator iter; + nsCOMPtr nodeList; + res = NS_NewISupportsArray(getter_AddRefs(nodeList)); + NS_ENSURE_SUCCESS(res, res); + res = iter.Init(docFragRange); + NS_ENSURE_SUCCESS(res, res); + res = iter.AppendList(functor, nodeList); + NS_ENSURE_SUCCESS(res, res); + + + // Loop over the node list: + nsCOMPtr lastInsertNode; + PRBool bDidInsert = PR_FALSE; + PRUint32 listCount, j; + nodeList->Count(&listCount); + for (j=0; j isupports = nodeList->ElementAt(j); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + nsCOMPtr origCurNode = curNode; + + NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(!nsHTMLEditUtils::IsBody(curNode), NS_ERROR_FAILURE); + + // try to insert + res = InsertNodeAtPoint(curNode, parentNode, offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + lastInsertNode = curNode; + } + + // assume failure means no legal parent in the document heirarchy. + // try again with the parent of curNode in the paste heirarchy. + nsCOMPtr parent, tmp; + while (NS_FAILED(res) && curNode) + { + res = GetPasteNodeParent(curNode, fragmentAsNode, contextLeaf, &parent); + if (NS_SUCCEEDED(res) && parent && !nsHTMLEditUtils::IsBody(parent)) + { + parent->CloneNode(PR_FALSE,getter_AddRefs(tmp)); + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); + + res = InsertNodeAtPoint(tmp, parentNode, offsetOfNewNode, PR_TRUE); + if (NS_SUCCEEDED(res)) + { + bDidInsert = PR_TRUE; + lastInsertNode = tmp; + res = InsertNodeAtPoint(curNode, tmp, 0, PR_TRUE); + // if this failed then we have a paste heirarchy that is + // not compatible with our dtd. This is where we should add + // some smarts to fix up common invalid html we allow in the + // browser. for now just stop trying to paste this node. + if (NS_FAILED(res)) break; + } + else + { + curNode = parent; + } + } + } + if (bDidInsert) + { + res = GetNodeLocation(origCurNode, &parentNode, &offsetOfNewNode); + NS_ENSURE_SUCCESS(res, res); + offsetOfNewNode++; + } } - if (NS_FAILED(res)) - return res; // Now collapse the selection to the end of what we just inserted: - selection->Collapse(parentNode, offsetOfNewNode); + if (lastInsertNode) + { + nsCOMPtr leafNode; + PRUint32 len; + res = GetLastEditableLeaf(lastInsertNode, &leafNode); + NS_ENSURE_SUCCESS(res, res); + res = GetLengthOfDOMNode(leafNode, len); + selection->Collapse(leafNode, (PRInt32)len); + } } res = mRules->DidDoAction(selection, &ruleInfo, res); return res; } +nsresult +nsHTMLEditor::StripFormattingNodes(nsIDOMNode *aNode) +{ + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); + + // use a contnet iterator over the node + nsTrivialFunctor functor; + nsDOMIterator iter; + nsCOMPtr nodeList; + nsresult res = NS_NewISupportsArray(getter_AddRefs(nodeList)); + NS_ENSURE_SUCCESS(res, res); + res = iter.Init(aNode); + NS_ENSURE_SUCCESS(res, res); + res = iter.AppendList(functor, nodeList); + NS_ENSURE_SUCCESS(res, res); + + // now go throught the list and remove anything that isn't editable + PRBool bDidInsert = PR_FALSE; + PRUint32 listCount, j; + nodeList->Count(&listCount); + for (j=0; j isupports = nodeList->ElementAt(j); + nsCOMPtr curNode( do_QueryInterface(isupports) ); + + NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE); + nsCOMPtr content = do_QueryInterface(curNode); + if (IsEmptyTextContent(content)) + { + nsCOMPtr parent, ignored; + PRInt32 offset; + GetNodeLocation(curNode, &parent, &offset); + if (parent) + { + res = parent->RemoveChild(curNode, getter_AddRefs(ignored)); + NS_ENSURE_SUCCESS(res, res); + } + } + } + return res; +} + +nsresult +nsHTMLEditor::GetPasteNodeParent(nsIDOMNode *aNode, + nsIDOMNode *aFragAsNode, + nsIDOMNode *aContextLeaf, + nsCOMPtr *outParentNode) +{ + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aFragAsNode, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(outParentNode, NS_ERROR_NULL_POINTER); + if (aNode == aFragAsNode) + { + if (aContextLeaf) + *outParentNode = aContextLeaf; + else + *outParentNode = nsnull; + return NS_OK; + } + nsresult res = aNode->GetParentNode(getter_AddRefs(*outParentNode)); + NS_ENSURE_SUCCESS(res, res); + if (*outParentNode == aFragAsNode) + { + if (aContextLeaf) + *outParentNode = aContextLeaf; + else + *outParentNode = nsnull; + return NS_OK; + } + return NS_OK; +} + // This is mostly like InsertHTMLWithCharset, // but we can't use that because it is selection-based and // the rules code won't let us edit under the node @@ -2851,6 +3030,8 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe if (!aElement) return NS_ERROR_NULL_POINTER; + nsCOMPtr node = do_QueryInterface(aElement); + ForceCompositionEnd(); nsAutoEditBatch beginBatching(this); nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); @@ -2885,7 +3066,6 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe // Named Anchor is a special case, // We collapse to insert element BEFORE the selection // For all other tags, we insert AFTER the selection - nsCOMPtr node = do_QueryInterface(aElement); if (IsNamedAnchorNode(node)) { selection->CollapseToStart(); @@ -2909,54 +3089,9 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe printf(" Offset: %d\n", offsetForInsert); } #endif - nsAutoString tagName; - aElement->GetNodeName(tagName); - tagName.ToLowerCase(); - nsCOMPtr parent = parentSelectedNode; - nsCOMPtr topChild = parentSelectedNode; - nsCOMPtr tmp; - nsAutoString parentTagName; - PRBool isRoot; - - // Search up the parent chain to find a suitable container - while (!CanContainTag(parent, tagName)) - { - // If the current parent is a root (body or table cell) - // then go no further - we can't insert - parent->GetNodeName(parentTagName); - res = IsRootTag(parentTagName, isRoot); - if (!NS_SUCCEEDED(res) || isRoot) - return NS_ERROR_FAILURE; - // Get the next parent - parent->GetParentNode(getter_AddRefs(tmp)); - if (!tmp) - return NS_ERROR_FAILURE; - topChild = parent; - parent = tmp; - } -#ifdef DEBUG_cmanske - { - nsAutoString name; - parent->GetNodeName(name); - printf("Parent node to insert under: "); - wprintf(name.GetUnicode()); - printf("\n"); - topChild->GetNodeName(name); - printf("TopChild to split: "); - wprintf(name.GetUnicode()); - printf("\n"); - } -#endif - if (parent != topChild) - { - // we need to split some levels above the original selection parent - res = SplitNodeDeep(topChild, parentSelectedNode, offsetForInsert, &offsetForInsert); - if (NS_FAILED(res)) - return res; - } - // Now we can insert the new node - res = InsertNode(aElement, parent, offsetForInsert); + res = InsertNodeAtPoint(node, 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 if (!SetCaretInTableCell(aElement)) @@ -2968,6 +3103,53 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe return res; } +nsresult +nsHTMLEditor::InsertNodeAtPoint(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRBool aNoEmptyNodes) +{ + NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aParent, NS_ERROR_NULL_POINTER); + + nsresult res = NS_OK; + nsAutoString tagName; + aNode->GetNodeName(tagName); + tagName.ToLowerCase(); + nsCOMPtr parent = aParent; + nsCOMPtr topChild = aParent; + nsCOMPtr tmp; + nsAutoString parentTagName; + PRBool isRoot; + PRInt32 offsetOfInsert = aOffset; + + // Search up the parent chain to find a suitable container + while (!CanContainTag(parent, tagName)) + { + // If the current parent is a root (body or table cell) + // then go no further - we can't insert + parent->GetNodeName(parentTagName); + res = IsRootTag(parentTagName, isRoot); + NS_ENSURE_SUCCESS(res, res); + NS_ENSURE_TRUE(!isRoot, NS_ERROR_FAILURE); + // Get the next parent + parent->GetParentNode(getter_AddRefs(tmp)); + NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE); + topChild = parent; + parent = tmp; + } + if (parent != topChild) + { + // we need to split some levels above the original selection parent + res = SplitNodeDeep(topChild, aParent, aOffset, &offsetOfInsert, aNoEmptyNodes); + if (NS_FAILED(res)) + return res; + } + // Now we can insert the new node + res = InsertNode(aNode, parent, offsetOfInsert); + return res; +} + // XXX: error handling in this routine needs to be cleaned up! NS_IMETHODIMP nsHTMLEditor::DeleteSelectionAndCreateNode(const nsString& aTag, @@ -4895,7 +5077,7 @@ NS_IMETHODIMP nsHTMLEditor::PrepareTransferable(nsITransferable **transferable) return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable) +NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable, PRInt32 aSelectionType) { nsresult rv = NS_OK; char* bestFlavor = nsnull; @@ -4904,8 +5086,7 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) ) { nsAutoTxnsConserveSelection dontSpazMySelection(this); - nsAutoString stuffToPaste; - nsAutoString flavor; + nsAutoString flavor, stuffToPaste; flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals() #ifdef DEBUG_akkana printf("Got flavor [%s]\n", bestFlavor); @@ -4915,11 +5096,48 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromTransferable(nsITransferable *transferable nsCOMPtr textDataObj ( do_QueryInterface(genericDataObj) ); if (textDataObj && len > 0) { + nsAutoString contextStr, infoStr; PRUnichar* text = nsnull; + textDataObj->ToString ( &text ); stuffToPaste.Assign ( text, len / 2 ); + + // also get additional html copy hints, if present + NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv ); + if ( NS_FAILED(rv) ) + return rv; + + nsCOMPtr contextDataObj, infoDataObj; + PRUint32 contextLen, infoLen; + + nsCOMPtr contextTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(contextTrans, NS_ERROR_NULL_POINTER); + contextTrans->AddDataFlavor(kHTMLContext); + clipboard->GetData(contextTrans, aSelectionType); + contextTrans->GetTransferData(kHTMLContext, getter_AddRefs(contextDataObj), &contextLen); + + nsCOMPtr infoTrans = do_CreateInstance(kCTransferableCID); + NS_ENSURE_TRUE(infoTrans, NS_ERROR_NULL_POINTER); + infoTrans->AddDataFlavor(kHTMLInfo); + clipboard->GetData(infoTrans, aSelectionType); + infoTrans->GetTransferData(kHTMLInfo, getter_AddRefs(infoDataObj), &infoLen); + + if (contextDataObj) + { + textDataObj = do_QueryInterface(contextDataObj); + textDataObj->ToString ( &text ); + contextStr.Assign ( text, contextLen / 2 ); + } + + if (infoDataObj) + { + textDataObj = do_QueryInterface(infoDataObj); + textDataObj->ToString ( &text ); + infoStr.Assign ( text, infoLen / 2 ); + } + nsAutoEditBatch beginBatching(this); - rv = InsertHTML(stuffToPaste); + rv = InsertHTMLWithContext(stuffToPaste, contextStr, infoStr); } } else if (flavor.EqualsWithConversion(kUnicodeMime)) @@ -5347,7 +5565,7 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt32 aSelectionType) // Get the Data from the clipboard if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable()) { - rv = InsertFromTransferable(trans); + rv = InsertFromTransferable(trans, aSelectionType); } } @@ -6294,8 +6512,9 @@ nsHTMLEditor::EndOperation() PRBool nsHTMLEditor::TagCanContainTag(const nsString &aParentTag, const nsString &aChildTag) { - // CNavDTD gives some unwanted results. We override them here. + // COtherDTD gives some unwanted results. We override them here. +/* if ( aParentTag.EqualsWithConversion("ol") || aParentTag.EqualsWithConversion("ul") ) { @@ -6323,6 +6542,7 @@ nsHTMLEditor::TagCanContainTag(const nsString &aParentTag, const nsString &aChil if (!mDTD->IsInlineElement(childTagEnum,parentTagEnum)) return PR_FALSE; } + */ // else fall thru return nsEditor::TagCanContainTag(aParentTag, aChildTag); } @@ -6703,75 +6923,6 @@ nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parent } } } - - // I dont know what is up with this, but there is no reason to split - // any node we happen to be inserting into. The code below (ifdef'd out) - // breaks InsertBreak(). - - -#if 0 - - /* if the selection is not a text node, split the parent node if necesary - and compute where to put the new node - */ - else - { // it's an interior node - nsCOMPtrparentChildList; - parentSelectedNode->GetChildNodes(getter_AddRefs(parentChildList)); - if ((NS_SUCCEEDED(result)) && parentChildList) - { - result = parentChildList->Item(offsetOfSelectedNode, getter_AddRefs(selectedNode)); - if ((NS_SUCCEEDED(result)) && selectedNode) - { - nsCOMPtrselectedNodeAsText; - selectedNodeAsText = do_QueryInterface(selectedNode); - nsCOMPtrchildList; - //CM: I added "result =" - result = selectedNode->GetChildNodes(getter_AddRefs(childList)); - if (NS_SUCCEEDED(result)) - { - if (childList) - { - childList->GetLength(&selectedNodeContentCount); - } - else - { - // This is the case where the collapsed selection offset - // points to an inline node with no children - // This must also be where the new node should be inserted - // and there is no splitting necessary - offsetOfNewNode = offsetOfSelectedNode; - return NS_OK; - } - } - else - { - return NS_ERROR_FAILURE; - } - if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount)) - { - nsCOMPtr newSiblingNode; - result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode)); - // now get the node's offset in it's parent, and insert the new tag there - if (NS_SUCCEEDED(result)) { - result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - } - } - else - { // determine where to insert the new node - if (0==offsetOfSelectedNode) { - offsetOfNewNode = 0; // insert new node as first child - } - else { // insert new node as last child - GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode); - offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node - } - } - } - } - } -#endif - // Here's where the new node was inserted } else { @@ -7041,9 +7192,6 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) if (NS_FAILED(res)) return res; if ((startNode == endNode) && IsTextNode(startNode)) { - // MOOSE: workaround for selection bug: - //selection->ClearSelection(); - PRInt32 startOffset, endOffset; range->GetStartOffset(&startOffset); range->GetEndOffset(&endOffset); @@ -7098,9 +7246,6 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) iter->Next(); } - // MOOSE: workaround for selection bug: - //selection->ClearSelection(); - // now that we have the list, do the font size change on each node PRUint32 listCount; PRUint32 j; diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index d1d0f427c75..c5a0187349e 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -307,6 +307,11 @@ public: NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify); /* ------------ Utility Routines, not part of public API -------------- */ + nsresult InsertNodeAtPoint(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRBool aNoEmptyNodes); + NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext); /** returns the absolute position of the end points of aSelection @@ -499,8 +504,21 @@ protected: // factored methods for handling insertion of data from transferables (drag&drop or clipboard) NS_IMETHOD PrepareTransferable(nsITransferable **transferable); - NS_IMETHOD InsertFromTransferable(nsITransferable *transferable); - + NS_IMETHOD InsertFromTransferable(nsITransferable *transferable, + PRInt32 aSelectionType = nsnull); + nsresult InsertHTMLWithContext(const nsString& aInputString, + const nsString& aContextStr, + const nsString& aInfoStr); + nsresult InsertHTMLWithCharsetAndContext(const nsString& aInputString, + const nsString& aCharset, + const nsString& aContextStr, + const nsString& aInfoStr); + nsresult StripFormattingNodes(nsIDOMNode *aNode); + nsresult GetPasteNodeParent(nsIDOMNode *aNode, + nsIDOMNode *aFragAsNode, + nsIDOMNode *aContextLeaf, + nsCOMPtr *outParentNode); + /** simple utility to handle any error with event listener allocation or registration */ void HandleEventListenerError();