зеркало из https://github.com/mozilla/gecko-dev.git
fix for 143338: Word wrap of double space incorrect in HTML mailcompose
r=glazman sr=kin
This commit is contained in:
Родитель
fd5c1668c3
Коммит
716db33760
|
@ -289,6 +289,31 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
|
||||||
|
|
||||||
if (!mActionNesting)
|
if (!mActionNesting)
|
||||||
{
|
{
|
||||||
|
// remember where our selection was before edit action took place:
|
||||||
|
|
||||||
|
// get selection
|
||||||
|
nsCOMPtr<nsISelection>selection;
|
||||||
|
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
|
||||||
|
if (NS_FAILED(res)) return res;
|
||||||
|
|
||||||
|
// get the selection start location
|
||||||
|
nsCOMPtr<nsIDOMNode> selNode;
|
||||||
|
PRInt32 selOffset;
|
||||||
|
res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
|
||||||
|
if (NS_FAILED(res)) return res;
|
||||||
|
mRangeItem.startNode = selNode;
|
||||||
|
mRangeItem.startOffset = selOffset;
|
||||||
|
|
||||||
|
// get the selection end location
|
||||||
|
res = mHTMLEditor->GetEndNodeAndOffset(selection, address_of(selNode), &selOffset);
|
||||||
|
if (NS_FAILED(res)) return res;
|
||||||
|
mRangeItem.endNode = selNode;
|
||||||
|
mRangeItem.endOffset = selOffset;
|
||||||
|
|
||||||
|
// register this range with range updater to track this as we perturb the doc
|
||||||
|
(mHTMLEditor->mRangeUpdater).RegisterRangeItem(&mRangeItem);
|
||||||
|
|
||||||
|
// clear out mDocChangeRange and mUtilRange
|
||||||
nsCOMPtr<nsIDOMNSRange> nsrange;
|
nsCOMPtr<nsIDOMNSRange> nsrange;
|
||||||
if(mDocChangeRange)
|
if(mDocChangeRange)
|
||||||
{
|
{
|
||||||
|
@ -304,6 +329,7 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
nsrange->NSDetach(); // ditto for mUtilRange.
|
nsrange->NSDetach(); // ditto for mUtilRange.
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn off caret
|
// turn off caret
|
||||||
nsCOMPtr<nsISelectionController> selCon;
|
nsCOMPtr<nsISelectionController> selCon;
|
||||||
mHTMLEditor->GetSelectionController(getter_AddRefs(selCon));
|
mHTMLEditor->GetSelectionController(getter_AddRefs(selCon));
|
||||||
|
@ -331,6 +357,10 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
|
||||||
{
|
{
|
||||||
// do all the tricky stuff
|
// do all the tricky stuff
|
||||||
res = AfterEditInner(action, aDirection);
|
res = AfterEditInner(action, aDirection);
|
||||||
|
|
||||||
|
// free up selectionState range item
|
||||||
|
(mHTMLEditor->mRangeUpdater).DropRangeItem(&mRangeItem);
|
||||||
|
|
||||||
// turn on caret
|
// turn on caret
|
||||||
nsCOMPtr<nsISelectionController> selCon;
|
nsCOMPtr<nsISelectionController> selCon;
|
||||||
mHTMLEditor->GetSelectionController(getter_AddRefs(selCon));
|
mHTMLEditor->GetSelectionController(getter_AddRefs(selCon));
|
||||||
|
@ -417,11 +447,38 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
|
||||||
res = RemoveEmptyNodes();
|
res = RemoveEmptyNodes();
|
||||||
if (NS_FAILED(res)) return res;
|
if (NS_FAILED(res)) return res;
|
||||||
|
|
||||||
// attempt to transform any uneeded nbsp's into spaces after doing deletions
|
// attempt to transform any uneeded nbsp's into spaces after doing various operations
|
||||||
if (action == nsEditor::kOpDeleteSelection)
|
if ((action == nsEditor::kOpInsertText) ||
|
||||||
|
(action == nsEditor::kOpInsertIMEText) ||
|
||||||
|
(action == nsEditor::kOpDeleteSelection) ||
|
||||||
|
(action == nsEditor::kOpInsertBreak) ||
|
||||||
|
(action == nsHTMLEditor::kOpHTMLPaste ||
|
||||||
|
(action == nsHTMLEditor::kOpLoadHTML)))
|
||||||
{
|
{
|
||||||
res = AdjustWhitespace(selection);
|
res = AdjustWhitespace(selection);
|
||||||
if (NS_FAILED(res)) return res;
|
if (NS_FAILED(res)) return res;
|
||||||
|
|
||||||
|
// also do this for original selection endpoints. The checks for the remembered
|
||||||
|
// selection endpoints are a performance fidgit. Otherwise
|
||||||
|
// nsRangeUpdater::SelAdjDeleteNode() would have to do much more expensive
|
||||||
|
// work (see comment in that routine in nsSelection.cpp)
|
||||||
|
nsCOMPtr<nsIDOMElement> bodyNode;
|
||||||
|
PRInt32 unused;
|
||||||
|
res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyNode));
|
||||||
|
if (NS_FAILED(res)) return res;
|
||||||
|
if (nsHTMLEditUtils::IsDescendantOf(mRangeItem.startNode, bodyNode, &unused))
|
||||||
|
{
|
||||||
|
nsWSRunObject(mHTMLEditor, mRangeItem.startNode, mRangeItem.startOffset).AdjustWhitespace();
|
||||||
|
}
|
||||||
|
// we only need to handle old selection endpoint if it was different from start
|
||||||
|
if ((mRangeItem.startNode != mRangeItem.endNode) || (mRangeItem.startOffset != mRangeItem.endOffset))
|
||||||
|
{
|
||||||
|
if (nsHTMLEditUtils::IsDescendantOf(mRangeItem.endNode, bodyNode, &unused))
|
||||||
|
{
|
||||||
|
nsWSRunObject(mHTMLEditor, mRangeItem.endNode, mRangeItem.endOffset).AdjustWhitespace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we created a new block, make sure selection lands in it
|
// if we created a new block, make sure selection lands in it
|
||||||
|
|
|
@ -264,7 +264,7 @@ protected:
|
||||||
nsCOMPtr<nsIDOMRange> mUtilRange;
|
nsCOMPtr<nsIDOMRange> mUtilRange;
|
||||||
PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin...
|
PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin...
|
||||||
nsCOMPtr<nsIDOMNode> mNewBlock;
|
nsCOMPtr<nsIDOMNode> mNewBlock;
|
||||||
|
nsRangeStore mRangeItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult);
|
nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult);
|
||||||
|
|
|
@ -1067,6 +1067,7 @@ nsWSRunObject::GetRuns()
|
||||||
{
|
{
|
||||||
normalRun->mEndNode = mLastNBSPNode;
|
normalRun->mEndNode = mLastNBSPNode;
|
||||||
normalRun->mEndOffset = mLastNBSPOffset+1;
|
normalRun->mEndOffset = mLastNBSPOffset+1;
|
||||||
|
normalRun->mRightType = eTrailingWS;
|
||||||
|
|
||||||
// set up next run
|
// set up next run
|
||||||
WSFragment *lastRun = new WSFragment();
|
WSFragment *lastRun = new WSFragment();
|
||||||
|
@ -1097,6 +1098,13 @@ nsWSRunObject::GetRuns()
|
||||||
// will point to it, even though in general start/end points not
|
// will point to it, even though in general start/end points not
|
||||||
// guaranteed to be in text nodes.
|
// guaranteed to be in text nodes.
|
||||||
if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
|
if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
|
||||||
|
{
|
||||||
|
mStartRun->mRightType = mEndReason;
|
||||||
|
mStartRun->mEndNode = mEndNode;
|
||||||
|
mStartRun->mEndOffset = mEndOffset;
|
||||||
|
mEndRun = mStartRun;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// set up next run
|
// set up next run
|
||||||
WSFragment *lastRun = new WSFragment();
|
WSFragment *lastRun = new WSFragment();
|
||||||
|
@ -1109,13 +1117,7 @@ nsWSRunObject::GetRuns()
|
||||||
lastRun->mRightType = mEndReason;
|
lastRun->mRightType = mEndReason;
|
||||||
mEndRun = lastRun;
|
mEndRun = lastRun;
|
||||||
mStartRun->mRight = lastRun;
|
mStartRun->mRight = lastRun;
|
||||||
}
|
mStartRun->mRightType = eTrailingWS;
|
||||||
else
|
|
||||||
{
|
|
||||||
mStartRun->mRightType = mEndReason;
|
|
||||||
mStartRun->mEndNode = mEndNode;
|
|
||||||
mStartRun->mEndOffset = mEndOffset;
|
|
||||||
mEndRun = mStartRun;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2034,6 +2036,7 @@ nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
|
||||||
if (!aRun) return NS_ERROR_NULL_POINTER;
|
if (!aRun) return NS_ERROR_NULL_POINTER;
|
||||||
WSPoint thePoint;
|
WSPoint thePoint;
|
||||||
PRBool leftCheck = PR_FALSE;
|
PRBool leftCheck = PR_FALSE;
|
||||||
|
PRBool spaceNBSP = PR_FALSE;
|
||||||
PRBool rightCheck = PR_FALSE;
|
PRBool rightCheck = PR_FALSE;
|
||||||
|
|
||||||
// confirm run is normalWS
|
// confirm run is normalWS
|
||||||
|
@ -2049,15 +2052,47 @@ nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
|
||||||
if (NS_SUCCEEDED(res) && prevPoint.mTextNode)
|
if (NS_SUCCEEDED(res) && prevPoint.mTextNode)
|
||||||
{
|
{
|
||||||
if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = PR_TRUE;
|
if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = PR_TRUE;
|
||||||
|
else spaceNBSP = PR_TRUE;
|
||||||
}
|
}
|
||||||
else if (aRun->mLeftType == eText) leftCheck = PR_TRUE;
|
else if (aRun->mLeftType == eText) leftCheck = PR_TRUE;
|
||||||
else if (aRun->mLeftType == eSpecial) leftCheck = PR_TRUE;
|
else if (aRun->mLeftType == eSpecial) leftCheck = PR_TRUE;
|
||||||
if (leftCheck)
|
if (leftCheck || spaceNBSP)
|
||||||
{
|
{
|
||||||
// now check that what is to the right of it is compatible with replacing nbsp with space
|
// now check that what is to the right of it is compatible with replacing nbsp with space
|
||||||
if (aRun->mRightType == eText) rightCheck = PR_TRUE;
|
if (aRun->mRightType == eText) rightCheck = PR_TRUE;
|
||||||
if (aRun->mRightType == eSpecial) rightCheck = PR_TRUE;
|
if (aRun->mRightType == eSpecial) rightCheck = PR_TRUE;
|
||||||
if (aRun->mRightType == eBreak) rightCheck = PR_TRUE;
|
if (aRun->mRightType == eBreak) rightCheck = PR_TRUE;
|
||||||
|
if (aRun->mRightType & eBlock)
|
||||||
|
{
|
||||||
|
// we are at a block boundary. Insert a <br>. Why? Well, first note that
|
||||||
|
// the br will have no visible effect since it is up against a block boundary.
|
||||||
|
// |foo<br><p>bar| renders like |foo<p>bar| and similarly
|
||||||
|
// |<p>foo<br></p>bar| renders like |<p>foo</p>bar|. What this <br> addition
|
||||||
|
// gets us is the ability to convert a trailing nbsp to a space. Consider:
|
||||||
|
// |<body>foo. '</body>|, where ' represents selection. User types space attempting
|
||||||
|
// to put 2 spaces after the end of their sentence. We used to do this as:
|
||||||
|
// |<body>foo.  </body>| This caused problems with soft wrapping: the nbsp
|
||||||
|
// would wrap to the next line, which looked attrocious. If you try to do:
|
||||||
|
// |<body>foo.  </body>| instead, the trailing space is invisible because it
|
||||||
|
// is against a block boundary. If you do: |<body>foo.  </body>| then
|
||||||
|
// you get an even uglier soft wrapping problem, where foo is on one line until
|
||||||
|
// you type the final space, and then "foo " jumps down to the next line. Ugh.
|
||||||
|
// The best way I can find out of this is to throw in a harmless <br>
|
||||||
|
// here, which allows us to do: |<body>foo.  <br></body>|, which doesn't
|
||||||
|
// cause foo to jump lines, doesn't cause spaces to show up at the beginning of
|
||||||
|
// soft wrapped lines, and lets the user see 2 spaces when they type 2 spaces.
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDOMNode> brNode;
|
||||||
|
res = mHTMLEditor->CreateBR(aRun->mEndNode, aRun->mEndOffset, address_of(brNode));
|
||||||
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
|
||||||
|
// refresh thePoint, prevPoint
|
||||||
|
res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint);
|
||||||
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
res = GetCharBefore(thePoint, &prevPoint);
|
||||||
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
rightCheck = PR_TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (leftCheck && rightCheck)
|
if (leftCheck && rightCheck)
|
||||||
{
|
{
|
||||||
|
@ -2076,6 +2111,32 @@ nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
|
||||||
res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
|
res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
}
|
}
|
||||||
|
else if (!mPRE && spaceNBSP && rightCheck) // don't mess with this preformatted for now.
|
||||||
|
{
|
||||||
|
// we have a run of ascii whitespace (which will render as one space)
|
||||||
|
// followed by an nbsp (which is at the end of the whitespace run). Let's
|
||||||
|
// switch their order. This will insure that if someone types two spaces
|
||||||
|
// after a sentence, and the editor softwraps at this point, the spaces wont
|
||||||
|
// be split across lines, which looks ugly and is bad for the moose.
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDOMNode> startNode, endNode, thenode(do_QueryInterface(prevPoint.mTextNode));
|
||||||
|
PRInt32 startOffset, endOffset;
|
||||||
|
res = GetAsciiWSBounds(eBoth, thenode, prevPoint.mOffset+1, address_of(startNode),
|
||||||
|
&startOffset, address_of(endNode), &endOffset);
|
||||||
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
|
||||||
|
// delete that nbsp
|
||||||
|
nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
|
||||||
|
res = DeleteChars(delNode, thePoint.mOffset, delNode, thePoint.mOffset+1);
|
||||||
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
|
||||||
|
// finally, insert that nbsp before the ascii ws run
|
||||||
|
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
|
||||||
|
nsAutoString nbspStr(nbsp);
|
||||||
|
nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(startNode));
|
||||||
|
res = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, textNode, startOffset, PR_TRUE);
|
||||||
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче