Bug 391584. Don't miss word-break opportunities at points where whitespace has collapsed away. Also, note that a word-break opportunity exists at the start of a frame when it starts with a space.

This commit is contained in:
roc+@cs.cmu.edu 2007-10-10 15:32:51 -07:00
Родитель 616b13e22c
Коммит 46325f9b35
3 изменённых файлов: 48 добавлений и 20 удалений

Просмотреть файл

@ -4408,7 +4408,7 @@ public:
PRBool NextCluster(); PRBool NextCluster();
PRBool IsWhitespace(); PRBool IsWhitespace();
PRBool IsPunctuation(); PRBool IsPunctuation();
PRBool HaveWordBreakBefore(); PRBool HaveWordBreakBefore() { return mHaveWordBreak; }
PRInt32 GetAfterOffset(); PRInt32 GetAfterOffset();
PRInt32 GetBeforeOffset(); PRInt32 GetBeforeOffset();
@ -4421,6 +4421,7 @@ private:
PRInt32 mCharIndex; PRInt32 mCharIndex;
nsTextFrame::TrimmedOffsets mTrimmed; nsTextFrame::TrimmedOffsets mTrimmed;
nsTArray<PRPackedBool> mWordBreaks; nsTArray<PRPackedBool> mWordBreaks;
PRPackedBool mHaveWordBreak;
}; };
PRBool PRBool
@ -4493,12 +4494,6 @@ ClusterIterator::IsPunctuation()
return c == nsIUGenCategory::kPunctuation || c == nsIUGenCategory::kSymbol; return c == nsIUGenCategory::kPunctuation || c == nsIUGenCategory::kSymbol;
} }
PRBool
ClusterIterator::HaveWordBreakBefore()
{
return mWordBreaks[GetBeforeOffset() - mTextFrame->GetContentOffset()];
}
PRInt32 PRInt32
ClusterIterator::GetBeforeOffset() ClusterIterator::GetBeforeOffset()
{ {
@ -4520,29 +4515,31 @@ ClusterIterator::NextCluster()
return PR_FALSE; return PR_FALSE;
gfxTextRun* textRun = mTextFrame->GetTextRun(); gfxTextRun* textRun = mTextFrame->GetTextRun();
mHaveWordBreak = PR_FALSE;
while (PR_TRUE) { while (PR_TRUE) {
PRBool keepGoing = PR_FALSE;
if (mDirection > 0) { if (mDirection > 0) {
if (mIterator.GetOriginalOffset() >= mTrimmed.GetEnd()) if (mIterator.GetOriginalOffset() >= mTrimmed.GetEnd())
return PR_FALSE; return PR_FALSE;
if (mIterator.IsOriginalCharSkipped() || keepGoing = mIterator.IsOriginalCharSkipped() ||
mIterator.GetOriginalOffset() < mTrimmed.mStart || mIterator.GetOriginalOffset() < mTrimmed.mStart ||
!textRun->IsClusterStart(mIterator.GetSkippedOffset())) { !textRun->IsClusterStart(mIterator.GetSkippedOffset());
mIterator.AdvanceOriginal(1);
continue;
}
mCharIndex = mIterator.GetOriginalOffset(); mCharIndex = mIterator.GetOriginalOffset();
mIterator.AdvanceOriginal(1); mIterator.AdvanceOriginal(1);
} else { } else {
if (mIterator.GetOriginalOffset() <= mTrimmed.mStart) if (mIterator.GetOriginalOffset() <= mTrimmed.mStart)
return PR_FALSE; return PR_FALSE;
mIterator.AdvanceOriginal(-1); mIterator.AdvanceOriginal(-1);
if (mIterator.IsOriginalCharSkipped() || keepGoing = mIterator.IsOriginalCharSkipped() ||
mIterator.GetOriginalOffset() >= mTrimmed.GetEnd() || mIterator.GetOriginalOffset() >= mTrimmed.GetEnd() ||
!textRun->IsClusterStart(mIterator.GetSkippedOffset())) !textRun->IsClusterStart(mIterator.GetSkippedOffset());
continue;
mCharIndex = mIterator.GetOriginalOffset(); mCharIndex = mIterator.GetOriginalOffset();
} }
if (mWordBreaks[GetBeforeOffset() - mTextFrame->GetContentOffset()]) {
mHaveWordBreak = PR_TRUE;
}
if (!keepGoing)
return PR_TRUE; return PR_TRUE;
} }
} }
@ -4563,6 +4560,7 @@ ClusterIterator::ClusterIterator(nsTextFrame* aTextFrame, PRInt32 aPosition,
mFrag = aTextFrame->GetContent()->GetText(); mFrag = aTextFrame->GetContent()->GetText();
mTrimmed = aTextFrame->GetTrimmedOffsets(mFrag, PR_TRUE); mTrimmed = aTextFrame->GetTrimmedOffsets(mFrag, PR_TRUE);
PRInt32 textOffset = aTextFrame->GetContentOffset();
PRInt32 textLen = aTextFrame->GetContentLength(); PRInt32 textLen = aTextFrame->GetContentLength();
if (!mWordBreaks.AppendElements(textLen + 1)) { if (!mWordBreaks.AppendElements(textLen + 1)) {
mDirection = 0; // signal failure mDirection = 0; // signal failure
@ -4570,7 +4568,7 @@ ClusterIterator::ClusterIterator(nsTextFrame* aTextFrame, PRInt32 aPosition,
} }
memset(mWordBreaks.Elements(), PR_FALSE, textLen + 1); memset(mWordBreaks.Elements(), PR_FALSE, textLen + 1);
nsAutoString text; nsAutoString text;
mFrag->AppendTo(text, aTextFrame->GetContentOffset(), textLen); mFrag->AppendTo(text, textOffset, textLen);
nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker(); nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
PRInt32 i = 0; PRInt32 i = 0;
while ((i = wordBreaker->NextWord(text.get(), textLen, i)) >= 0) { while ((i = wordBreaker->NextWord(text.get(), textLen, i)) >= 0) {
@ -4578,7 +4576,13 @@ ClusterIterator::ClusterIterator(nsTextFrame* aTextFrame, PRInt32 aPosition,
} }
// XXX this never allows word breaks at the start or end of the frame, but to fix // XXX this never allows word breaks at the start or end of the frame, but to fix
// this we would need to rewrite word-break detection to use the text from // this we would need to rewrite word-break detection to use the text from
// textruns or something. Not a regression, at least. // textruns or something. Not a regression, at least. For now we can make things
// a little better by noting a word break opportunity when the frame starts
// or ends with white-space.
if (textLen > 0) {
mWordBreaks[0] = IsSelectionSpace(mFrag, textOffset);
mWordBreaks[textLen] = IsSelectionSpace(mFrag, textOffset + textLen - 1);
}
} }
PRBool PRBool
@ -5404,9 +5408,9 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
const nsTextFragment* frag = mContent->GetText(); const nsTextFragment* frag = mContent->GetText();
TrimmedOffsets trimmed = GetTrimmedOffsets(frag, PR_TRUE); TrimmedOffsets trimmed = GetTrimmedOffsets(frag, PR_TRUE);
gfxSkipCharsIterator iter = start; gfxSkipCharsIterator iter = start;
PRUint32 trimmedEnd = iter.ConvertOriginalToSkipped(trimmed.GetEnd());
const nsStyleText* textStyle = GetStyleText(); const nsStyleText* textStyle = GetStyleText();
gfxFloat delta = 0; gfxFloat delta = 0;
PRUint32 trimmedEnd = iter.ConvertOriginalToSkipped(trimmed.GetEnd());
if (GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) { if (GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) {
aLastCharIsJustifiable = PR_TRUE; aLastCharIsJustifiable = PR_TRUE;

Просмотреть файл

@ -55,6 +55,7 @@ _TEST_FILES = test_bug323656.html \
test_bug392923.html \ test_bug392923.html \
test_bug394173.html \ test_bug394173.html \
test_bug394239.html \ test_bug394239.html \
test_word_movement.html \
$(NULL) $(NULL)
libs:: $(_TEST_FILES) libs:: $(_TEST_FILES)

Просмотреть файл

@ -22,7 +22,26 @@ SimpleTest.waitForExplicitFinish();
// This seems to be necessary because the selection is not set up properly otherwise // This seems to be necessary because the selection is not set up properly otherwise
setTimeout(test, 0); setTimeout(test, 0);
function getPrefs() {
const prefSvcContractID = "@mozilla.org/preferences-service;1";
const prefSvcIID = Components.interfaces.nsIPrefService;
return Components.classes[prefSvcContractID].getService(prefSvcIID)
.getBranch("layout.word_select.");
}
function setEatSpace(newValue) {
getPrefs().setBoolPref("eat_space_to_next_word", newValue);
}
function restoreEatSpace() {
try {
getPrefs().clearUserPref("eat_space_to_next_word");
} catch(ex) {}
}
function test() { function test() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var wordModifiers = var wordModifiers =
(navigator.platform.indexOf("Mac") >= 0) ? {altKey:true} : {ctrlKey:true}; (navigator.platform.indexOf("Mac") >= 0) ? {altKey:true} : {ctrlKey:true};
var sel = window.getSelection(); var sel = window.getSelection();
@ -40,6 +59,8 @@ function test() {
is(sel.anchorOffset, offset, "Left movement broken in " + editor.innerHTML); is(sel.anchorOffset, offset, "Left movement broken in " + editor.innerHTML);
} }
setEatSpace(false);
editor.innerHTML = "Hello Kitty"; editor.innerHTML = "Hello Kitty";
sel.collapse(editor.firstChild, 0); sel.collapse(editor.firstChild, 0);
testRight(editor.firstChild, 5); testRight(editor.firstChild, 5);
@ -72,6 +93,8 @@ function test() {
testLeft(editor.firstChild.firstChild, 4); testLeft(editor.firstChild.firstChild, 4);
testLeft(editor.firstChild.firstChild, 0); testLeft(editor.firstChild.firstChild, 0);
restoreEatSpace();
SimpleTest.finish(); SimpleTest.finish();
} }