зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
616b13e22c
Коммит
46325f9b35
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче