diff --git a/editor/base/nsHTMLEditRules.cpp b/editor/base/nsHTMLEditRules.cpp
index 3ea8ba43fac..87fbc7b100e 100644
--- a/editor/base/nsHTMLEditRules.cpp
+++ b/editor/base/nsHTMLEditRules.cpp
@@ -18,20 +18,31 @@
#include "nsHTMLEditRules.h"
#include "nsEditor.h"
+#include "nsTextEditor.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
+#include "nsIContent.h"
+#include "nsIContentIterator.h"
#include "nsIDOMNode.h"
+#include "nsIDOMText.h"
#include "nsIDOMElement.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMSelection.h"
#include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIEnumerator.h"
+#include "nsIStyleContext.h"
+#include "nsIPresShell.h"
+#include "nsLayoutCID.h"
+
+class nsIFrame;
const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
const static char* kMOZEditorBogusNodeValue="TRUE";
+const unsigned char nbsp = nbsp;
static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID);
+static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
nsIAtom *nsHTMLEditRules::sAAtom;
nsIAtom *nsHTMLEditRules::sAddressAtom;
@@ -61,7 +72,7 @@ nsIAtom *nsHTMLEditRules::sUAtom;
nsIAtom *nsHTMLEditRules::sVarAtom;
nsIAtom *nsHTMLEditRules::sWbrAtom;
-PRInt32 nsHTMLEditRules::sInstanceCount;
+PRInt32 nsHTMLEditRules::sInstanceCount = 0;
/********************************************************
* Constructor/Destructor
@@ -69,8 +80,11 @@ PRInt32 nsHTMLEditRules::sInstanceCount;
nsHTMLEditRules::nsHTMLEditRules()
{
+ // XXX: this sux. We are dependant on layout's private conception of tag atom names.
+
if (sInstanceCount <= 0)
{
+ // inline tags
sAAtom = NS_NewAtom("a");
sAddressAtom = NS_NewAtom("address");
sBigAtom = NS_NewAtom("big");
@@ -349,44 +363,60 @@ nsHTMLEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult)
* helper methods
********************************************************/
+///////////////////////////////////////////////////////////////////////////
+// IsBlockNode: true if this node is an html block node
+//
PRBool
-nsHTMLEditRules::IsBlockNode(nsCOMPtr aContent)
+nsHTMLEditRules::IsBlockNode(nsIDOMNode *aNode)
{
nsIAtom* atom = nsnull;
PRBool result;
- aContent->GetTag(atom);
+ if (!aNode)
+ {
+ NS_NOTREACHED("null node passed to IsBlockNode()");
+ return PR_FALSE;
+ }
+
+ nsCOMPtr content = do_QueryInterface(aNode);
+ if (!content)
+ {
+ NS_NOTREACHED("could not get content node in IsBlockNode()");
+ return PR_FALSE;
+ }
+
+ content->GetTag(atom);
if (!atom)
return PR_TRUE;
if (sAAtom != atom &&
- sAddressAtom != atom &&
- sBigAtom != atom &&
- sBlinkAtom != atom &&
- sBAtom != atom &&
- sCiteAtom != atom &&
- sCodeAtom != atom &&
- sDfnAtom != atom &&
- sEmAtom != atom &&
- sFontAtom != atom &&
- sIAtom != atom &&
- sKbdAtom != atom &&
- sKeygenAtom != atom &&
- sNobrAtom != atom &&
- sSAtom != atom &&
- sSampAtom != atom &&
- sSmallAtom != atom &&
- sSpacerAtom != atom &&
- sSpanAtom != atom &&
- sStrikeAtom != atom &&
- sStrongAtom != atom &&
- sSubAtom != atom &&
- sSupAtom != atom &&
- sTtAtom != atom &&
- sUAtom != atom &&
- sVarAtom != atom &&
- sWbrAtom != atom)
+ sAddressAtom != atom &&
+ sBigAtom != atom &&
+ sBlinkAtom != atom &&
+ sBAtom != atom &&
+ sCiteAtom != atom &&
+ sCodeAtom != atom &&
+ sDfnAtom != atom &&
+ sEmAtom != atom &&
+ sFontAtom != atom &&
+ sIAtom != atom &&
+ sKbdAtom != atom &&
+ sKeygenAtom != atom &&
+ sNobrAtom != atom &&
+ sSAtom != atom &&
+ sSampAtom != atom &&
+ sSmallAtom != atom &&
+ sSpacerAtom != atom &&
+ sSpanAtom != atom &&
+ sStrikeAtom != atom &&
+ sStrongAtom != atom &&
+ sSubAtom != atom &&
+ sSupAtom != atom &&
+ sTtAtom != atom &&
+ sUAtom != atom &&
+ sVarAtom != atom &&
+ sWbrAtom != atom)
{
result = PR_TRUE;
}
@@ -398,19 +428,31 @@ nsHTMLEditRules::IsBlockNode(nsCOMPtr aContent)
return result;
}
-nsCOMPtr
-nsHTMLEditRules::GetBlockNodeParent(nsCOMPtr aContent)
+
+///////////////////////////////////////////////////////////////////////////
+// IsInlineNode: true if this node is an html inline node
+//
+PRBool
+nsHTMLEditRules::IsInlineNode(nsIDOMNode *aNode)
{
- nsCOMPtr p;
+ return !IsBlockNode(aNode);
+}
- if (NS_FAILED(aContent->GetParent(*getter_AddRefs(p)))) // no parent, ran off top of tree
- return aContent;
+///////////////////////////////////////////////////////////////////////////
+// GetBlockNodeParent: returns enclosing block level ancestor, if any
+// else returns the node itself
+nsCOMPtr
+nsHTMLEditRules::GetBlockNodeParent(nsIDOMNode *aNode)
+{
+ nsCOMPtr tmp = do_QueryInterface(aNode);
+ nsCOMPtr p;
- nsCOMPtr tmp;
+ if (NS_FAILED(aNode->GetParentNode(getter_AddRefs(p)))) // no parent, ran off top of tree
+ return tmp;
while (p && !IsBlockNode(p))
{
- if (NS_FAILED(p->GetParent(*getter_AddRefs(tmp)))) // no parent, ran off top of tree
+ if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp)))) // no parent, ran off top of tree
return p;
p = tmp;
@@ -418,6 +460,121 @@ nsHTMLEditRules::GetBlockNodeParent(nsCOMPtr aContent)
return p;
}
+
+///////////////////////////////////////////////////////////////////////////
+// HasSameBlockNodeParent: true if nodes have same block level ancestor
+//
+PRBool
+nsHTMLEditRules::HasSameBlockNodeParent(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
+{
+ if (!aNode1 || !aNode2)
+ {
+ NS_NOTREACHED("null node passed to HasSameBlockNodeParent()");
+ return PR_FALSE;
+ }
+
+ if (aNode1 == aNode2)
+ return PR_TRUE;
+
+ nsCOMPtr p1 = GetBlockNodeParent(aNode1);
+ nsCOMPtr p2 = GetBlockNodeParent(aNode2);
+
+ return (p1 == p2);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// IsTextOrElementNode: true if node of dom type element or text
+//
+PRBool
+nsHTMLEditRules::IsTextOrElementNode(nsIDOMNode *aNode)
+{
+ if (!aNode)
+ {
+ NS_NOTREACHED("null node passed to IsTextOrElementNode()");
+ return PR_FALSE;
+ }
+
+ PRUint16 nodeType;
+ aNode->GetNodeType(&nodeType);
+ if ((nodeType == nsIDOMNode::ELEMENT_NODE) || (nodeType == nsIDOMNode::TEXT_NODE))
+ return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// IsTextNode: true if node of dom type text
+//
+PRBool
+nsHTMLEditRules::IsTextNode(nsIDOMNode *aNode)
+{
+ if (!aNode)
+ {
+ NS_NOTREACHED("null node passed to IsTextNode()");
+ return PR_FALSE;
+ }
+
+ PRUint16 nodeType;
+ aNode->GetNodeType(&nodeType);
+ if (nodeType == nsIDOMNode::TEXT_NODE)
+ return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// NextNodeInBlock: gets the next/prev node in the block, if any. Next node
+// must be an element or text node, others are ignored
+nsCOMPtr
+nsHTMLEditRules::NextNodeInBlock(nsIDOMNode *aNode, IterDirection aDir)
+{
+ nsCOMPtr nullNode;
+ nsCOMPtr content;
+ nsCOMPtr blockContent;
+ nsCOMPtr node;
+ nsCOMPtr blockParent;
+
+ if (!aNode) return nullNode;
+
+ nsCOMPtr iter;
+ if (NS_FAILED(nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
+ nsIContentIterator::GetIID(),
+ getter_AddRefs(iter))))
+ return nullNode;
+
+ // much gnashing of teeth as we twit back and forth between content and domnode types
+ content = do_QueryInterface(aNode);
+ blockParent = GetBlockNodeParent(aNode);
+ if (!blockParent) return nullNode;
+ blockContent = do_QueryInterface(blockParent);
+ if (!blockContent) return nullNode;
+
+ if (NS_FAILED(iter->Init(blockContent))) return nullNode;
+ if (NS_FAILED(iter->PositionAt(content))) return nullNode;
+
+ while (NS_COMFALSE == iter->IsDone())
+ {
+ if (NS_FAILED(iter->CurrentNode(getter_AddRefs(content)))) return nullNode;
+ // ignore nodes that aren't elements or text, or that are the block parent
+ node = do_QueryInterface(content);
+ if (node && IsTextOrElementNode(node) && (node != blockParent))
+ return node;
+
+ if (aDir == kIterForward)
+ iter->Next();
+ else
+ iter->Prev();
+ }
+
+ return nullNode;
+}
+
+
///////////////////////////////////////////////////////////////////////////
// GetStartNode: returns whatever the start parent is of the first range
// in the selection.
@@ -457,13 +614,33 @@ nsHTMLEditRules::GetStartNodeAndOffset(nsIDOMSelection *aSelection,
// IsPreformatted: checks the style info for the node for the preformatted
// text style.
nsresult
-nsHTMLEditRules::IsPreformatted(nsCOMPtr aNode, PRBool *aResult)
+nsHTMLEditRules::IsPreformatted(nsIDOMNode *aNode, PRBool *aResult)
{
- // XXX not yet impl
- if (!aResult)
- return NS_ERROR_NULL_POINTER;
-
- *aResult = PR_TRUE;
+ nsIPresShell* shell = nsnull;
+ nsresult result;
+ nsCOMPtr content = do_QueryInterface(aNode);
+ nsIFrame *frame;
+ nsCOMPtr styleContext;
+ const nsStyleText* styleText;
+ PRBool bPreformatted;
+
+ if (!aResult || !content) return NS_ERROR_NULL_POINTER;
+
+ result = mEditor->GetPresShell(&shell);
+ if (NS_FAILED(result)) return result;
+
+ result = shell->GetPrimaryFrameFor(content, &frame);
+ if (NS_FAILED(result)) return result;
+
+ result = shell->GetStyleContextFor(frame, getter_AddRefs(styleContext));
+ if (NS_FAILED(result)) return result;
+
+ styleText = (const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text);
+
+ bPreformatted = (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
+ (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace);
+
+ *aResult = bPreformatted;
return NS_OK;
}
@@ -472,15 +649,54 @@ nsHTMLEditRules::IsPreformatted(nsCOMPtr aNode, PRBool *aResult)
// IsNextCharWhitespace: checks the adjacent content in the same block
// to see if following selection is whitespace
nsresult
-nsHTMLEditRules::IsNextCharWhitespace(nsCOMPtr aParentNode,
+nsHTMLEditRules::IsNextCharWhitespace(nsIDOMNode *aParentNode,
PRInt32 aOffset,
PRBool *aResult)
{
- // XXX not yet impl
- if (!aResult)
- return NS_ERROR_NULL_POINTER;
-
- *aResult = PR_TRUE;
+ if (!aResult) return NS_ERROR_NULL_POINTER;
+ *aResult = PR_FALSE;
+
+ nsString tempString;
+ PRUint32 strLength;
+ nsCOMPtr textNode = do_QueryInterface(aParentNode);
+ if (textNode)
+ {
+ textNode->GetLength(&strLength);
+ if (aOffset < strLength)
+ {
+ // easy case: next char is in same node
+ textNode->SubstringData(aOffset,aOffset+1,tempString);
+ *aResult = nsString::IsSpace(tempString.First());
+ return NS_OK;
+ }
+ }
+
+ // harder case: next char in next node.
+ nsCOMPtr node = NextNodeInBlock(aParentNode, kIterForward);
+ while (node)
+ {
+ if (!IsInlineNode(node)) // skip over bold, italic, link, ect nodes
+ {
+ if (IsTextNode(node))
+ {
+ textNode = do_QueryInterface(node);
+ textNode->GetLength(&strLength);
+ if (strLength)
+ {
+ textNode->SubstringData(0,1,tempString);
+ *aResult = nsString::IsSpace(tempString.First());
+ return NS_OK;
+ }
+ // else it's an empty text node, skip it.
+ }
+ else // node is an image or some other thingy that doesn't count as whitespace
+ {
+ break;
+ }
+ }
+ node = NextNodeInBlock(aParentNode, kIterForward);
+ }
+
return NS_OK;
}
@@ -489,15 +705,81 @@ nsHTMLEditRules::IsNextCharWhitespace(nsCOMPtr aParentNode,
// IsPrevCharWhitespace: checks the adjacent content in the same block
// to see if following selection is whitespace
nsresult
-nsHTMLEditRules::IsPrevCharWhitespace(nsCOMPtr aParentNode,
+nsHTMLEditRules::IsPrevCharWhitespace(nsIDOMNode *aParentNode,
PRInt32 aOffset,
PRBool *aResult)
{
- // XXX not yet impl
- if (!aResult)
- return NS_ERROR_NULL_POINTER;
-
- *aResult = PR_TRUE;
+ if (!aResult) return NS_ERROR_NULL_POINTER;
+ *aResult = PR_FALSE;
+
+ nsString tempString;
+ PRUint32 strLength;
+ nsCOMPtr textNode = do_QueryInterface(aParentNode);
+ if (textNode)
+ {
+ if (aOffset > 0)
+ {
+ // easy case: prev char is in same node
+ textNode->SubstringData(aOffset-1,aOffset,tempString);
+ *aResult = nsString::IsSpace(tempString.First());
+ return NS_OK;
+ }
+ }
+
+ // harder case: prev char in next node
+ nsCOMPtr node = NextNodeInBlock(aParentNode, kIterBackward);
+ while (node)
+ {
+ if (!IsInlineNode(node)) // skip over bold, italic, link, ect nodes
+ {
+ if (IsTextNode(node))
+ {
+ textNode = do_QueryInterface(node);
+ textNode->GetLength(&strLength);
+ if (strLength)
+ {
+ textNode->SubstringData(strLength-1,strLength,tempString);
+ *aResult = nsString::IsSpace(tempString.First());
+ return NS_OK;
+ }
+ // else it's an empty text node, skip it.
+ }
+ else // node is an image or some other thingy that doesn't count as whitespace
+ {
+ break;
+ }
+ }
+ // otherwise we found a node we want to skip, keep going
+ node = NextNodeInBlock(aParentNode, kIterBackward);
+ }
+
+ return NS_OK;
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// GetTabAsNBSPs: stuff the right number of nbsp's into outString
+//
+nsresult
+nsHTMLEditRules::GetTabAsNBSPs(nsString *outString)
+{
+ if (!outString) return NS_ERROR_NULL_POINTER;
+ // XXX - this should get the right number from prefs
+ *outString += nbsp; *outString += nbsp; *outString += nbsp; *outString += nbsp;
+ return NS_OK;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// GetTabAsNBSPsAndSpace: stuff the right number of nbsp's followed by a
+// space into outString
+nsresult
+nsHTMLEditRules::GetTabAsNBSPsAndSpace(nsString *outString)
+{
+ if (!outString) return NS_ERROR_NULL_POINTER;
+ // XXX - this should get the right number from prefs
+ *outString += nbsp; *outString += nbsp; *outString += nbsp; *outString += ' ';
return NS_OK;
}
@@ -516,35 +798,57 @@ nsHTMLEditRules::InsertTab(nsIDOMSelection *aSelection,
PlaceholderTxn **aTxn,
nsString *outString)
{
- nsCOMPtr theNode;
- PRBool isPRE;
+ nsCOMPtr parentNode;
PRInt32 offset;
+ PRBool isPRE;
+ PRBool isNextWhiteSpace;
+ PRBool isPrevWhiteSpace;
- nsresult result = GetStartNodeAndOffset(aSelection, &theNode, &offset);
- if (NS_FAILED(result))
- return result;
+ nsresult result = GetStartNodeAndOffset(aSelection, &parentNode, &offset);
+ if (NS_FAILED(result)) return result;
- if (!theNode)
- return NS_ERROR_FAILURE;
+ if (!parentNode) return NS_ERROR_FAILURE;
- result = IsPreformatted(theNode,&isPRE);
- if (NS_FAILED(result))
- return result;
+ result = IsPreformatted(parentNode,&isPRE);
+ if (NS_FAILED(result)) return result;
if (isPRE)
{
- outString += '\t';
- // we're done - let everything fall through to the InsertText code
- // in nsTextEditor which will insert the tab as is.
+ *outString += '\t';
+ return NS_OK;
}
- else
+
+ result = IsNextCharWhitespace(parentNode, offset, &isNextWhiteSpace);
+ if (NS_FAILED(result)) return result;
+
+ result = IsPrevCharWhitespace(parentNode, offset, &isPrevWhiteSpace);
+ if (NS_FAILED(result)) return result;
+
+ if (isPrevWhiteSpace)
{
- // XXX fix me
- *outString += 160;
- *outString += 160;
- *outString += 160;
- *outString += 160;
+ // prev character is a whitespace; Need to
+ // insert nbsp's BEFORE the space
+
+ // XXX for now put tab in wrong place
+ if (isNextWhiteSpace)
+ {
+ GetTabAsNBSPs(outString);
+ return NS_OK;
+ }
+ GetTabAsNBSPsAndSpace(outString);
+ return NS_OK;
}
+
+ if (isNextWhiteSpace)
+ {
+ // character after us is ws. insert nbsps
+ GetTabAsNBSPs(outString);
+ return NS_OK;
+ }
+
+ // else we are in middle of a word; use n-1 nbsp's plus a space
+ GetTabAsNBSPsAndSpace(outString);
+
return NS_OK;
}
@@ -560,41 +864,52 @@ nsHTMLEditRules::InsertSpace(nsIDOMSelection *aSelection,
{
nsCOMPtr parentNode;
PRInt32 offset;
- PRBool isWhiteSpace;
+ PRBool isPRE;
+ PRBool isNextWhiteSpace;
+ PRBool isPrevWhiteSpace;
nsresult result = GetStartNodeAndOffset(aSelection, &parentNode, &offset);
- if (NS_FAILED(result))
- return result;
+ if (NS_FAILED(result)) return result;
- if (!parentNode)
- return NS_ERROR_FAILURE;
+ if (!parentNode) return NS_ERROR_FAILURE;
- result = IsNextCharWhitespace(parentNode, offset, &isWhiteSpace);
- if (NS_FAILED(result))
- return result;
+ result = IsPreformatted(parentNode,&isPRE);
+ if (NS_FAILED(result)) return result;
- if (isWhiteSpace)
+ if (isPRE)
{
- // next character after us is a whitespace, so we need to insert nbsp
- *outString += 160;
+ *outString += " ";
return NS_OK;
}
- result = IsPrevCharWhitespace(parentNode, offset, &isWhiteSpace);
- if (NS_FAILED(result))
- return result;
+ result = IsNextCharWhitespace(parentNode, offset, &isNextWhiteSpace);
+ if (NS_FAILED(result)) return result;
- if (isWhiteSpace)
+ result = IsPrevCharWhitespace(parentNode, offset, &isPrevWhiteSpace);
+ if (NS_FAILED(result)) return result;
+
+ if (isPrevWhiteSpace)
{
- // character after us is non-ws, but char before is ws. Need to
+ // prev character is a whitespace; Need to
// insert nbsp BEFORE the space
- // XXX fornow just nbsp
-
- *outString += 160;
+ // XXX for now put in wrong place
+ if (isNextWhiteSpace)
+ {
+ *outString += nbsp;
+ return NS_OK;
+ }
+ *outString += nbsp;
return NS_OK;
}
+ if (isNextWhiteSpace)
+ {
+ // character after us is ws. insert nbsp
+ *outString += nbsp;
+ return NS_OK;
+ }
+
// else just a space
*outString += " ";
diff --git a/editor/base/nsHTMLEditRules.h b/editor/base/nsHTMLEditRules.h
index 9d46ce5f734..b180b0d1e7d 100644
--- a/editor/base/nsHTMLEditRules.h
+++ b/editor/base/nsHTMLEditRules.h
@@ -21,7 +21,6 @@
#include "nsTextEditRules.h"
#include "nsCOMPtr.h"
-#include "nsIContent.h"
class nsHTMLEditRules : public nsTextEditRules
{
@@ -39,8 +38,15 @@ public:
{
kInsertBreak = 3000
};
+
protected:
+ enum IterDirection
+ {
+ kIterForward,
+ kIterBackward
+ };
+
// nsHTMLEditRules implementation methods
nsresult WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
@@ -57,12 +63,21 @@ protected:
nsresult InsertSpace(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
// helper methods
- PRBool IsBlockNode(nsCOMPtr aContent);
- nsCOMPtr GetBlockNodeParent(nsCOMPtr aContent);
- nsresult GetStartNodeAndOffset(nsIDOMSelection *aSelection, nsCOMPtr *outStartNode, PRInt32 *outStartOffset);
- nsresult IsPreformatted(nsCOMPtr aNode, PRBool *aResult);
- nsresult IsNextCharWhitespace(nsCOMPtr aParentNode, PRInt32 aOffset, PRBool *aResult);
- nsresult IsPrevCharWhitespace(nsCOMPtr aParentNode, PRInt32 aOffset, PRBool *aResult);
+ static nsresult GetTabAsNBSPs(nsString *outString);
+ static nsresult GetTabAsNBSPsAndSpace(nsString *outString);
+ static PRBool IsInlineNode(nsIDOMNode *aNode);
+ static PRBool IsBlockNode(nsIDOMNode *aNode);
+ static nsCOMPtr GetBlockNodeParent(nsIDOMNode *aNode);
+ static PRBool HasSameBlockNodeParent(nsIDOMNode *aNode1, nsIDOMNode *aNode2);
+ static PRBool IsTextOrElementNode(nsIDOMNode *aNode);
+ static PRBool IsTextNode(nsIDOMNode *aNode);
+ static nsCOMPtr NextNodeInBlock(nsIDOMNode *aNode, IterDirection aDir);
+ static nsresult GetStartNodeAndOffset(nsIDOMSelection *aSelection, nsCOMPtr *outStartNode, PRInt32 *outStartOffset);
+
+ nsresult IsPreformatted(nsIDOMNode *aNode, PRBool *aResult);
+ nsresult IsNextCharWhitespace(nsIDOMNode *aParentNode, PRInt32 aOffset, PRBool *aResult);
+ nsresult IsPrevCharWhitespace(nsIDOMNode *aParentNode, PRInt32 aOffset, PRBool *aResult);
+
// data
static nsIAtom *sAAtom;
diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp
index 3ea8ba43fac..87fbc7b100e 100644
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -18,20 +18,31 @@
#include "nsHTMLEditRules.h"
#include "nsEditor.h"
+#include "nsTextEditor.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
+#include "nsIContent.h"
+#include "nsIContentIterator.h"
#include "nsIDOMNode.h"
+#include "nsIDOMText.h"
#include "nsIDOMElement.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMSelection.h"
#include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIEnumerator.h"
+#include "nsIStyleContext.h"
+#include "nsIPresShell.h"
+#include "nsLayoutCID.h"
+
+class nsIFrame;
const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
const static char* kMOZEditorBogusNodeValue="TRUE";
+const unsigned char nbsp = nbsp;
static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID);
+static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
nsIAtom *nsHTMLEditRules::sAAtom;
nsIAtom *nsHTMLEditRules::sAddressAtom;
@@ -61,7 +72,7 @@ nsIAtom *nsHTMLEditRules::sUAtom;
nsIAtom *nsHTMLEditRules::sVarAtom;
nsIAtom *nsHTMLEditRules::sWbrAtom;
-PRInt32 nsHTMLEditRules::sInstanceCount;
+PRInt32 nsHTMLEditRules::sInstanceCount = 0;
/********************************************************
* Constructor/Destructor
@@ -69,8 +80,11 @@ PRInt32 nsHTMLEditRules::sInstanceCount;
nsHTMLEditRules::nsHTMLEditRules()
{
+ // XXX: this sux. We are dependant on layout's private conception of tag atom names.
+
if (sInstanceCount <= 0)
{
+ // inline tags
sAAtom = NS_NewAtom("a");
sAddressAtom = NS_NewAtom("address");
sBigAtom = NS_NewAtom("big");
@@ -349,44 +363,60 @@ nsHTMLEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult)
* helper methods
********************************************************/
+///////////////////////////////////////////////////////////////////////////
+// IsBlockNode: true if this node is an html block node
+//
PRBool
-nsHTMLEditRules::IsBlockNode(nsCOMPtr aContent)
+nsHTMLEditRules::IsBlockNode(nsIDOMNode *aNode)
{
nsIAtom* atom = nsnull;
PRBool result;
- aContent->GetTag(atom);
+ if (!aNode)
+ {
+ NS_NOTREACHED("null node passed to IsBlockNode()");
+ return PR_FALSE;
+ }
+
+ nsCOMPtr content = do_QueryInterface(aNode);
+ if (!content)
+ {
+ NS_NOTREACHED("could not get content node in IsBlockNode()");
+ return PR_FALSE;
+ }
+
+ content->GetTag(atom);
if (!atom)
return PR_TRUE;
if (sAAtom != atom &&
- sAddressAtom != atom &&
- sBigAtom != atom &&
- sBlinkAtom != atom &&
- sBAtom != atom &&
- sCiteAtom != atom &&
- sCodeAtom != atom &&
- sDfnAtom != atom &&
- sEmAtom != atom &&
- sFontAtom != atom &&
- sIAtom != atom &&
- sKbdAtom != atom &&
- sKeygenAtom != atom &&
- sNobrAtom != atom &&
- sSAtom != atom &&
- sSampAtom != atom &&
- sSmallAtom != atom &&
- sSpacerAtom != atom &&
- sSpanAtom != atom &&
- sStrikeAtom != atom &&
- sStrongAtom != atom &&
- sSubAtom != atom &&
- sSupAtom != atom &&
- sTtAtom != atom &&
- sUAtom != atom &&
- sVarAtom != atom &&
- sWbrAtom != atom)
+ sAddressAtom != atom &&
+ sBigAtom != atom &&
+ sBlinkAtom != atom &&
+ sBAtom != atom &&
+ sCiteAtom != atom &&
+ sCodeAtom != atom &&
+ sDfnAtom != atom &&
+ sEmAtom != atom &&
+ sFontAtom != atom &&
+ sIAtom != atom &&
+ sKbdAtom != atom &&
+ sKeygenAtom != atom &&
+ sNobrAtom != atom &&
+ sSAtom != atom &&
+ sSampAtom != atom &&
+ sSmallAtom != atom &&
+ sSpacerAtom != atom &&
+ sSpanAtom != atom &&
+ sStrikeAtom != atom &&
+ sStrongAtom != atom &&
+ sSubAtom != atom &&
+ sSupAtom != atom &&
+ sTtAtom != atom &&
+ sUAtom != atom &&
+ sVarAtom != atom &&
+ sWbrAtom != atom)
{
result = PR_TRUE;
}
@@ -398,19 +428,31 @@ nsHTMLEditRules::IsBlockNode(nsCOMPtr aContent)
return result;
}
-nsCOMPtr
-nsHTMLEditRules::GetBlockNodeParent(nsCOMPtr aContent)
+
+///////////////////////////////////////////////////////////////////////////
+// IsInlineNode: true if this node is an html inline node
+//
+PRBool
+nsHTMLEditRules::IsInlineNode(nsIDOMNode *aNode)
{
- nsCOMPtr p;
+ return !IsBlockNode(aNode);
+}
- if (NS_FAILED(aContent->GetParent(*getter_AddRefs(p)))) // no parent, ran off top of tree
- return aContent;
+///////////////////////////////////////////////////////////////////////////
+// GetBlockNodeParent: returns enclosing block level ancestor, if any
+// else returns the node itself
+nsCOMPtr
+nsHTMLEditRules::GetBlockNodeParent(nsIDOMNode *aNode)
+{
+ nsCOMPtr tmp = do_QueryInterface(aNode);
+ nsCOMPtr p;
- nsCOMPtr tmp;
+ if (NS_FAILED(aNode->GetParentNode(getter_AddRefs(p)))) // no parent, ran off top of tree
+ return tmp;
while (p && !IsBlockNode(p))
{
- if (NS_FAILED(p->GetParent(*getter_AddRefs(tmp)))) // no parent, ran off top of tree
+ if (NS_FAILED(p->GetParentNode(getter_AddRefs(tmp)))) // no parent, ran off top of tree
return p;
p = tmp;
@@ -418,6 +460,121 @@ nsHTMLEditRules::GetBlockNodeParent(nsCOMPtr aContent)
return p;
}
+
+///////////////////////////////////////////////////////////////////////////
+// HasSameBlockNodeParent: true if nodes have same block level ancestor
+//
+PRBool
+nsHTMLEditRules::HasSameBlockNodeParent(nsIDOMNode *aNode1, nsIDOMNode *aNode2)
+{
+ if (!aNode1 || !aNode2)
+ {
+ NS_NOTREACHED("null node passed to HasSameBlockNodeParent()");
+ return PR_FALSE;
+ }
+
+ if (aNode1 == aNode2)
+ return PR_TRUE;
+
+ nsCOMPtr p1 = GetBlockNodeParent(aNode1);
+ nsCOMPtr p2 = GetBlockNodeParent(aNode2);
+
+ return (p1 == p2);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// IsTextOrElementNode: true if node of dom type element or text
+//
+PRBool
+nsHTMLEditRules::IsTextOrElementNode(nsIDOMNode *aNode)
+{
+ if (!aNode)
+ {
+ NS_NOTREACHED("null node passed to IsTextOrElementNode()");
+ return PR_FALSE;
+ }
+
+ PRUint16 nodeType;
+ aNode->GetNodeType(&nodeType);
+ if ((nodeType == nsIDOMNode::ELEMENT_NODE) || (nodeType == nsIDOMNode::TEXT_NODE))
+ return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// IsTextNode: true if node of dom type text
+//
+PRBool
+nsHTMLEditRules::IsTextNode(nsIDOMNode *aNode)
+{
+ if (!aNode)
+ {
+ NS_NOTREACHED("null node passed to IsTextNode()");
+ return PR_FALSE;
+ }
+
+ PRUint16 nodeType;
+ aNode->GetNodeType(&nodeType);
+ if (nodeType == nsIDOMNode::TEXT_NODE)
+ return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// NextNodeInBlock: gets the next/prev node in the block, if any. Next node
+// must be an element or text node, others are ignored
+nsCOMPtr
+nsHTMLEditRules::NextNodeInBlock(nsIDOMNode *aNode, IterDirection aDir)
+{
+ nsCOMPtr nullNode;
+ nsCOMPtr content;
+ nsCOMPtr blockContent;
+ nsCOMPtr node;
+ nsCOMPtr blockParent;
+
+ if (!aNode) return nullNode;
+
+ nsCOMPtr iter;
+ if (NS_FAILED(nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
+ nsIContentIterator::GetIID(),
+ getter_AddRefs(iter))))
+ return nullNode;
+
+ // much gnashing of teeth as we twit back and forth between content and domnode types
+ content = do_QueryInterface(aNode);
+ blockParent = GetBlockNodeParent(aNode);
+ if (!blockParent) return nullNode;
+ blockContent = do_QueryInterface(blockParent);
+ if (!blockContent) return nullNode;
+
+ if (NS_FAILED(iter->Init(blockContent))) return nullNode;
+ if (NS_FAILED(iter->PositionAt(content))) return nullNode;
+
+ while (NS_COMFALSE == iter->IsDone())
+ {
+ if (NS_FAILED(iter->CurrentNode(getter_AddRefs(content)))) return nullNode;
+ // ignore nodes that aren't elements or text, or that are the block parent
+ node = do_QueryInterface(content);
+ if (node && IsTextOrElementNode(node) && (node != blockParent))
+ return node;
+
+ if (aDir == kIterForward)
+ iter->Next();
+ else
+ iter->Prev();
+ }
+
+ return nullNode;
+}
+
+
///////////////////////////////////////////////////////////////////////////
// GetStartNode: returns whatever the start parent is of the first range
// in the selection.
@@ -457,13 +614,33 @@ nsHTMLEditRules::GetStartNodeAndOffset(nsIDOMSelection *aSelection,
// IsPreformatted: checks the style info for the node for the preformatted
// text style.
nsresult
-nsHTMLEditRules::IsPreformatted(nsCOMPtr aNode, PRBool *aResult)
+nsHTMLEditRules::IsPreformatted(nsIDOMNode *aNode, PRBool *aResult)
{
- // XXX not yet impl
- if (!aResult)
- return NS_ERROR_NULL_POINTER;
-
- *aResult = PR_TRUE;
+ nsIPresShell* shell = nsnull;
+ nsresult result;
+ nsCOMPtr content = do_QueryInterface(aNode);
+ nsIFrame *frame;
+ nsCOMPtr styleContext;
+ const nsStyleText* styleText;
+ PRBool bPreformatted;
+
+ if (!aResult || !content) return NS_ERROR_NULL_POINTER;
+
+ result = mEditor->GetPresShell(&shell);
+ if (NS_FAILED(result)) return result;
+
+ result = shell->GetPrimaryFrameFor(content, &frame);
+ if (NS_FAILED(result)) return result;
+
+ result = shell->GetStyleContextFor(frame, getter_AddRefs(styleContext));
+ if (NS_FAILED(result)) return result;
+
+ styleText = (const nsStyleText*)styleContext->GetStyleData(eStyleStruct_Text);
+
+ bPreformatted = (NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
+ (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace);
+
+ *aResult = bPreformatted;
return NS_OK;
}
@@ -472,15 +649,54 @@ nsHTMLEditRules::IsPreformatted(nsCOMPtr aNode, PRBool *aResult)
// IsNextCharWhitespace: checks the adjacent content in the same block
// to see if following selection is whitespace
nsresult
-nsHTMLEditRules::IsNextCharWhitespace(nsCOMPtr aParentNode,
+nsHTMLEditRules::IsNextCharWhitespace(nsIDOMNode *aParentNode,
PRInt32 aOffset,
PRBool *aResult)
{
- // XXX not yet impl
- if (!aResult)
- return NS_ERROR_NULL_POINTER;
-
- *aResult = PR_TRUE;
+ if (!aResult) return NS_ERROR_NULL_POINTER;
+ *aResult = PR_FALSE;
+
+ nsString tempString;
+ PRUint32 strLength;
+ nsCOMPtr textNode = do_QueryInterface(aParentNode);
+ if (textNode)
+ {
+ textNode->GetLength(&strLength);
+ if (aOffset < strLength)
+ {
+ // easy case: next char is in same node
+ textNode->SubstringData(aOffset,aOffset+1,tempString);
+ *aResult = nsString::IsSpace(tempString.First());
+ return NS_OK;
+ }
+ }
+
+ // harder case: next char in next node.
+ nsCOMPtr node = NextNodeInBlock(aParentNode, kIterForward);
+ while (node)
+ {
+ if (!IsInlineNode(node)) // skip over bold, italic, link, ect nodes
+ {
+ if (IsTextNode(node))
+ {
+ textNode = do_QueryInterface(node);
+ textNode->GetLength(&strLength);
+ if (strLength)
+ {
+ textNode->SubstringData(0,1,tempString);
+ *aResult = nsString::IsSpace(tempString.First());
+ return NS_OK;
+ }
+ // else it's an empty text node, skip it.
+ }
+ else // node is an image or some other thingy that doesn't count as whitespace
+ {
+ break;
+ }
+ }
+ node = NextNodeInBlock(aParentNode, kIterForward);
+ }
+
return NS_OK;
}
@@ -489,15 +705,81 @@ nsHTMLEditRules::IsNextCharWhitespace(nsCOMPtr aParentNode,
// IsPrevCharWhitespace: checks the adjacent content in the same block
// to see if following selection is whitespace
nsresult
-nsHTMLEditRules::IsPrevCharWhitespace(nsCOMPtr aParentNode,
+nsHTMLEditRules::IsPrevCharWhitespace(nsIDOMNode *aParentNode,
PRInt32 aOffset,
PRBool *aResult)
{
- // XXX not yet impl
- if (!aResult)
- return NS_ERROR_NULL_POINTER;
-
- *aResult = PR_TRUE;
+ if (!aResult) return NS_ERROR_NULL_POINTER;
+ *aResult = PR_FALSE;
+
+ nsString tempString;
+ PRUint32 strLength;
+ nsCOMPtr textNode = do_QueryInterface(aParentNode);
+ if (textNode)
+ {
+ if (aOffset > 0)
+ {
+ // easy case: prev char is in same node
+ textNode->SubstringData(aOffset-1,aOffset,tempString);
+ *aResult = nsString::IsSpace(tempString.First());
+ return NS_OK;
+ }
+ }
+
+ // harder case: prev char in next node
+ nsCOMPtr node = NextNodeInBlock(aParentNode, kIterBackward);
+ while (node)
+ {
+ if (!IsInlineNode(node)) // skip over bold, italic, link, ect nodes
+ {
+ if (IsTextNode(node))
+ {
+ textNode = do_QueryInterface(node);
+ textNode->GetLength(&strLength);
+ if (strLength)
+ {
+ textNode->SubstringData(strLength-1,strLength,tempString);
+ *aResult = nsString::IsSpace(tempString.First());
+ return NS_OK;
+ }
+ // else it's an empty text node, skip it.
+ }
+ else // node is an image or some other thingy that doesn't count as whitespace
+ {
+ break;
+ }
+ }
+ // otherwise we found a node we want to skip, keep going
+ node = NextNodeInBlock(aParentNode, kIterBackward);
+ }
+
+ return NS_OK;
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// GetTabAsNBSPs: stuff the right number of nbsp's into outString
+//
+nsresult
+nsHTMLEditRules::GetTabAsNBSPs(nsString *outString)
+{
+ if (!outString) return NS_ERROR_NULL_POINTER;
+ // XXX - this should get the right number from prefs
+ *outString += nbsp; *outString += nbsp; *outString += nbsp; *outString += nbsp;
+ return NS_OK;
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// GetTabAsNBSPsAndSpace: stuff the right number of nbsp's followed by a
+// space into outString
+nsresult
+nsHTMLEditRules::GetTabAsNBSPsAndSpace(nsString *outString)
+{
+ if (!outString) return NS_ERROR_NULL_POINTER;
+ // XXX - this should get the right number from prefs
+ *outString += nbsp; *outString += nbsp; *outString += nbsp; *outString += ' ';
return NS_OK;
}
@@ -516,35 +798,57 @@ nsHTMLEditRules::InsertTab(nsIDOMSelection *aSelection,
PlaceholderTxn **aTxn,
nsString *outString)
{
- nsCOMPtr theNode;
- PRBool isPRE;
+ nsCOMPtr parentNode;
PRInt32 offset;
+ PRBool isPRE;
+ PRBool isNextWhiteSpace;
+ PRBool isPrevWhiteSpace;
- nsresult result = GetStartNodeAndOffset(aSelection, &theNode, &offset);
- if (NS_FAILED(result))
- return result;
+ nsresult result = GetStartNodeAndOffset(aSelection, &parentNode, &offset);
+ if (NS_FAILED(result)) return result;
- if (!theNode)
- return NS_ERROR_FAILURE;
+ if (!parentNode) return NS_ERROR_FAILURE;
- result = IsPreformatted(theNode,&isPRE);
- if (NS_FAILED(result))
- return result;
+ result = IsPreformatted(parentNode,&isPRE);
+ if (NS_FAILED(result)) return result;
if (isPRE)
{
- outString += '\t';
- // we're done - let everything fall through to the InsertText code
- // in nsTextEditor which will insert the tab as is.
+ *outString += '\t';
+ return NS_OK;
}
- else
+
+ result = IsNextCharWhitespace(parentNode, offset, &isNextWhiteSpace);
+ if (NS_FAILED(result)) return result;
+
+ result = IsPrevCharWhitespace(parentNode, offset, &isPrevWhiteSpace);
+ if (NS_FAILED(result)) return result;
+
+ if (isPrevWhiteSpace)
{
- // XXX fix me
- *outString += 160;
- *outString += 160;
- *outString += 160;
- *outString += 160;
+ // prev character is a whitespace; Need to
+ // insert nbsp's BEFORE the space
+
+ // XXX for now put tab in wrong place
+ if (isNextWhiteSpace)
+ {
+ GetTabAsNBSPs(outString);
+ return NS_OK;
+ }
+ GetTabAsNBSPsAndSpace(outString);
+ return NS_OK;
}
+
+ if (isNextWhiteSpace)
+ {
+ // character after us is ws. insert nbsps
+ GetTabAsNBSPs(outString);
+ return NS_OK;
+ }
+
+ // else we are in middle of a word; use n-1 nbsp's plus a space
+ GetTabAsNBSPsAndSpace(outString);
+
return NS_OK;
}
@@ -560,41 +864,52 @@ nsHTMLEditRules::InsertSpace(nsIDOMSelection *aSelection,
{
nsCOMPtr parentNode;
PRInt32 offset;
- PRBool isWhiteSpace;
+ PRBool isPRE;
+ PRBool isNextWhiteSpace;
+ PRBool isPrevWhiteSpace;
nsresult result = GetStartNodeAndOffset(aSelection, &parentNode, &offset);
- if (NS_FAILED(result))
- return result;
+ if (NS_FAILED(result)) return result;
- if (!parentNode)
- return NS_ERROR_FAILURE;
+ if (!parentNode) return NS_ERROR_FAILURE;
- result = IsNextCharWhitespace(parentNode, offset, &isWhiteSpace);
- if (NS_FAILED(result))
- return result;
+ result = IsPreformatted(parentNode,&isPRE);
+ if (NS_FAILED(result)) return result;
- if (isWhiteSpace)
+ if (isPRE)
{
- // next character after us is a whitespace, so we need to insert nbsp
- *outString += 160;
+ *outString += " ";
return NS_OK;
}
- result = IsPrevCharWhitespace(parentNode, offset, &isWhiteSpace);
- if (NS_FAILED(result))
- return result;
+ result = IsNextCharWhitespace(parentNode, offset, &isNextWhiteSpace);
+ if (NS_FAILED(result)) return result;
- if (isWhiteSpace)
+ result = IsPrevCharWhitespace(parentNode, offset, &isPrevWhiteSpace);
+ if (NS_FAILED(result)) return result;
+
+ if (isPrevWhiteSpace)
{
- // character after us is non-ws, but char before is ws. Need to
+ // prev character is a whitespace; Need to
// insert nbsp BEFORE the space
- // XXX fornow just nbsp
-
- *outString += 160;
+ // XXX for now put in wrong place
+ if (isNextWhiteSpace)
+ {
+ *outString += nbsp;
+ return NS_OK;
+ }
+ *outString += nbsp;
return NS_OK;
}
+ if (isNextWhiteSpace)
+ {
+ // character after us is ws. insert nbsp
+ *outString += nbsp;
+ return NS_OK;
+ }
+
// else just a space
*outString += " ";
diff --git a/editor/libeditor/html/nsHTMLEditRules.h b/editor/libeditor/html/nsHTMLEditRules.h
index 9d46ce5f734..b180b0d1e7d 100644
--- a/editor/libeditor/html/nsHTMLEditRules.h
+++ b/editor/libeditor/html/nsHTMLEditRules.h
@@ -21,7 +21,6 @@
#include "nsTextEditRules.h"
#include "nsCOMPtr.h"
-#include "nsIContent.h"
class nsHTMLEditRules : public nsTextEditRules
{
@@ -39,8 +38,15 @@ public:
{
kInsertBreak = 3000
};
+
protected:
+ enum IterDirection
+ {
+ kIterForward,
+ kIterBackward
+ };
+
// nsHTMLEditRules implementation methods
nsresult WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
@@ -57,12 +63,21 @@ protected:
nsresult InsertSpace(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
// helper methods
- PRBool IsBlockNode(nsCOMPtr aContent);
- nsCOMPtr GetBlockNodeParent(nsCOMPtr aContent);
- nsresult GetStartNodeAndOffset(nsIDOMSelection *aSelection, nsCOMPtr *outStartNode, PRInt32 *outStartOffset);
- nsresult IsPreformatted(nsCOMPtr aNode, PRBool *aResult);
- nsresult IsNextCharWhitespace(nsCOMPtr aParentNode, PRInt32 aOffset, PRBool *aResult);
- nsresult IsPrevCharWhitespace(nsCOMPtr aParentNode, PRInt32 aOffset, PRBool *aResult);
+ static nsresult GetTabAsNBSPs(nsString *outString);
+ static nsresult GetTabAsNBSPsAndSpace(nsString *outString);
+ static PRBool IsInlineNode(nsIDOMNode *aNode);
+ static PRBool IsBlockNode(nsIDOMNode *aNode);
+ static nsCOMPtr GetBlockNodeParent(nsIDOMNode *aNode);
+ static PRBool HasSameBlockNodeParent(nsIDOMNode *aNode1, nsIDOMNode *aNode2);
+ static PRBool IsTextOrElementNode(nsIDOMNode *aNode);
+ static PRBool IsTextNode(nsIDOMNode *aNode);
+ static nsCOMPtr NextNodeInBlock(nsIDOMNode *aNode, IterDirection aDir);
+ static nsresult GetStartNodeAndOffset(nsIDOMSelection *aSelection, nsCOMPtr *outStartNode, PRInt32 *outStartOffset);
+
+ nsresult IsPreformatted(nsIDOMNode *aNode, PRBool *aResult);
+ nsresult IsNextCharWhitespace(nsIDOMNode *aParentNode, PRInt32 aOffset, PRBool *aResult);
+ nsresult IsPrevCharWhitespace(nsIDOMNode *aParentNode, PRInt32 aOffset, PRBool *aResult);
+
// data
static nsIAtom *sAAtom;