diff --git a/mozilla/editor/base/nsEditorShell.cpp b/mozilla/editor/base/nsEditorShell.cpp index 5556317951e..29ee2ed691b 100644 --- a/mozilla/editor/base/nsEditorShell.cpp +++ b/mozilla/editor/base/nsEditorShell.cpp @@ -2761,6 +2761,31 @@ nsEditorShell::InsertSourceWithCharset(const PRUnichar *aSourceToInsert, return err; } +NS_IMETHODIMP +nsEditorShell::RebuildDocumentFromSource(const PRUnichar *aSource) +{ + nsresult err = NS_NOINTERFACE; + + nsAutoString source(aSource); + + switch (mEditorType) + { + case ePlainTextEditorType: + case eHTMLTextEditorType: + { + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) + err = htmlEditor->RebuildDocumentFromSource(source); + } + break; + + default: + err = NS_NOINTERFACE; + } + + return err; +} + NS_IMETHODIMP nsEditorShell::InsertBreak() { diff --git a/mozilla/editor/base/nsEditorShellMouseListener.cpp b/mozilla/editor/base/nsEditorShellMouseListener.cpp index f76f91a9bcb..f09b1c4693f 100644 --- a/mozilla/editor/base/nsEditorShellMouseListener.cpp +++ b/mozilla/editor/base/nsEditorShellMouseListener.cpp @@ -165,6 +165,25 @@ nsEditorShellMouseListener::MouseDown(nsIDOMEvent* aMouseEvent) nsresult res = mouseEvent->GetButton(&buttonNumber); if (NS_FAILED(res)) return res; + PRBool isContextClick; + // Test if special 'table selection' key is pressed when double-clicking + // so we look for an enclosing cell or table + PRBool tableMode = PR_FALSE; + +#ifdef XP_MAC + // Cmd is Mac table-select key + res = mouseEvent->GetMetaKey(&tableMode); + if (NS_FAILED(res)) return res; + + // Ctrl+Click for context menu + res = mouseEvent->GetCtrlKey(&isContextClick); +#else + // Right mouse button for Windows, UNIX + isContextClick = buttonNumber == 3; + res = mouseEvent->GetCtrlKey(&tableMode); +#endif + if (NS_FAILED(res)) return res; + nsCOMPtr target; res = aMouseEvent->GetTarget(getter_AddRefs(target)); if (NS_FAILED(res)) return res; @@ -175,52 +194,49 @@ nsEditorShellMouseListener::MouseDown(nsIDOMEvent* aMouseEvent) res = mouseEvent->GetDetail(&clickCount); if (NS_FAILED(res)) return res; - if (buttonNumber == 3 || (buttonNumber == 1 && clickCount == 2)) - { - /**XXX Context menu design is flawed: - * Mouse message arrives here first, - * then to XULPopupListenerImpl::MouseDown, - * and never bubbles to frame. - * So we don't reposition the caret correctly - * within a text node and don't detect if clicking - * on a selection. (Logic for both is in frame code.) - * Kludge: Set selection to beginning of text node. - * TODO: try to solve click within selection with a new - * nsSelection method. - * We also want to do this for double-click to detect - * a link enclosing the text node - */ + PRBool NodeIsInSelection = PR_FALSE; - nsCOMPtr textNode = do_QueryInterface(target); - if (textNode) + if (isContextClick || (buttonNumber == 1 && clickCount == 2)) + { + // Context menu or double click + nsCOMPtr node = do_QueryInterface(target); + if (node) { - //XXX We should do this only if not clicking inside an existing selection! nsCOMPtr selection; mEditorShell->GetEditorSelection(getter_AddRefs(selection)); if (selection) - selection->Collapse(textNode, 0); + { + res = selection->ContainsNode(node, PR_TRUE, &NodeIsInSelection); + // Kludge: We really want to reposition the caret exactly as done for + // left mouse button, but we never reach the frame's HandlePress method. + // Thus for text nodes, the best we can do is reposition to the start of the node, + // but only if not already selected, of course + if (!NodeIsInSelection) + selection->Collapse(node, 0); + } // Get enclosing link - res = mEditorShell->GetElementOrParentByTagName(NS_LITERAL_STRING("href"), textNode, getter_AddRefs(element)); + nsCOMPtr linkElement; + res = mEditorShell->GetElementOrParentByTagName(NS_LITERAL_STRING("href"), node, getter_AddRefs(linkElement)); if (NS_FAILED(res)) return res; + if (linkElement) + element = linkElement; } } - if (buttonNumber == 1) + if (isContextClick) + { + // Set selection to node clicked on if NOT within an existing selection + if (element && !NodeIsInSelection) + mEditorShell->SelectElement(element); + // Always fall through to do other actions, such as context menu + } + else if (buttonNumber == 1) { #ifdef DEBUG_cmanske -printf("nsEditorShellMouseListener::MouseDown: clickCount=%d\n",clickCount); + printf("nsEditorShellMouseListener::MouseDown: clickCount=%d\n",clickCount); #endif - // Test if special 'table selection' key is pressed when double-clicking - // so we look for an enclosing cell or table - PRBool tableMode = PR_FALSE; -#ifdef XP_MAC - res = mouseEvent->GetMetaKey(&tableMode); -#else - res = mouseEvent->GetCtrlKey(&tableMode); -#endif - if (NS_FAILED(res)) return res; if (tableMode && clickCount == 2) { #ifdef DEBUG_cmanske @@ -244,22 +260,14 @@ printf("nsEditorShellMouseListener::MouseDown-DoubleClick in cell\n"); if (NS_FAILED(res)) return res; // Let editor decide what to do with this - PRBool handled; + PRBool handled = PR_FALSE; mEditorShell->HandleMouseClickOnElement(element, clickCount, x, y, &handled); if (handled) mouseEvent->PreventDefault(); } } - // Should we do this only for "right" mouse button? - // What about Mac? - else if (buttonNumber == 3) - { - if (element) - // Set selection to node clicked on - mEditorShell->SelectElement(element); - // Always fall through to do other actions, such as context menu - } + return NS_OK; } diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp index 56df89b4fb6..588c58c9db3 100644 --- a/mozilla/editor/base/nsHTMLEditor.cpp +++ b/mozilla/editor/base/nsHTMLEditor.cpp @@ -76,6 +76,8 @@ #include "nsIDOMDocumentFragment.h" #include "nsIPresShell.h" #include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" #include "nsIImage.h" #include "nsAOLCiter.h" #include "nsInternetCiter.h" @@ -119,6 +121,8 @@ static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); 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); // Drag & Drop, Clipboard Support static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); @@ -460,7 +464,7 @@ nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) PRBool newMetaCharset = PR_TRUE; // get a list of META tags - result = domdoc->GetElementsByTagName(NS_ConvertASCIItoUCS2("meta"), getter_AddRefs(metaList)); + result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("meta"), getter_AddRefs(metaList)); if (NS_SUCCEEDED(result) && metaList) { PRUint32 listLength = 0; (void) metaList->GetLength(&listLength); @@ -474,17 +478,17 @@ nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) const NS_ConvertASCIItoUCS2 content("charset="); nsString currentValue; - if (NS_FAILED(metaElement->GetAttribute(NS_ConvertASCIItoUCS2("http-equiv"), currentValue))) continue; + if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("http-equiv"), currentValue))) continue; if (kNotFound != currentValue.Find("content-type", PR_TRUE)) { - if (NS_FAILED(metaElement->GetAttribute(NS_ConvertASCIItoUCS2("content"), currentValue))) continue; + if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("content"), currentValue))) continue; PRInt32 offset = currentValue.Find(content.GetUnicode(), PR_TRUE); if (kNotFound != offset) { currentValue.Left(newMetaString, offset); // copy current value before "charset=" (e.g. text/html) newMetaString.Append(content); newMetaString.Append(characterSet); - result = nsEditor::SetAttribute(metaElement, NS_ConvertASCIItoUCS2("content"), newMetaString); + result = nsEditor::SetAttribute(metaElement, NS_LITERAL_STRING("content"), newMetaString); if (NS_SUCCEEDED(result)) newMetaCharset = PR_FALSE; break; @@ -498,12 +502,12 @@ nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) nsCOMPtrheadNode; nsCOMPtrresultNode; - result = domdoc->GetElementsByTagName(NS_ConvertASCIItoUCS2("head"),getter_AddRefs(headList)); + result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"),getter_AddRefs(headList)); if (NS_SUCCEEDED(result) && headList) { headList->Item(0, getter_AddRefs(headNode)); if (headNode) { // Create a new meta charset tag - result = CreateNode(NS_ConvertASCIItoUCS2("meta"), headNode, 0, getter_AddRefs(resultNode)); + result = CreateNode(NS_LITERAL_STRING("meta"), headNode, 0, getter_AddRefs(resultNode)); if (NS_FAILED(result)) return NS_ERROR_FAILURE; @@ -512,12 +516,12 @@ nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) metaElement = do_QueryInterface(resultNode); if (metaElement) { // not undoable, undo should undo CreateNode - result = metaElement->SetAttribute(NS_ConvertASCIItoUCS2("http-equiv"), NS_ConvertASCIItoUCS2("Content-Type")); + result = metaElement->SetAttribute(NS_LITERAL_STRING("http-equiv"), NS_LITERAL_STRING("Content-Type")); if (NS_SUCCEEDED(result)) { newMetaString.AssignWithConversion("text/html;charset="); newMetaString.Append(characterSet); // not undoable, undo should undo CreateNode - result = metaElement->SetAttribute(NS_ConvertASCIItoUCS2("content"), newMetaString); + result = metaElement->SetAttribute(NS_LITERAL_STRING("content"), newMetaString); } } } @@ -807,7 +811,7 @@ NS_IMETHODIMP nsHTMLEditor::TabInTable(PRBool inIsShift, PRBool *outHandled) // Find enclosing table cell from the selection (cell may be the selected element) nsCOMPtr cellElement; - nsresult res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("td"), nsnull, getter_AddRefs(cellElement)); + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nsnull, getter_AddRefs(cellElement)); if (NS_FAILED(res)) return res; // Do nothing -- we didn't find a table cell if (!cellElement) return NS_OK; @@ -2405,12 +2409,12 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, nsAutoString inputString (aInputString); // hope this does copy-on-write // Windows linebreaks: Map CRLF to LF: - inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r\n"), - NS_ConvertASCIItoUCS2("\n")); + inputString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), + NS_LITERAL_STRING("\n")); // Mac linebreaks: Map any remaining CR to LF: - inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r"), - NS_ConvertASCIItoUCS2("\n")); + inputString.ReplaceSubstring(NS_LITERAL_STRING("\r"), + NS_LITERAL_STRING("\n")); ForceCompositionEnd(); nsAutoEditBatch beginBatching(this); @@ -2535,6 +2539,43 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, return res; } +NS_IMETHODIMP +nsHTMLEditor::RebuildDocumentFromSource(const nsString& aSourceString) +{ + // First, make sure there are no return chars in the document. + // Bad things happen if you insert returns (instead of dom newlines, \n) + // into an editor document. + nsAutoString sourceString (aSourceString); // hope this does copy-on-write + + // Windows linebreaks: Map CRLF to LF: + sourceString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), + NS_LITERAL_STRING("\n")); + + // Mac linebreaks: Map any remaining CR to LF: + sourceString.ReplaceSubstring(NS_LITERAL_STRING("\r"), + NS_LITERAL_STRING("\n")); + + ForceCompositionEnd(); + + // Get the document we want to replace + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + + nsCOMPtr document; + nsresult res = ps->GetDocument(getter_AddRefs(document)); + if (NS_FAILED(res)) return res; + + nsCOMPtr theParser; + res = nsComponentManager::CreateInstance(kCParserCID, nsnull, kCParserIID, getter_AddRefs(theParser)); + + if (NS_FAILED(res)) return res; + + // Parse the string to rebuild the document + // Last 2 params: enableVerify and "this is the last chunk to parse" + return theParser->Parse(sourceString, (void*)document.get(), NS_LITERAL_STRING("text/html"), PR_TRUE, PR_TRUE); +} + NS_IMETHODIMP nsHTMLEditor::InsertBreak() { nsresult res; @@ -2887,7 +2928,7 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists) if (aGetLists) { // Get the "ol", "ul", or "dl" parent element - res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("list"), node, getter_AddRefs(blockParentElem)); + res = GetElementOrParentByTagName(NS_LITERAL_STRING("list"), node, getter_AddRefs(blockParentElem)); if (NS_FAILED(res)) return res; } else @@ -2941,7 +2982,7 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists) if (aGetLists) { // Get the "ol", "ul", or "dl" parent element - res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("list"), startParent, getter_AddRefs(blockParent)); + res = GetElementOrParentByTagName(NS_LITERAL_STRING("list"), startParent, getter_AddRefs(blockParent)); } else { @@ -3515,6 +3556,14 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu PRBool isCollapsed; selection->GetIsCollapsed(&isCollapsed); + nsAutoString domTagName; + nsAutoString TagName = aTagName; + TagName.ToLowerCase(); + // Empty string indicates we should match any element tag + PRBool anyTag = (TagName.IsEmpty()); + PRBool isLinkTag = IsLink(TagName); + PRBool isNamedAnchorTag = IsLink(TagName); + nsCOMPtr selectedElement; nsCOMPtr range; res = selection->GetRangeAt(0, getter_AddRefs(range)); @@ -3533,179 +3582,188 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu res = range->GetEndOffset(&endOffset); if (NS_FAILED(res)) return res; + // Optimization for a single selected element if (startParent && startParent == endParent && (endOffset-startOffset) == 1) { nsCOMPtr selectedNode = GetChildAt(startParent, startOffset); if (NS_FAILED(res)) return NS_OK; - - selectedElement = do_QueryInterface(selectedNode); - bNodeFound = PR_TRUE; - } - - nsAutoString TagName = aTagName; - TagName.ToLowerCase(); - // Empty string indicates we should match any element tag - PRBool anyTag = (TagName.IsEmpty()); - - //Note that this doesn't need to go through the transaction system - if (IsLink(TagName)) - { - // Link tag is a special case - we return the anchor node - // found for any selection that is totally within a link, - // included a collapsed selection (just a caret in a link) - nsCOMPtr anchorNode; - res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); - if (NS_FAILED(res)) return res; - PRInt32 anchorOffset = -1; - if (anchorNode) - selection->GetAnchorOffset(&anchorOffset); - - nsCOMPtr focusNode; - res = selection->GetFocusNode(getter_AddRefs(focusNode)); - if (NS_FAILED(res)) return res; - PRInt32 focusOffset = -1; - if (focusNode) - selection->GetFocusOffset(&focusOffset); - - // Link node must be the same for both ends of selection - if (NS_SUCCEEDED(res) && anchorNode) + if (selectedNode) { -#ifdef DEBUG_cmanske + selectedNode->GetNodeName(domTagName); + domTagName.ToLowerCase(); + + // Only consider this node if requested to find a link or "any" tagname + if (anyTag || + isLinkTag && IsLinkNode(selectedNode) || + isNamedAnchorTag && IsNamedAnchorNode(selectedNode) || + TagName == domTagName ) { - nsAutoString name; - anchorNode->GetNodeName(name); - printf("GetSelectedElement: Anchor node of selection: "); - wprintf(name.GetUnicode()); - printf(" Offset: %d\n", anchorOffset); - focusNode->GetNodeName(name); - printf("Focus node of selection: "); - wprintf(name.GetUnicode()); - printf(" Offset: %d\n", focusOffset); - } -#endif - nsCOMPtr parentLinkOfAnchor; - res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("href"), anchorNode, getter_AddRefs(parentLinkOfAnchor)); - // XXX: ERROR_HANDLING can parentLinkOfAnchor be null? - if (NS_SUCCEEDED(res) && parentLinkOfAnchor) - { - if (isCollapsed) - { - // We have just a caret in the link - bNodeFound = PR_TRUE; - } else if(focusNode) - { // Link node must be the same for both ends of selection - nsCOMPtr parentLinkOfFocus; - res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("href"), focusNode, getter_AddRefs(parentLinkOfFocus)); - if (NS_SUCCEEDED(res) && parentLinkOfFocus == parentLinkOfAnchor) - bNodeFound = PR_TRUE; - } - - // We found a link node parent - if (bNodeFound) { - // GetElementOrParentByTagName addref'd this, so we don't need to do it here - *aReturn = parentLinkOfAnchor; - NS_IF_ADDREF(*aReturn); - return NS_OK; - } - } - else if (anchorOffset >= 0) // Check if link node is the only thing selected - { - nsCOMPtr anchorChild; - anchorChild = GetChildAt(anchorNode,anchorOffset); - if (anchorChild && IsLinkNode(anchorChild) && - (anchorNode == focusNode) && focusOffset == (anchorOffset+1)) - { - selectedElement = do_QueryInterface(anchorChild); - bNodeFound = PR_TRUE; - } + bNodeFound = PR_TRUE; + selectedElement = do_QueryInterface(selectedNode); } } - } + } - if (!bNodeFound && !isCollapsed) // Don't bother to examine selection if it is collapsed + if (!bNodeFound) { - nsCOMPtr enumerator; - res = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(res)) + if (isLinkTag) { - if(!enumerator) - return NS_ERROR_NULL_POINTER; + // Link tag is a special case - we return the anchor node + // found for any selection that is totally within a link, + // included a collapsed selection (just a caret in a link) + nsCOMPtr anchorNode; + res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); + if (NS_FAILED(res)) return res; + PRInt32 anchorOffset = -1; + if (anchorNode) + selection->GetAnchorOffset(&anchorOffset); + + nsCOMPtr focusNode; + res = selection->GetFocusNode(getter_AddRefs(focusNode)); + if (NS_FAILED(res)) return res; + PRInt32 focusOffset = -1; + if (focusNode) + selection->GetFocusOffset(&focusOffset); - enumerator->First(); - nsCOMPtr currentItem; - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(res)) && currentItem) + // Link node must be the same for both ends of selection + if (NS_SUCCEEDED(res) && anchorNode) { - nsCOMPtr currange( do_QueryInterface(currentItem) ); - nsCOMPtr iter; - res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - // XXX: ERROR_HANDLING XPCOM usage - if ((NS_SUCCEEDED(res)) && iter) + #ifdef DEBUG_cmanske { - iter->Init(currange); - // loop through the content iterator for each content node - nsCOMPtr content; - while (NS_ENUMERATOR_FALSE == iter->IsDone()) + nsAutoString name; + anchorNode->GetNodeName(name); + printf("GetSelectedElement: Anchor node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n", anchorOffset); + focusNode->GetNodeName(name); + printf("Focus node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n", focusOffset); + } + #endif + nsCOMPtr parentLinkOfAnchor; + res = GetElementOrParentByTagName(NS_LITERAL_STRING("href"), anchorNode, getter_AddRefs(parentLinkOfAnchor)); + // XXX: ERROR_HANDLING can parentLinkOfAnchor be null? + if (NS_SUCCEEDED(res) && parentLinkOfAnchor) + { + if (isCollapsed) { - res = iter->CurrentNode(getter_AddRefs(content)); - // Note likely! - if (NS_FAILED(res)) - return NS_ERROR_FAILURE; - - // Query interface to cast nsIContent to nsIDOMNode - // then get tagType to compare to aTagName - // Clone node of each desired type and append it to the aDomFrag - selectedElement = do_QueryInterface(content); - if (selectedElement) - { - // If we already found a node, then we have another element, - // thus there's not just one element selected - if (bNodeFound) - { - bNodeFound = PR_FALSE; - break; - } - - nsAutoString domTagName; - selectedElement->GetNodeName(domTagName); - domTagName.ToLowerCase(); - - if (anyTag) - { - // Get name of first selected element - selectedElement->GetTagName(TagName); - TagName.ToLowerCase(); - anyTag = PR_FALSE; - } - - // The "A" tag is a pain, - // used for both link(href is set) and "Named Anchor" - nsCOMPtr selectedNode = do_QueryInterface(selectedElement); - if ( (IsLink(TagName) && IsLinkNode(selectedNode)) || - (IsNamedAnchor(TagName) && IsNamedAnchorNode(selectedNode)) ) - { - bNodeFound = PR_TRUE; - } else if (TagName == domTagName) { // All other tag names are handled here - bNodeFound = PR_TRUE; - } - if (!bNodeFound) - { - // Check if node we have is really part of the selection??? - break; - } - } - iter->Next(); + // We have just a caret in the link + bNodeFound = PR_TRUE; + } else if(focusNode) + { // Link node must be the same for both ends of selection + nsCOMPtr parentLinkOfFocus; + res = GetElementOrParentByTagName(NS_LITERAL_STRING("href"), focusNode, getter_AddRefs(parentLinkOfFocus)); + if (NS_SUCCEEDED(res) && parentLinkOfFocus == parentLinkOfAnchor) + bNodeFound = PR_TRUE; + } + + // We found a link node parent + if (bNodeFound) { + // GetElementOrParentByTagName addref'd this, so we don't need to do it here + *aReturn = parentLinkOfAnchor; + NS_IF_ADDREF(*aReturn); + return NS_OK; + } + } + else if (anchorOffset >= 0) // Check if link node is the only thing selected + { + nsCOMPtr anchorChild; + anchorChild = GetChildAt(anchorNode,anchorOffset); + if (anchorChild && IsLinkNode(anchorChild) && + (anchorNode == focusNode) && focusOffset == (anchorOffset+1)) + { + selectedElement = do_QueryInterface(anchorChild); + bNodeFound = PR_TRUE; } } - } else { - // Should never get here? - isCollapsed = PR_TRUE; - printf("isCollapsed was FALSE, but no elements found in selection\n"); } - } else { - printf("Could not create enumerator for GetSelectionProperties\n"); + } + + if (!isCollapsed) // Don't bother to examine selection if it is collapsed + { + nsCOMPtr enumerator; + res = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(res)) + { + if(!enumerator) + return NS_ERROR_NULL_POINTER; + + enumerator->First(); + nsCOMPtr currentItem; + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(res)) && currentItem) + { + nsCOMPtr currange( do_QueryInterface(currentItem) ); + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (iter) + { + iter->Init(currange); + // loop through the content iterator for each content node + nsCOMPtr content; + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + // Note likely! + if (NS_FAILED(res)) + return NS_ERROR_FAILURE; + + // Query interface to cast nsIContent to nsIDOMNode + // then get tagType to compare to aTagName + // Clone node of each desired type and append it to the aDomFrag + selectedElement = do_QueryInterface(content); + if (selectedElement) + { + // If we already found a node, then we have another element, + // thus there's not just one element selected + if (bNodeFound) + { + bNodeFound = PR_FALSE; + break; + } + + selectedElement->GetNodeName(domTagName); + domTagName.ToLowerCase(); + + if (anyTag) + { + // Get name of first selected element + selectedElement->GetTagName(TagName); + TagName.ToLowerCase(); + anyTag = PR_FALSE; + } + + // The "A" tag is a pain, + // used for both link(href is set) and "Named Anchor" + nsCOMPtr selectedNode = do_QueryInterface(selectedElement); + if ( (isLinkTag && IsLinkNode(selectedNode)) || + (isNamedAnchorTag && IsNamedAnchorNode(selectedNode)) ) + { + bNodeFound = PR_TRUE; + } else if (TagName == domTagName) { // All other tag names are handled here + bNodeFound = PR_TRUE; + } + if (!bNodeFound) + { + // Check if node we have is really part of the selection??? + break; + } + } + iter->Next(); + } + } + } else { + // Should never get here? + isCollapsed = PR_TRUE; + printf("isCollapsed was FALSE, but no elements found in selection\n"); + } + } else { + printf("Could not create enumerator for GetSelectionProperties\n"); + } } } if (bNodeFound) @@ -3758,26 +3816,26 @@ nsHTMLEditor::CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement* return NS_ERROR_FAILURE; // Mark the new element dirty, so it will be formatted - newElement->SetAttribute(NS_ConvertASCIItoUCS2("_moz_dirty"), nsAutoString()); + newElement->SetAttribute(NS_LITERAL_STRING("_moz_dirty"), nsAutoString()); // Set default values for new elements if (TagName.EqualsWithConversion("hr")) { // Note that we read the user's attributes for these from prefs (in InsertHLine JS) - newElement->SetAttribute(NS_ConvertASCIItoUCS2("align"),NS_ConvertASCIItoUCS2("center")); - newElement->SetAttribute(NS_ConvertASCIItoUCS2("width"),NS_ConvertASCIItoUCS2("100%")); - newElement->SetAttribute(NS_ConvertASCIItoUCS2("size"),NS_ConvertASCIItoUCS2("2")); + newElement->SetAttribute(NS_LITERAL_STRING("align"),NS_LITERAL_STRING("center")); + newElement->SetAttribute(NS_LITERAL_STRING("width"),NS_LITERAL_STRING("100%")); + newElement->SetAttribute(NS_LITERAL_STRING("size"),NS_LITERAL_STRING("2")); } else if (TagName.EqualsWithConversion("table")) { - newElement->SetAttribute(NS_ConvertASCIItoUCS2("cellpadding"),NS_ConvertASCIItoUCS2("2")); - newElement->SetAttribute(NS_ConvertASCIItoUCS2("cellspacing"),NS_ConvertASCIItoUCS2("2")); - newElement->SetAttribute(NS_ConvertASCIItoUCS2("border"),NS_ConvertASCIItoUCS2("1")); + newElement->SetAttribute(NS_LITERAL_STRING("cellpadding"),NS_LITERAL_STRING("2")); + newElement->SetAttribute(NS_LITERAL_STRING("cellspacing"),NS_LITERAL_STRING("2")); + newElement->SetAttribute(NS_LITERAL_STRING("border"),NS_LITERAL_STRING("1")); } else if (TagName.EqualsWithConversion("tr")) { - newElement->SetAttribute(NS_ConvertASCIItoUCS2("valign"),NS_ConvertASCIItoUCS2("top")); + newElement->SetAttribute(NS_LITERAL_STRING("valign"),NS_LITERAL_STRING("top")); } else if (TagName.EqualsWithConversion("td")) { - newElement->SetAttribute(NS_ConvertASCIItoUCS2("valign"),NS_ConvertASCIItoUCS2("top")); + newElement->SetAttribute(NS_LITERAL_STRING("valign"),NS_LITERAL_STRING("top")); } // ADD OTHER TAGS HERE @@ -3866,9 +3924,9 @@ nsHTMLEditor::SetBackgroundColor(const nsString& aColor) while(cell) { if (setColor) - res = SetAttribute(cell, NS_ConvertASCIItoUCS2("bgcolor"), aColor); + res = SetAttribute(cell, NS_LITERAL_STRING("bgcolor"), aColor); else - res = RemoveAttribute(cell, NS_ConvertASCIItoUCS2("bgcolor")); + res = RemoveAttribute(cell, NS_LITERAL_STRING("bgcolor")); if (NS_FAILED(res)) break; GetNextSelectedCell(getter_AddRefs(cell), nsnull); @@ -3885,9 +3943,9 @@ nsHTMLEditor::SetBackgroundColor(const nsString& aColor) } // Use the editor method that goes through the transaction system if (setColor) - res = SetAttribute(element, NS_ConvertASCIItoUCS2("bgcolor"), aColor); + res = SetAttribute(element, NS_LITERAL_STRING("bgcolor"), aColor); else - res = RemoveAttribute(element, NS_ConvertASCIItoUCS2("bgcolor")); + res = RemoveAttribute(element, NS_LITERAL_STRING("bgcolor")); return res; } @@ -5196,11 +5254,11 @@ nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText, nsCOMPtr preElement (do_QueryInterface(preNode)); if (preElement) { - preElement->SetAttribute(NS_ConvertASCIItoUCS2("_moz_quote"), - NS_ConvertASCIItoUCS2("true")); + preElement->SetAttribute(NS_LITERAL_STRING("_moz_quote"), + NS_LITERAL_STRING("true")); // set style to not have unwanted vertical margins - preElement->SetAttribute(NS_ConvertASCIItoUCS2("style"), - NS_ConvertASCIItoUCS2("margin: 0 0 0 0px;")); + preElement->SetAttribute(NS_LITERAL_STRING("style"), + NS_LITERAL_STRING("margin: 0 0 0 0px;")); } // and set the selection inside it: @@ -5650,13 +5708,13 @@ nsHTMLEditor::GetHeadContentsAsHTML(nsString& aOutputString) res = SetSelectionAroundHeadChildren(selection, mDocWeak); if (NS_FAILED(res)) return res; - res = OutputToString(aOutputString, NS_ConvertASCIItoUCS2("text/html"), + res = OutputToString(aOutputString, NS_LITERAL_STRING("text/html"), nsIDocumentEncoder::OutputSelectionOnly); if (NS_SUCCEEDED(res)) { // Selection always includes , // so terminate there - PRInt32 offset = aOutputString.Find(NS_ConvertASCIItoUCS2(" 0) { // Ensure the string ends in a newline @@ -6810,7 +6868,7 @@ nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, } // reparent the node inside font node with appropriate relative size - res = InsertContainerAbove(node, &tmp, NS_ConvertASCIItoUCS2( aSizeChange==1 ? "big" : "small" )); + res = InsertContainerAbove(node, &tmp, aSizeChange==1 ? NS_LITERAL_STRING("big") : NS_LITERAL_STRING("small")); return res; } diff --git a/mozilla/editor/base/nsHTMLEditor.h b/mozilla/editor/base/nsHTMLEditor.h index b7fc9bffa05..dd24380ebc0 100644 --- a/mozilla/editor/base/nsHTMLEditor.h +++ b/mozilla/editor/base/nsHTMLEditor.h @@ -121,6 +121,7 @@ public: NS_IMETHOD InsertHTML(const nsString &aInputString); NS_IMETHOD InsertHTMLWithCharset(const nsString& aInputString, const nsString& aCharset); + NS_IMETHOD RebuildDocumentFromSource(const nsString& aSourceString); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); NS_IMETHOD DeleteSelection(EDirection aAction); diff --git a/mozilla/editor/base/nsTableEditor.cpp b/mozilla/editor/base/nsTableEditor.cpp index 7e2d3a401c0..f8d0753623d 100644 --- a/mozilla/editor/base/nsTableEditor.cpp +++ b/mozilla/editor/base/nsTableEditor.cpp @@ -413,6 +413,12 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) //.. so suppress Rules System selection munging nsAutoTxnsConserveSelection dontChangeSelection(this); + // If we are inserting after all existing columns + // Make sure table is "well formed" + // before appending new column + if (startColIndex >= colCount) + NormalizeTable(table); + nsCOMPtr rowElement; for ( rowIndex = 0; rowIndex < rowCount; rowIndex++) { @@ -445,11 +451,6 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) } } } else { - // We are inserting after all existing columns - // Make sure table is "well formed" - // before appending new column - NormalizeTable(table); - // Get current row and append new cells after last cell in row if(rowIndex == 0) res = GetFirstRow(table.get(), *getter_AddRefs(rowElement)); @@ -3226,7 +3227,7 @@ nsHTMLEditor::GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 &aSelection if (IndexNotTested(&indexArray, startRowIndex)) { indexArray.AppendElement((void*)startColIndex); - allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, colCount); + allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, rowCount); // We're done as soon as we fail for any column if (!allCellsInRowAreSelected) break; } diff --git a/mozilla/editor/composer/src/nsEditorShell.cpp b/mozilla/editor/composer/src/nsEditorShell.cpp index 5556317951e..29ee2ed691b 100644 --- a/mozilla/editor/composer/src/nsEditorShell.cpp +++ b/mozilla/editor/composer/src/nsEditorShell.cpp @@ -2761,6 +2761,31 @@ nsEditorShell::InsertSourceWithCharset(const PRUnichar *aSourceToInsert, return err; } +NS_IMETHODIMP +nsEditorShell::RebuildDocumentFromSource(const PRUnichar *aSource) +{ + nsresult err = NS_NOINTERFACE; + + nsAutoString source(aSource); + + switch (mEditorType) + { + case ePlainTextEditorType: + case eHTMLTextEditorType: + { + nsCOMPtr htmlEditor = do_QueryInterface(mEditor); + if (htmlEditor) + err = htmlEditor->RebuildDocumentFromSource(source); + } + break; + + default: + err = NS_NOINTERFACE; + } + + return err; +} + NS_IMETHODIMP nsEditorShell::InsertBreak() { diff --git a/mozilla/editor/composer/src/nsEditorShellMouseListener.cpp b/mozilla/editor/composer/src/nsEditorShellMouseListener.cpp index f76f91a9bcb..f09b1c4693f 100644 --- a/mozilla/editor/composer/src/nsEditorShellMouseListener.cpp +++ b/mozilla/editor/composer/src/nsEditorShellMouseListener.cpp @@ -165,6 +165,25 @@ nsEditorShellMouseListener::MouseDown(nsIDOMEvent* aMouseEvent) nsresult res = mouseEvent->GetButton(&buttonNumber); if (NS_FAILED(res)) return res; + PRBool isContextClick; + // Test if special 'table selection' key is pressed when double-clicking + // so we look for an enclosing cell or table + PRBool tableMode = PR_FALSE; + +#ifdef XP_MAC + // Cmd is Mac table-select key + res = mouseEvent->GetMetaKey(&tableMode); + if (NS_FAILED(res)) return res; + + // Ctrl+Click for context menu + res = mouseEvent->GetCtrlKey(&isContextClick); +#else + // Right mouse button for Windows, UNIX + isContextClick = buttonNumber == 3; + res = mouseEvent->GetCtrlKey(&tableMode); +#endif + if (NS_FAILED(res)) return res; + nsCOMPtr target; res = aMouseEvent->GetTarget(getter_AddRefs(target)); if (NS_FAILED(res)) return res; @@ -175,52 +194,49 @@ nsEditorShellMouseListener::MouseDown(nsIDOMEvent* aMouseEvent) res = mouseEvent->GetDetail(&clickCount); if (NS_FAILED(res)) return res; - if (buttonNumber == 3 || (buttonNumber == 1 && clickCount == 2)) - { - /**XXX Context menu design is flawed: - * Mouse message arrives here first, - * then to XULPopupListenerImpl::MouseDown, - * and never bubbles to frame. - * So we don't reposition the caret correctly - * within a text node and don't detect if clicking - * on a selection. (Logic for both is in frame code.) - * Kludge: Set selection to beginning of text node. - * TODO: try to solve click within selection with a new - * nsSelection method. - * We also want to do this for double-click to detect - * a link enclosing the text node - */ + PRBool NodeIsInSelection = PR_FALSE; - nsCOMPtr textNode = do_QueryInterface(target); - if (textNode) + if (isContextClick || (buttonNumber == 1 && clickCount == 2)) + { + // Context menu or double click + nsCOMPtr node = do_QueryInterface(target); + if (node) { - //XXX We should do this only if not clicking inside an existing selection! nsCOMPtr selection; mEditorShell->GetEditorSelection(getter_AddRefs(selection)); if (selection) - selection->Collapse(textNode, 0); + { + res = selection->ContainsNode(node, PR_TRUE, &NodeIsInSelection); + // Kludge: We really want to reposition the caret exactly as done for + // left mouse button, but we never reach the frame's HandlePress method. + // Thus for text nodes, the best we can do is reposition to the start of the node, + // but only if not already selected, of course + if (!NodeIsInSelection) + selection->Collapse(node, 0); + } // Get enclosing link - res = mEditorShell->GetElementOrParentByTagName(NS_LITERAL_STRING("href"), textNode, getter_AddRefs(element)); + nsCOMPtr linkElement; + res = mEditorShell->GetElementOrParentByTagName(NS_LITERAL_STRING("href"), node, getter_AddRefs(linkElement)); if (NS_FAILED(res)) return res; + if (linkElement) + element = linkElement; } } - if (buttonNumber == 1) + if (isContextClick) + { + // Set selection to node clicked on if NOT within an existing selection + if (element && !NodeIsInSelection) + mEditorShell->SelectElement(element); + // Always fall through to do other actions, such as context menu + } + else if (buttonNumber == 1) { #ifdef DEBUG_cmanske -printf("nsEditorShellMouseListener::MouseDown: clickCount=%d\n",clickCount); + printf("nsEditorShellMouseListener::MouseDown: clickCount=%d\n",clickCount); #endif - // Test if special 'table selection' key is pressed when double-clicking - // so we look for an enclosing cell or table - PRBool tableMode = PR_FALSE; -#ifdef XP_MAC - res = mouseEvent->GetMetaKey(&tableMode); -#else - res = mouseEvent->GetCtrlKey(&tableMode); -#endif - if (NS_FAILED(res)) return res; if (tableMode && clickCount == 2) { #ifdef DEBUG_cmanske @@ -244,22 +260,14 @@ printf("nsEditorShellMouseListener::MouseDown-DoubleClick in cell\n"); if (NS_FAILED(res)) return res; // Let editor decide what to do with this - PRBool handled; + PRBool handled = PR_FALSE; mEditorShell->HandleMouseClickOnElement(element, clickCount, x, y, &handled); if (handled) mouseEvent->PreventDefault(); } } - // Should we do this only for "right" mouse button? - // What about Mac? - else if (buttonNumber == 3) - { - if (element) - // Set selection to node clicked on - mEditorShell->SelectElement(element); - // Always fall through to do other actions, such as context menu - } + return NS_OK; } diff --git a/mozilla/editor/composer/src/res/EditorOverride.css b/mozilla/editor/composer/src/res/EditorOverride.css index 6aee101b1b4..5f42b47221f 100644 --- a/mozilla/editor/composer/src/res/EditorOverride.css +++ b/mozilla/editor/composer/src/res/EditorOverride.css @@ -43,6 +43,10 @@ a[name] { cursor: default; } +a:link img, a:visited img, a:active, { + user-input: none; +} + input, select, textarea { user-select: all !important; user-input: none !important; diff --git a/mozilla/editor/idl/nsIEditorShell.idl b/mozilla/editor/idl/nsIEditorShell.idl index 36263794a8c..a4472f0ad4e 100644 --- a/mozilla/editor/idl/nsIEditorShell.idl +++ b/mozilla/editor/idl/nsIEditorShell.idl @@ -198,6 +198,13 @@ interface nsIEditorShell : nsISupports /* Insert HTML source at current location (replace current selection) */ void InsertSource(in wstring textToInsert); void InsertSourceWithCharset(in wstring textToInsert, in wstring charset); + + /** Rebuild the entire document from source HTML + * Needed to be able to edit HEAD and other outside-of-BODY content + * param source: HTML source string of the entire new document + */ + void RebuildDocumentFromSource(in wstring source); + void InsertBreak(); void MakeOrChangeList(in wstring listType); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index 56df89b4fb6..588c58c9db3 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -76,6 +76,8 @@ #include "nsIDOMDocumentFragment.h" #include "nsIPresShell.h" #include "nsIPresContext.h" +#include "nsIParser.h" +#include "nsParserCIID.h" #include "nsIImage.h" #include "nsAOLCiter.h" #include "nsInternetCiter.h" @@ -119,6 +121,8 @@ static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID); static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID); 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); // Drag & Drop, Clipboard Support static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); @@ -460,7 +464,7 @@ nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) PRBool newMetaCharset = PR_TRUE; // get a list of META tags - result = domdoc->GetElementsByTagName(NS_ConvertASCIItoUCS2("meta"), getter_AddRefs(metaList)); + result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("meta"), getter_AddRefs(metaList)); if (NS_SUCCEEDED(result) && metaList) { PRUint32 listLength = 0; (void) metaList->GetLength(&listLength); @@ -474,17 +478,17 @@ nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) const NS_ConvertASCIItoUCS2 content("charset="); nsString currentValue; - if (NS_FAILED(metaElement->GetAttribute(NS_ConvertASCIItoUCS2("http-equiv"), currentValue))) continue; + if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("http-equiv"), currentValue))) continue; if (kNotFound != currentValue.Find("content-type", PR_TRUE)) { - if (NS_FAILED(metaElement->GetAttribute(NS_ConvertASCIItoUCS2("content"), currentValue))) continue; + if (NS_FAILED(metaElement->GetAttribute(NS_LITERAL_STRING("content"), currentValue))) continue; PRInt32 offset = currentValue.Find(content.GetUnicode(), PR_TRUE); if (kNotFound != offset) { currentValue.Left(newMetaString, offset); // copy current value before "charset=" (e.g. text/html) newMetaString.Append(content); newMetaString.Append(characterSet); - result = nsEditor::SetAttribute(metaElement, NS_ConvertASCIItoUCS2("content"), newMetaString); + result = nsEditor::SetAttribute(metaElement, NS_LITERAL_STRING("content"), newMetaString); if (NS_SUCCEEDED(result)) newMetaCharset = PR_FALSE; break; @@ -498,12 +502,12 @@ nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) nsCOMPtrheadNode; nsCOMPtrresultNode; - result = domdoc->GetElementsByTagName(NS_ConvertASCIItoUCS2("head"),getter_AddRefs(headList)); + result = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"),getter_AddRefs(headList)); if (NS_SUCCEEDED(result) && headList) { headList->Item(0, getter_AddRefs(headNode)); if (headNode) { // Create a new meta charset tag - result = CreateNode(NS_ConvertASCIItoUCS2("meta"), headNode, 0, getter_AddRefs(resultNode)); + result = CreateNode(NS_LITERAL_STRING("meta"), headNode, 0, getter_AddRefs(resultNode)); if (NS_FAILED(result)) return NS_ERROR_FAILURE; @@ -512,12 +516,12 @@ nsHTMLEditor::SetDocumentCharacterSet(const PRUnichar* characterSet) metaElement = do_QueryInterface(resultNode); if (metaElement) { // not undoable, undo should undo CreateNode - result = metaElement->SetAttribute(NS_ConvertASCIItoUCS2("http-equiv"), NS_ConvertASCIItoUCS2("Content-Type")); + result = metaElement->SetAttribute(NS_LITERAL_STRING("http-equiv"), NS_LITERAL_STRING("Content-Type")); if (NS_SUCCEEDED(result)) { newMetaString.AssignWithConversion("text/html;charset="); newMetaString.Append(characterSet); // not undoable, undo should undo CreateNode - result = metaElement->SetAttribute(NS_ConvertASCIItoUCS2("content"), newMetaString); + result = metaElement->SetAttribute(NS_LITERAL_STRING("content"), newMetaString); } } } @@ -807,7 +811,7 @@ NS_IMETHODIMP nsHTMLEditor::TabInTable(PRBool inIsShift, PRBool *outHandled) // Find enclosing table cell from the selection (cell may be the selected element) nsCOMPtr cellElement; - nsresult res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("td"), nsnull, getter_AddRefs(cellElement)); + nsresult res = GetElementOrParentByTagName(NS_LITERAL_STRING("td"), nsnull, getter_AddRefs(cellElement)); if (NS_FAILED(res)) return res; // Do nothing -- we didn't find a table cell if (!cellElement) return NS_OK; @@ -2405,12 +2409,12 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, nsAutoString inputString (aInputString); // hope this does copy-on-write // Windows linebreaks: Map CRLF to LF: - inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r\n"), - NS_ConvertASCIItoUCS2("\n")); + inputString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), + NS_LITERAL_STRING("\n")); // Mac linebreaks: Map any remaining CR to LF: - inputString.ReplaceSubstring(NS_ConvertASCIItoUCS2("\r"), - NS_ConvertASCIItoUCS2("\n")); + inputString.ReplaceSubstring(NS_LITERAL_STRING("\r"), + NS_LITERAL_STRING("\n")); ForceCompositionEnd(); nsAutoEditBatch beginBatching(this); @@ -2535,6 +2539,43 @@ NS_IMETHODIMP nsHTMLEditor::InsertHTMLWithCharset(const nsString& aInputString, return res; } +NS_IMETHODIMP +nsHTMLEditor::RebuildDocumentFromSource(const nsString& aSourceString) +{ + // First, make sure there are no return chars in the document. + // Bad things happen if you insert returns (instead of dom newlines, \n) + // into an editor document. + nsAutoString sourceString (aSourceString); // hope this does copy-on-write + + // Windows linebreaks: Map CRLF to LF: + sourceString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), + NS_LITERAL_STRING("\n")); + + // Mac linebreaks: Map any remaining CR to LF: + sourceString.ReplaceSubstring(NS_LITERAL_STRING("\r"), + NS_LITERAL_STRING("\n")); + + ForceCompositionEnd(); + + // Get the document we want to replace + if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; + nsCOMPtr ps = do_QueryReferent(mPresShellWeak); + if (!ps) return NS_ERROR_NOT_INITIALIZED; + + nsCOMPtr document; + nsresult res = ps->GetDocument(getter_AddRefs(document)); + if (NS_FAILED(res)) return res; + + nsCOMPtr theParser; + res = nsComponentManager::CreateInstance(kCParserCID, nsnull, kCParserIID, getter_AddRefs(theParser)); + + if (NS_FAILED(res)) return res; + + // Parse the string to rebuild the document + // Last 2 params: enableVerify and "this is the last chunk to parse" + return theParser->Parse(sourceString, (void*)document.get(), NS_LITERAL_STRING("text/html"), PR_TRUE, PR_TRUE); +} + NS_IMETHODIMP nsHTMLEditor::InsertBreak() { nsresult res; @@ -2887,7 +2928,7 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists) if (aGetLists) { // Get the "ol", "ul", or "dl" parent element - res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("list"), node, getter_AddRefs(blockParentElem)); + res = GetElementOrParentByTagName(NS_LITERAL_STRING("list"), node, getter_AddRefs(blockParentElem)); if (NS_FAILED(res)) return res; } else @@ -2941,7 +2982,7 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists) if (aGetLists) { // Get the "ol", "ul", or "dl" parent element - res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("list"), startParent, getter_AddRefs(blockParent)); + res = GetElementOrParentByTagName(NS_LITERAL_STRING("list"), startParent, getter_AddRefs(blockParent)); } else { @@ -3515,6 +3556,14 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu PRBool isCollapsed; selection->GetIsCollapsed(&isCollapsed); + nsAutoString domTagName; + nsAutoString TagName = aTagName; + TagName.ToLowerCase(); + // Empty string indicates we should match any element tag + PRBool anyTag = (TagName.IsEmpty()); + PRBool isLinkTag = IsLink(TagName); + PRBool isNamedAnchorTag = IsLink(TagName); + nsCOMPtr selectedElement; nsCOMPtr range; res = selection->GetRangeAt(0, getter_AddRefs(range)); @@ -3533,179 +3582,188 @@ nsHTMLEditor::GetSelectedElement(const nsString& aTagName, nsIDOMElement** aRetu res = range->GetEndOffset(&endOffset); if (NS_FAILED(res)) return res; + // Optimization for a single selected element if (startParent && startParent == endParent && (endOffset-startOffset) == 1) { nsCOMPtr selectedNode = GetChildAt(startParent, startOffset); if (NS_FAILED(res)) return NS_OK; - - selectedElement = do_QueryInterface(selectedNode); - bNodeFound = PR_TRUE; - } - - nsAutoString TagName = aTagName; - TagName.ToLowerCase(); - // Empty string indicates we should match any element tag - PRBool anyTag = (TagName.IsEmpty()); - - //Note that this doesn't need to go through the transaction system - if (IsLink(TagName)) - { - // Link tag is a special case - we return the anchor node - // found for any selection that is totally within a link, - // included a collapsed selection (just a caret in a link) - nsCOMPtr anchorNode; - res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); - if (NS_FAILED(res)) return res; - PRInt32 anchorOffset = -1; - if (anchorNode) - selection->GetAnchorOffset(&anchorOffset); - - nsCOMPtr focusNode; - res = selection->GetFocusNode(getter_AddRefs(focusNode)); - if (NS_FAILED(res)) return res; - PRInt32 focusOffset = -1; - if (focusNode) - selection->GetFocusOffset(&focusOffset); - - // Link node must be the same for both ends of selection - if (NS_SUCCEEDED(res) && anchorNode) + if (selectedNode) { -#ifdef DEBUG_cmanske + selectedNode->GetNodeName(domTagName); + domTagName.ToLowerCase(); + + // Only consider this node if requested to find a link or "any" tagname + if (anyTag || + isLinkTag && IsLinkNode(selectedNode) || + isNamedAnchorTag && IsNamedAnchorNode(selectedNode) || + TagName == domTagName ) { - nsAutoString name; - anchorNode->GetNodeName(name); - printf("GetSelectedElement: Anchor node of selection: "); - wprintf(name.GetUnicode()); - printf(" Offset: %d\n", anchorOffset); - focusNode->GetNodeName(name); - printf("Focus node of selection: "); - wprintf(name.GetUnicode()); - printf(" Offset: %d\n", focusOffset); - } -#endif - nsCOMPtr parentLinkOfAnchor; - res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("href"), anchorNode, getter_AddRefs(parentLinkOfAnchor)); - // XXX: ERROR_HANDLING can parentLinkOfAnchor be null? - if (NS_SUCCEEDED(res) && parentLinkOfAnchor) - { - if (isCollapsed) - { - // We have just a caret in the link - bNodeFound = PR_TRUE; - } else if(focusNode) - { // Link node must be the same for both ends of selection - nsCOMPtr parentLinkOfFocus; - res = GetElementOrParentByTagName(NS_ConvertASCIItoUCS2("href"), focusNode, getter_AddRefs(parentLinkOfFocus)); - if (NS_SUCCEEDED(res) && parentLinkOfFocus == parentLinkOfAnchor) - bNodeFound = PR_TRUE; - } - - // We found a link node parent - if (bNodeFound) { - // GetElementOrParentByTagName addref'd this, so we don't need to do it here - *aReturn = parentLinkOfAnchor; - NS_IF_ADDREF(*aReturn); - return NS_OK; - } - } - else if (anchorOffset >= 0) // Check if link node is the only thing selected - { - nsCOMPtr anchorChild; - anchorChild = GetChildAt(anchorNode,anchorOffset); - if (anchorChild && IsLinkNode(anchorChild) && - (anchorNode == focusNode) && focusOffset == (anchorOffset+1)) - { - selectedElement = do_QueryInterface(anchorChild); - bNodeFound = PR_TRUE; - } + bNodeFound = PR_TRUE; + selectedElement = do_QueryInterface(selectedNode); } } - } + } - if (!bNodeFound && !isCollapsed) // Don't bother to examine selection if it is collapsed + if (!bNodeFound) { - nsCOMPtr enumerator; - res = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(res)) + if (isLinkTag) { - if(!enumerator) - return NS_ERROR_NULL_POINTER; + // Link tag is a special case - we return the anchor node + // found for any selection that is totally within a link, + // included a collapsed selection (just a caret in a link) + nsCOMPtr anchorNode; + res = selection->GetAnchorNode(getter_AddRefs(anchorNode)); + if (NS_FAILED(res)) return res; + PRInt32 anchorOffset = -1; + if (anchorNode) + selection->GetAnchorOffset(&anchorOffset); + + nsCOMPtr focusNode; + res = selection->GetFocusNode(getter_AddRefs(focusNode)); + if (NS_FAILED(res)) return res; + PRInt32 focusOffset = -1; + if (focusNode) + selection->GetFocusOffset(&focusOffset); - enumerator->First(); - nsCOMPtr currentItem; - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(res)) && currentItem) + // Link node must be the same for both ends of selection + if (NS_SUCCEEDED(res) && anchorNode) { - nsCOMPtr currange( do_QueryInterface(currentItem) ); - nsCOMPtr iter; - res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - NS_GET_IID(nsIContentIterator), - getter_AddRefs(iter)); - // XXX: ERROR_HANDLING XPCOM usage - if ((NS_SUCCEEDED(res)) && iter) + #ifdef DEBUG_cmanske { - iter->Init(currange); - // loop through the content iterator for each content node - nsCOMPtr content; - while (NS_ENUMERATOR_FALSE == iter->IsDone()) + nsAutoString name; + anchorNode->GetNodeName(name); + printf("GetSelectedElement: Anchor node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n", anchorOffset); + focusNode->GetNodeName(name); + printf("Focus node of selection: "); + wprintf(name.GetUnicode()); + printf(" Offset: %d\n", focusOffset); + } + #endif + nsCOMPtr parentLinkOfAnchor; + res = GetElementOrParentByTagName(NS_LITERAL_STRING("href"), anchorNode, getter_AddRefs(parentLinkOfAnchor)); + // XXX: ERROR_HANDLING can parentLinkOfAnchor be null? + if (NS_SUCCEEDED(res) && parentLinkOfAnchor) + { + if (isCollapsed) { - res = iter->CurrentNode(getter_AddRefs(content)); - // Note likely! - if (NS_FAILED(res)) - return NS_ERROR_FAILURE; - - // Query interface to cast nsIContent to nsIDOMNode - // then get tagType to compare to aTagName - // Clone node of each desired type and append it to the aDomFrag - selectedElement = do_QueryInterface(content); - if (selectedElement) - { - // If we already found a node, then we have another element, - // thus there's not just one element selected - if (bNodeFound) - { - bNodeFound = PR_FALSE; - break; - } - - nsAutoString domTagName; - selectedElement->GetNodeName(domTagName); - domTagName.ToLowerCase(); - - if (anyTag) - { - // Get name of first selected element - selectedElement->GetTagName(TagName); - TagName.ToLowerCase(); - anyTag = PR_FALSE; - } - - // The "A" tag is a pain, - // used for both link(href is set) and "Named Anchor" - nsCOMPtr selectedNode = do_QueryInterface(selectedElement); - if ( (IsLink(TagName) && IsLinkNode(selectedNode)) || - (IsNamedAnchor(TagName) && IsNamedAnchorNode(selectedNode)) ) - { - bNodeFound = PR_TRUE; - } else if (TagName == domTagName) { // All other tag names are handled here - bNodeFound = PR_TRUE; - } - if (!bNodeFound) - { - // Check if node we have is really part of the selection??? - break; - } - } - iter->Next(); + // We have just a caret in the link + bNodeFound = PR_TRUE; + } else if(focusNode) + { // Link node must be the same for both ends of selection + nsCOMPtr parentLinkOfFocus; + res = GetElementOrParentByTagName(NS_LITERAL_STRING("href"), focusNode, getter_AddRefs(parentLinkOfFocus)); + if (NS_SUCCEEDED(res) && parentLinkOfFocus == parentLinkOfAnchor) + bNodeFound = PR_TRUE; + } + + // We found a link node parent + if (bNodeFound) { + // GetElementOrParentByTagName addref'd this, so we don't need to do it here + *aReturn = parentLinkOfAnchor; + NS_IF_ADDREF(*aReturn); + return NS_OK; + } + } + else if (anchorOffset >= 0) // Check if link node is the only thing selected + { + nsCOMPtr anchorChild; + anchorChild = GetChildAt(anchorNode,anchorOffset); + if (anchorChild && IsLinkNode(anchorChild) && + (anchorNode == focusNode) && focusOffset == (anchorOffset+1)) + { + selectedElement = do_QueryInterface(anchorChild); + bNodeFound = PR_TRUE; } } - } else { - // Should never get here? - isCollapsed = PR_TRUE; - printf("isCollapsed was FALSE, but no elements found in selection\n"); } - } else { - printf("Could not create enumerator for GetSelectionProperties\n"); + } + + if (!isCollapsed) // Don't bother to examine selection if it is collapsed + { + nsCOMPtr enumerator; + res = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(res)) + { + if(!enumerator) + return NS_ERROR_NULL_POINTER; + + enumerator->First(); + nsCOMPtr currentItem; + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if ((NS_SUCCEEDED(res)) && currentItem) + { + nsCOMPtr currange( do_QueryInterface(currentItem) ); + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (iter) + { + iter->Init(currange); + // loop through the content iterator for each content node + nsCOMPtr content; + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + // Note likely! + if (NS_FAILED(res)) + return NS_ERROR_FAILURE; + + // Query interface to cast nsIContent to nsIDOMNode + // then get tagType to compare to aTagName + // Clone node of each desired type and append it to the aDomFrag + selectedElement = do_QueryInterface(content); + if (selectedElement) + { + // If we already found a node, then we have another element, + // thus there's not just one element selected + if (bNodeFound) + { + bNodeFound = PR_FALSE; + break; + } + + selectedElement->GetNodeName(domTagName); + domTagName.ToLowerCase(); + + if (anyTag) + { + // Get name of first selected element + selectedElement->GetTagName(TagName); + TagName.ToLowerCase(); + anyTag = PR_FALSE; + } + + // The "A" tag is a pain, + // used for both link(href is set) and "Named Anchor" + nsCOMPtr selectedNode = do_QueryInterface(selectedElement); + if ( (isLinkTag && IsLinkNode(selectedNode)) || + (isNamedAnchorTag && IsNamedAnchorNode(selectedNode)) ) + { + bNodeFound = PR_TRUE; + } else if (TagName == domTagName) { // All other tag names are handled here + bNodeFound = PR_TRUE; + } + if (!bNodeFound) + { + // Check if node we have is really part of the selection??? + break; + } + } + iter->Next(); + } + } + } else { + // Should never get here? + isCollapsed = PR_TRUE; + printf("isCollapsed was FALSE, but no elements found in selection\n"); + } + } else { + printf("Could not create enumerator for GetSelectionProperties\n"); + } } } if (bNodeFound) @@ -3758,26 +3816,26 @@ nsHTMLEditor::CreateElementWithDefaults(const nsString& aTagName, nsIDOMElement* return NS_ERROR_FAILURE; // Mark the new element dirty, so it will be formatted - newElement->SetAttribute(NS_ConvertASCIItoUCS2("_moz_dirty"), nsAutoString()); + newElement->SetAttribute(NS_LITERAL_STRING("_moz_dirty"), nsAutoString()); // Set default values for new elements if (TagName.EqualsWithConversion("hr")) { // Note that we read the user's attributes for these from prefs (in InsertHLine JS) - newElement->SetAttribute(NS_ConvertASCIItoUCS2("align"),NS_ConvertASCIItoUCS2("center")); - newElement->SetAttribute(NS_ConvertASCIItoUCS2("width"),NS_ConvertASCIItoUCS2("100%")); - newElement->SetAttribute(NS_ConvertASCIItoUCS2("size"),NS_ConvertASCIItoUCS2("2")); + newElement->SetAttribute(NS_LITERAL_STRING("align"),NS_LITERAL_STRING("center")); + newElement->SetAttribute(NS_LITERAL_STRING("width"),NS_LITERAL_STRING("100%")); + newElement->SetAttribute(NS_LITERAL_STRING("size"),NS_LITERAL_STRING("2")); } else if (TagName.EqualsWithConversion("table")) { - newElement->SetAttribute(NS_ConvertASCIItoUCS2("cellpadding"),NS_ConvertASCIItoUCS2("2")); - newElement->SetAttribute(NS_ConvertASCIItoUCS2("cellspacing"),NS_ConvertASCIItoUCS2("2")); - newElement->SetAttribute(NS_ConvertASCIItoUCS2("border"),NS_ConvertASCIItoUCS2("1")); + newElement->SetAttribute(NS_LITERAL_STRING("cellpadding"),NS_LITERAL_STRING("2")); + newElement->SetAttribute(NS_LITERAL_STRING("cellspacing"),NS_LITERAL_STRING("2")); + newElement->SetAttribute(NS_LITERAL_STRING("border"),NS_LITERAL_STRING("1")); } else if (TagName.EqualsWithConversion("tr")) { - newElement->SetAttribute(NS_ConvertASCIItoUCS2("valign"),NS_ConvertASCIItoUCS2("top")); + newElement->SetAttribute(NS_LITERAL_STRING("valign"),NS_LITERAL_STRING("top")); } else if (TagName.EqualsWithConversion("td")) { - newElement->SetAttribute(NS_ConvertASCIItoUCS2("valign"),NS_ConvertASCIItoUCS2("top")); + newElement->SetAttribute(NS_LITERAL_STRING("valign"),NS_LITERAL_STRING("top")); } // ADD OTHER TAGS HERE @@ -3866,9 +3924,9 @@ nsHTMLEditor::SetBackgroundColor(const nsString& aColor) while(cell) { if (setColor) - res = SetAttribute(cell, NS_ConvertASCIItoUCS2("bgcolor"), aColor); + res = SetAttribute(cell, NS_LITERAL_STRING("bgcolor"), aColor); else - res = RemoveAttribute(cell, NS_ConvertASCIItoUCS2("bgcolor")); + res = RemoveAttribute(cell, NS_LITERAL_STRING("bgcolor")); if (NS_FAILED(res)) break; GetNextSelectedCell(getter_AddRefs(cell), nsnull); @@ -3885,9 +3943,9 @@ nsHTMLEditor::SetBackgroundColor(const nsString& aColor) } // Use the editor method that goes through the transaction system if (setColor) - res = SetAttribute(element, NS_ConvertASCIItoUCS2("bgcolor"), aColor); + res = SetAttribute(element, NS_LITERAL_STRING("bgcolor"), aColor); else - res = RemoveAttribute(element, NS_ConvertASCIItoUCS2("bgcolor")); + res = RemoveAttribute(element, NS_LITERAL_STRING("bgcolor")); return res; } @@ -5196,11 +5254,11 @@ nsHTMLEditor::InsertAsPlaintextQuotation(const nsString& aQuotedText, nsCOMPtr preElement (do_QueryInterface(preNode)); if (preElement) { - preElement->SetAttribute(NS_ConvertASCIItoUCS2("_moz_quote"), - NS_ConvertASCIItoUCS2("true")); + preElement->SetAttribute(NS_LITERAL_STRING("_moz_quote"), + NS_LITERAL_STRING("true")); // set style to not have unwanted vertical margins - preElement->SetAttribute(NS_ConvertASCIItoUCS2("style"), - NS_ConvertASCIItoUCS2("margin: 0 0 0 0px;")); + preElement->SetAttribute(NS_LITERAL_STRING("style"), + NS_LITERAL_STRING("margin: 0 0 0 0px;")); } // and set the selection inside it: @@ -5650,13 +5708,13 @@ nsHTMLEditor::GetHeadContentsAsHTML(nsString& aOutputString) res = SetSelectionAroundHeadChildren(selection, mDocWeak); if (NS_FAILED(res)) return res; - res = OutputToString(aOutputString, NS_ConvertASCIItoUCS2("text/html"), + res = OutputToString(aOutputString, NS_LITERAL_STRING("text/html"), nsIDocumentEncoder::OutputSelectionOnly); if (NS_SUCCEEDED(res)) { // Selection always includes , // so terminate there - PRInt32 offset = aOutputString.Find(NS_ConvertASCIItoUCS2(" 0) { // Ensure the string ends in a newline @@ -6810,7 +6868,7 @@ nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, } // reparent the node inside font node with appropriate relative size - res = InsertContainerAbove(node, &tmp, NS_ConvertASCIItoUCS2( aSizeChange==1 ? "big" : "small" )); + res = InsertContainerAbove(node, &tmp, aSizeChange==1 ? NS_LITERAL_STRING("big") : NS_LITERAL_STRING("small")); return res; } diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index b7fc9bffa05..dd24380ebc0 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -121,6 +121,7 @@ public: NS_IMETHOD InsertHTML(const nsString &aInputString); NS_IMETHOD InsertHTMLWithCharset(const nsString& aInputString, const nsString& aCharset); + NS_IMETHOD RebuildDocumentFromSource(const nsString& aSourceString); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); NS_IMETHOD DeleteSelection(EDirection aAction); diff --git a/mozilla/editor/libeditor/html/nsTableEditor.cpp b/mozilla/editor/libeditor/html/nsTableEditor.cpp index 7e2d3a401c0..f8d0753623d 100644 --- a/mozilla/editor/libeditor/html/nsTableEditor.cpp +++ b/mozilla/editor/libeditor/html/nsTableEditor.cpp @@ -413,6 +413,12 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) //.. so suppress Rules System selection munging nsAutoTxnsConserveSelection dontChangeSelection(this); + // If we are inserting after all existing columns + // Make sure table is "well formed" + // before appending new column + if (startColIndex >= colCount) + NormalizeTable(table); + nsCOMPtr rowElement; for ( rowIndex = 0; rowIndex < rowCount; rowIndex++) { @@ -445,11 +451,6 @@ nsHTMLEditor::InsertTableColumn(PRInt32 aNumber, PRBool aAfter) } } } else { - // We are inserting after all existing columns - // Make sure table is "well formed" - // before appending new column - NormalizeTable(table); - // Get current row and append new cells after last cell in row if(rowIndex == 0) res = GetFirstRow(table.get(), *getter_AddRefs(rowElement)); @@ -3226,7 +3227,7 @@ nsHTMLEditor::GetSelectedCellsType(nsIDOMElement *aElement, PRUint32 &aSelection if (IndexNotTested(&indexArray, startRowIndex)) { indexArray.AppendElement((void*)startColIndex); - allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, colCount); + allCellsInColAreSelected = AllCellsInColumnSelected(table, startColIndex, rowCount); // We're done as soon as we fail for any column if (!allCellsInRowAreSelected) break; } diff --git a/mozilla/editor/public/nsIHTMLEditor.h b/mozilla/editor/public/nsIHTMLEditor.h index 146f048996f..d6c61488848 100644 --- a/mozilla/editor/public/nsIHTMLEditor.h +++ b/mozilla/editor/public/nsIHTMLEditor.h @@ -224,6 +224,14 @@ public: */ NS_IMETHOD InsertHTML(const nsString &aInputString)=0; + + /** Rebuild the entire document from source HTML + * Needed to be able to edit HEAD and other outside-of-BODY content + * + * @param aSourceString HTML source string of the entire new document + */ + NS_IMETHOD RebuildDocumentFromSource(const nsString& aSourceString)=0; + /** * Insert some HTML source, interpreting * the string argument according to the given charset. diff --git a/mozilla/editor/ui/composer/content/ComposerCommands.js b/mozilla/editor/ui/composer/content/ComposerCommands.js index f6c7fd6d0e8..660ed7956a9 100644 --- a/mozilla/editor/ui/composer/content/ComposerCommands.js +++ b/mozilla/editor/ui/composer/content/ComposerCommands.js @@ -54,7 +54,11 @@ function SetupControllerCommands() gComposerCommandManager.registerCommand("cmd_findNext", nsFindNextCommand); gComposerCommandManager.registerCommand("cmd_spelling", nsSpellingCommand); - gComposerCommandManager.registerCommand("cmd_editHTML", nsEditHTMLCommand); + gComposerCommandManager.registerCommand("cmd_NormalMode", nsNormalModeCommand); + gComposerCommandManager.registerCommand("cmd_AllTagsMode", nsAllTagsModeCommand); + gComposerCommandManager.registerCommand("cmd_HTMLSourceMode", nsHTMLSourceModeCommand); + gComposerCommandManager.registerCommand("cmd_PreviewMode", nsPreviewModeCommand); + gComposerCommandManager.registerCommand("cmd_insertChars", nsInsertCharsCommand); gComposerCommandManager.registerCommand("cmd_preferences", nsPreferencesCommand); @@ -91,13 +95,21 @@ function SetupControllerCommands() gComposerCommandManager.registerCommand("cmd_DeleteColumn", nsDeleteTableColumnCommand); gComposerCommandManager.registerCommand("cmd_DeleteCell", nsDeleteTableCellCommand); gComposerCommandManager.registerCommand("cmd_DeleteCellContents", nsDeleteTableCellContentsCommand); - gComposerCommandManager.registerCommand("cmd_tableJoinCells", nsJoinTableCellsCommand); - gComposerCommandManager.registerCommand("cmd_tableSplitCell", nsSplitTableCellCommand); + gComposerCommandManager.registerCommand("cmd_JoinTableCells", nsJoinTableCellsCommand); + gComposerCommandManager.registerCommand("cmd_SplitTableCell", nsSplitTableCellCommand); + gComposerCommandManager.registerCommand("cmd_TableOrCellColor", nsTableOrCellColorCommand); gComposerCommandManager.registerCommand("cmd_NormalizeTable", nsNormalizeTableCommand); gComposerCommandManager.registerCommand("cmd_FinishHTMLSource", nsFinishHTMLSource); gComposerCommandManager.registerCommand("cmd_CancelHTMLSource", nsCancelHTMLSource); } +function SetupHTMLSourceController() +{ + // TODO: Need to write this and register commands + // that we support while in HTML Source mode + return; +} + //----------------------------------------------------------------------------------- function GetComposerController() { @@ -175,8 +187,6 @@ var nsOpenCommand = doCommand: function(aCommand) { -dump("*** nsOpenCommand: doCommand\n"); - var fp = Components.classes["component://mozilla/filepicker"].createInstance(nsIFilePicker); fp.init(window, window.editorShell.GetString("OpenHTMLFile"), nsIFilePicker.modeOpen); @@ -384,7 +394,8 @@ var nsPreviewCommand = doCommand: function(aCommand) { FinishHTMLSource(); - if (CheckAndSaveDocument(window.editorShell.GetString("BeforePreview"))) + // Don't continue if user canceled during prompt for saving + if (!CheckAndSaveDocument(window.editorShell.GetString("BeforePreview"))) return; var fileurl = ""; @@ -639,7 +650,9 @@ var nsInsertCharsCommand = }, doCommand: function(aCommand) { - window.openDialog("chrome://editor/content/EdInsertChars.xul", "_blank", "chrome,close,titlebar", ""); + // Problems using this non-modal. Use modal for now + //window.openDialog("chrome://editor/content/EdInsertChars.xul", "_blank", "chrome,close,titlebar", ""); + window.openDialog("chrome://editor/content/EdInsertChars.xul", "_blank", "chrome,close,titlebar,modal", ""); } }; @@ -704,22 +717,22 @@ var nsObjectPropertiesCommand = if (window.editorShell && window.editorShell.documentEditable) { // Launch Object properties for appropriate selected element - var element = GetSelectedElementOrParentCell(); + var element = GetSelectedElementOrParentCellOrLink(); //dump("nsObjectProperties, isCommandEnabled: element="+element+",TagName="+element.nodeName+"\n"); return (element && (element.nodeName == "img" || element.nodeName == "hr" || element.nodeName == "table" || element.nodeName == "td" || - element.nodeName == "a" || - window.editorShell.GetSelectedElement("href"))); + element.nodeName == "a")) +// window.editorShell.GetSelectedElement("href"))); } return false; }, doCommand: function(aCommand) { // Launch Object properties for appropriate selected element - var element = GetSelectedElementOrParentCell(); + var element = GetSelectedElementOrParentCellOrLink(); if (element) { var name = element.nodeName.toLowerCase(); @@ -789,7 +802,20 @@ var nsColorPropertiesCommand = //----------------------------------------------------------------------------------- -var nsEditHTMLCommand = +var nsNormalModeCommand = +{ + isCommandEnabled: function(aCommand, dummy) + { + return true; //(window.editorShell && window.editorShell.documentEditable); + }, + doCommand: function(aCommand) + { + if (gEditorDisplayMode != DisplayModeNormal) + SetEditMode(DisplayModeNormal); + } +}; + +var nsAllTagsModeCommand = { isCommandEnabled: function(aCommand, dummy) { @@ -797,14 +823,34 @@ var nsEditHTMLCommand = }, doCommand: function(aCommand) { - if (gEditorDisplayMode === DisplayModeSource) - { - SetEditMode(DisplayModeNormal); - } - else - { + if (gEditorDisplayMode != DisplayModeAllTags) + SetEditMode(DisplayModeAllTags); + } +}; + +var nsHTMLSourceModeCommand = +{ + isCommandEnabled: function(aCommand, dummy) + { + return (window.editorShell && window.editorShell.documentEditable); + }, + doCommand: function(aCommand) + { + if (gEditorDisplayMode != DisplayModeSource) SetEditMode(DisplayModeSource); - } + } +}; + +var nsPreviewModeCommand = +{ + isCommandEnabled: function(aCommand, dummy) + { + return (window.editorShell && window.editorShell.documentEditable); + }, + doCommand: function(aCommand) + { + if (gEditorDisplayMode != DisplayModePreview) + SetEditMode(DisplayModePreview); } }; @@ -817,7 +863,6 @@ var nsInsertOrEditTableCommand = }, doCommand: function(aCommand) { -dump("nsInsertOrEditTableCommand\n"); EditorInsertOrEditTable(true); } }; @@ -831,7 +876,6 @@ var nsEditTableCommand = }, doCommand: function(aCommand) { -dump("nsEditTableCommand\n"); EditorInsertOrEditTable(false); } }; @@ -845,7 +889,7 @@ var nsSelectTableCommand = }, doCommand: function(aCommand) { -dump("nsSelectTableCommand\n"); +//dump("nsSelectTableCommand\n"); window.editorShell.SelectTable(); window._content.focus(); } @@ -859,7 +903,7 @@ var nsSelectTableRowCommand = }, doCommand: function(aCommand) { -dump("nsSelectTableRowCommand\n"); +//dump("nsSelectTableRowCommand\n"); window.editorShell.SelectTableRow(); window._content.focus(); } @@ -873,7 +917,7 @@ var nsSelectTableColumnCommand = }, doCommand: function(aCommand) { -dump("nsSelectTableColumnCommand\n"); +//dump("nsSelectTableColumnCommand\n"); window.editorShell.SelectTableColumn(); window._content.focus(); } @@ -887,7 +931,7 @@ var nsSelectTableCellCommand = }, doCommand: function(aCommand) { -dump("nsSelectTableCellCommand\n"); +//dump("nsSelectTableCellCommand\n"); window.editorShell.SelectTableCell(); window._content.focus(); } @@ -901,7 +945,6 @@ var nsSelectAllTableCellsCommand = }, doCommand: function(aCommand) { -dump("nsSelectAllTableCellsCommand\n"); window.editorShell.SelectAllTableCells(); window._content.focus(); } @@ -1019,7 +1062,7 @@ var nsDeleteTableRowCommand = }, doCommand: function(aCommand) { -dump("nsDeleteTableRowCommand: doCommand\n"); +//dump("nsDeleteTableRowCommand: doCommand\n"); window.editorShell.DeleteTableRow(1); window._content.focus(); } @@ -1154,6 +1197,21 @@ var nsSplitTableCellCommand = } }; +//----------------------------------------------------------------------------------- +var nsTableOrCellColorCommand = +{ + isCommandEnabled: function(aCommand, dummy) + { + return IsInTable(); + }, + doCommand: function(aCommand) + { + window.editorShell.SetBackgroundColor(); + window._content.focus(); + } +}; + + //----------------------------------------------------------------------------------- var nsPreferencesCommand = { diff --git a/mozilla/editor/ui/composer/content/EditorCommandsOverlay.xul b/mozilla/editor/ui/composer/content/EditorCommandsOverlay.xul index d8dd9b148c2..18f04aabeff 100644 --- a/mozilla/editor/ui/composer/content/EditorCommandsOverlay.xul +++ b/mozilla/editor/ui/composer/content/EditorCommandsOverlay.xul @@ -47,8 +47,9 @@ - - + + + @@ -100,11 +101,14 @@ + - - +--> + + + diff --git a/mozilla/editor/ui/composer/content/EditorOverride.css b/mozilla/editor/ui/composer/content/EditorOverride.css index 6aee101b1b4..5f42b47221f 100644 --- a/mozilla/editor/ui/composer/content/EditorOverride.css +++ b/mozilla/editor/ui/composer/content/EditorOverride.css @@ -43,6 +43,10 @@ a[name] { cursor: default; } +a:link img, a:visited img, a:active, { + user-input: none; +} + input, select, textarea { user-select: all !important; user-input: none !important; diff --git a/mozilla/editor/ui/composer/content/editor.js b/mozilla/editor/ui/composer/content/editor.js index c242a8765f5..12dc583fea8 100644 --- a/mozilla/editor/ui/composer/content/editor.js +++ b/mozilla/editor/ui/composer/content/editor.js @@ -38,14 +38,13 @@ var DisplayModeAllTags = 2; var DisplayModeSource = 3; var PreviousNonSourceDisplayMode = 1; var gEditorDisplayMode = 1; // Normal Editor mode -var EditModeType = ""; var WebCompose = false; // Set true for Web Composer, leave false for Messenger Composer var docWasModified = false; // Check if clean document, if clean then unload when user "Opens" var gContentWindow = 0; var gSourceContentWindow = 0; var gContentWindowDeck; var gFormatToolbar; -var gFormatToolbarHidden; +var gFormatToolbarHidden = false; var gFormatToolbarCollapsed; var gEditModeBar; // Bummer! Can't get at enums from nsIDocumentEncoder.h @@ -159,10 +158,6 @@ function EditorStartup(editorType, editorElement) gSourceModeButton = document.getElementById("SourceModeButton"); gPreviewModeButton = document.getElementById("PreviewModeButton"); - // The "type" attribute persists, so use that value - // to setup edit mode buttons - ToggleEditModeType(gNormalModeButton.getAttribute("type")); - // XUL elements we use when switching from normal editor to edit source gContentWindowDeck = document.getElementById("ContentWindowDeck"); gFormatToolbar = document.getElementById("FormatToolbar"); @@ -182,7 +177,10 @@ function EditorStartup(editorType, editorElement) editorShell.RegisterDocumentStateListener( DocumentStateListener ); // Startup also used by other editor users, such as Message Composer - EditorSharedStartup() + EditorSharedStartup(); + + // HTML Source editor is not shared with Mail, IM + SetupHTMLSourceController(); // set up our global prefs object GetPrefsService(); @@ -223,6 +221,10 @@ function EditorSharedStartup() // And add "Del" or "Clear" SafeSetAttribute("menu_DeleteCellContents", "acceltext", delStr); + // Remove a Privacy menu that causes problems + // (method is in tasksOverlay.js) + HideImage(); + // hide UI that we don't have components for RemoveInapplicableUIElements(); } @@ -745,11 +747,13 @@ function EditorRemoveLinks() */ // For property dialogs, we want the selected element, -// but will accept a parent table cell if inside one -function GetSelectedElementOrParentCell() +// but will accept a parent table cell or link if inside one +function GetSelectedElementOrParentCellOrLink() { //dump("GetSelectedElementOrParentCell\n"); var element = editorShell.GetSelectedElement(""); + if (!element) + element = editorShell.GetElementOrParentByTagName("href",null); if (!element) element = editorShell.GetElementOrParentByTagName("td",null); @@ -768,13 +772,6 @@ function SetEditMode(mode) { if (gIsHTMLEditor) { - // Be sure toolbar is visible - gEditModeBar.setAttribute("hidden", ""); - gEditModeBar.setAttribute("collapsed", ""); - // Remember the state - document.persist("EditModeToolbar","hidden"); - document.persist("EditModeToolbar","collapsed"); - var bodyNode = editorShell.editorDocument.getElementsByTagName("body").item(0); if (!bodyNode) { @@ -795,8 +792,9 @@ function SetEditMode(mode) var childCount = bodyNode.childNodes.length; if( childCount) { - gSourceContentWindow.setAttribute("value",editorShell.GetContentsAs("text/html", gOutputBodyOnly)); + gSourceContentWindow.setAttribute("value",editorShell.GetContentsAs("text/html", 0)); //gOutputBodyOnly)); gSourceContentWindow.focus(); + // Note: We can't set the caret location in a multiline textfield return; } } @@ -807,10 +805,15 @@ function SetEditMode(mode) { // We are comming from edit source mode, // so transfer that back into the document + //TODO: THIS IS NOT WORKING YET! + editorShell.RebuildDocumentFromSource(gSourceContentWindow.value); +/* editorShell.SelectAll(); editorShell.InsertSource(gSourceContentWindow.value); +*/ // Clear out the source editor buffer gSourceContentWindow.value = ""; + // reset selection to top of doc (wish we could preserve it!) if (bodyNode) editorShell.editorSelection.collapse(bodyNode, 0); @@ -892,19 +895,18 @@ function SetDisplayMode(mode) // Hide menus that are completely disabled // Note: ShowMenuItem is implemented in EditorContextMenu.js - ShowMenuItem("editMenu", false); - ShowMenuItem("viewMenu", false); - ShowMenuItem("insertMenu", false); - ShowMenuItem("formatMenu", false); - ShowMenuItem("tableMenu", false); - -/* CollapseItem("editMenu", true); - CollapseItem("viewMenu", true); CollapseItem("insertMenu", true); CollapseItem("formatMenu", true); CollapseItem("tableMenu", true); -*/ + + // Collapse allitems in the view menu except mode switch items + SetViewMenuForHTMLSource(true); + + DisableItem("viewToolbar", true); + DisableItem("viewSep1", true); + DisableItem("viewSep1", true); + DisableItem("composerCharsetMenu", true); DisableItem("findButton", true); DisableItem("spellingButton", true); @@ -921,7 +923,6 @@ function SetDisplayMode(mode) gFormatToolbar.setAttribute("hidden", "true"); } - // THIS DOESN'T WORK! gSourceContentWindow.focus(); } else @@ -930,19 +931,13 @@ function SetDisplayMode(mode) gContentWindowDeck.setAttribute("index","0"); // Restore menus and toolbars - ShowMenuItem("editMenu", true); - ShowMenuItem("viewMenu", true); - ShowMenuItem("insertMenu", true); - ShowMenuItem("formatMenu", true); - ShowMenuItem("tableMenu", true); - -/* CollapseItem("editMenu", false); - CollapseItem("viewMenu", false); CollapseItem("insertMenu", false); CollapseItem("formatMenu", false); CollapseItem("tableMenu", false); -*/ + + SetViewMenuForHTMLSource(false); + DisableItem("findButton", false); DisableItem("spellingButton", false); DisableItem("imageButton", false); @@ -963,40 +958,47 @@ dump("Switching back to visible toolbar. gFormatToolbarHidden = "+gFormatToolbar } } -function ToggleEditModeType() +// We disable all items in View menu except Edit mode and sidebar items +function SetViewMenuForHTMLSource(disable) { - if (gIsHTMLEditor) + var viewMenu = document.getElementById("viewMenu"); + // menuitems are children of the menupopup child + var children = viewMenu.firstChild.childNodes; + for (var i = 0; i < children.length; i++) { - if (EditModeType == "text") + var item = children.item(i); + if (item.id != "viewNormalMode" && + item.id != "viewAllTagsMode" && + item.id != "viewSourceMode" && + item.id != "viewPreviewMode" && + item.id != "sidebar-menu") { - EditModeType = "image"; - gNormalModeButton.setAttribute("value",""); - gTagModeButton.setAttribute("value",""); - gSourceModeButton.setAttribute("value",""); - gPreviewModeButton.setAttribute("value",""); - // Advanced users don't need to see the label (cleaner look) - gEditModeLabel.setAttribute("hidden","true"); + DisableItem(item.id, disable); } - else - { - EditModeType = "text"; - gNormalModeButton.setAttribute("value","Normal"); - gTagModeButton.setAttribute("value","Show All Tags"); - gSourceModeButton.setAttribute("value","HTML Source"); - gPreviewModeButton.setAttribute("value","Edit Preview"); - gEditModeLabel.setAttribute("hidden",""); - } - - gNormalModeButton.setAttribute("type",EditModeType); - gTagModeButton.setAttribute("type",EditModeType); - gSourceModeButton.setAttribute("type",EditModeType); - gPreviewModeButton.setAttribute("type",EditModeType); - - // Remember the state - document.persist("NormalModeButton","type"); } } +function DisableMenuItem(id, disable) +{ +dump("DisableMenuItem: item id="+id+": "+disable+"\n"); + var item = document.getElementById(id); + if (item) + { + if(disable != (item.getAttribute("disabled") == true)) + { + if (disable) + item.setAttribute("disabled", disable); + else + { +dump("Remove disable\n"); + item.removeAttribute("disabled"); + } + } + } + else + dump("DisableMenuItem: item id="+id+" not found\n"); +} + function EditorToggleParagraphMarks() { var menuItem = document.getElementById("viewParagraphMarks"); @@ -1208,16 +1210,13 @@ function getUnicharPref(aPrefName, aDefVal) function EditorInitFormatMenu() { - // Set the string for the background color menu item - SetBackColorString("backgroundColorMenu"); - // Set strings and enable for the [Object] Properties item // Note that we directly do the enabling instead of // using goSetCommandEnabled since we already have the menuitem var menuItem = document.getElementById("objectProperties"); if (menuItem) { - var element = GetSelectedElementOrParentCell(); + var element = GetSelectedElementOrParentCellOrLink(); var menuStr = GetString("ObjectProperties"); if (element && element.nodeName) { @@ -1259,7 +1258,7 @@ function EditorInitFormatMenu() } } -function SetBackColorString(xulElementID) +function SetBackColorString(xulElementID, allowPageBackground) { var xulElement = document.getElementById(xulElementID); if (xulElement) @@ -1273,16 +1272,17 @@ function SetBackColorString(xulElementID) textVal = GetString("TableBackColor"); else if (tagNameObj.value == "td") textVal = GetString("CellBackColor"); - else + else if (allowPageBackground) textVal = GetString("PageBackColor"); - xulElement.setAttribute("value",textVal); + if (textVal) + xulElement.setAttribute("value",textVal); } } -function InitBackColorPopup() +function InitBackColorPopup(allowPageBackground) { - SetBackColorString("BackColorCaption"); + SetBackColorString("BackColorCaption", allowPageBackground); } function InitParagraphMenu() @@ -1423,7 +1423,7 @@ function EditorSetDefaultPrefs() node = nodelist.item(i); if ( node ) { - var value = node.getAttribute("name"); + var value = node.getAttribute("name").toLowerCase(); if (value == "author") { authorFound = true; @@ -1445,7 +1445,7 @@ function EditorSetDefaultPrefs() var element = domdoc.createElement("meta"); if ( element ) { - AddAttrToElem(domdoc, "name", "Author", element); + AddAttrToElem(domdoc, "name", "author", element); AddAttrToElem(domdoc, "content", prefAuthorString, element); headelement.appendChild( element ); } @@ -1742,6 +1742,9 @@ function IsFindInstalled() //----------------------------------------------------------------------------------- function RemoveInapplicableUIElements() { + // For items that are in their own menu block, remove associated separator + // (we can't use "hidden" since class="hide-in-IM" CSS rule interferes) + // if no find, remove find ui if (!IsFindInstalled()) { @@ -1755,10 +1758,11 @@ function RemoveInapplicableUIElements() if (findMenuItem) findMenuItem.setAttribute("hidden", "true"); - var findSepItem = document.getElementById("sep_find"); - if (findSepItem) - findSepItem.parentNode.removeChild(findSepItem); - } + var findSepItem = document.getElementById("sep_find"); + if (findSepItem) + findSepItem.parentNode.removeChild(findSepItem); + } + // if no spell checker, remove spell checker ui if (!IsSpellCheckerInstalled()) { @@ -1772,40 +1776,10 @@ function RemoveInapplicableUIElements() if (spellingMenuItem) spellingMenuItem.setAttribute("hidden", "true"); - // Spelling item is in its own menu block, so remove it - // (we can't use "hidden" since class="hide-in-IM" CSS rule overrides!) var spellingSepItem = document.getElementById("sep_checkspelling"); if (spellingSepItem) spellingSepItem.parentNode.removeChild(spellingSepItem); } -/* - // if no spell checker, remove spell checker ui - if (!IsSpellCheckerInstalled()) - { - dump("Removing spell checker items\n"); - - // Completely remove UI elements intended for spelling - // This allows HTMLSource to use "hidden" to turn items off/on - // and also needed because of class="hide-in-IM" CSS rule used by AIM - - // First, remove the command node used by both button and menuitem - var spellingCommand = document.getElementById("cmd_spelling"); - if (spellingCommand) - spellingCommand.parentNode.removeChild(spellingCommand); - - var spellingButton = document.getElementById("spellingButton"); - if (spellingButton) - spellingButton.parentNode.removeChild(spellingButton); - - var spellingMenuItem = document.getElementById("menu_checkspelling"); - if (spellingMenuItem) - spellingMenuItem.parentNode.removeChild(spellingMenuItem); - - var spellingSepItem = document.getElementById("sep_checkspelling"); - if (spellingSepItem) - spellingSepItem.parentNode.removeChild(spellingSepItem); - } -*/ } function onEditorFocus() @@ -1852,7 +1826,12 @@ function EditorInitTableMenu() else menuText = GetString("JoinCellToRight"); - document.getElementById("menu_tableJoinCells").setAttribute("value",menuText); + document.getElementById("menu_JoinTableCells").setAttribute("value",menuText); + document.getElementById("menu_JoinTableCells").setAttribute("accesskey","j"); + + // Change text on background item to "Table..." or "Cell..." + // "false" means don't allow "Page Background..." string + SetBackColorString("menu_TableOrCellColor", false); // Set enable states for all table commands goUpdateTableMenuItems(document.getElementById("composerTableMenuItems")); @@ -1885,15 +1864,16 @@ function goUpdateTableMenuItems(commandset) if (commandID) { if (commandID == "cmd_InsertTable" || - commandID == "cmd_tableJoinCells" || - commandID == "cmd_tableSplitCell") + commandID == "cmd_JoinTableCells" || + commandID == "cmd_SplitTableCell") { // Call the update method in the command class goUpdateCommand(commandID); } // Directly set with the values calculated here else if (commandID == "cmd_DeleteTable" || - commandID == "cmd_NormalizeTable") + commandID == "cmd_NormalizeTable" || + commandID == "cmd_TableOrCellColor") { goSetCommandEnabled(commandID, enabledIfTable); } else { diff --git a/mozilla/editor/ui/composer/content/editor.xul b/mozilla/editor/ui/composer/content/editor.xul index bac935d9e03..e6170ff9d52 100644 --- a/mozilla/editor/ui/composer/content/editor.xul +++ b/mozilla/editor/ui/composer/content/editor.xul @@ -110,9 +110,12 @@ - - - + + + + + + @@ -150,7 +153,6 @@ - - + + - - - - - - - - + + + + + + + + + + + + + @@ -354,7 +363,7 @@ - + @@ -605,12 +614,17 @@ + - - + + + + + - + @@ -631,7 +645,7 @@