зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 5 changesets (bug 1623764) for WPT failures in /editing/run/forwarddelete.html. CLOSED TREE
Backed out changeset 838b1a256f25 (bug 1623764) Backed out changeset 00911e3c92c3 (bug 1623764) Backed out changeset 5f7d278953d7 (bug 1623764) Backed out changeset 0059379a3c29 (bug 1623764) Backed out changeset 6e3a9276ec41 (bug 1623764)
This commit is contained in:
Родитель
25b814837c
Коммит
c243829674
|
@ -42,13 +42,10 @@ async function* runTests() {
|
|||
yield;
|
||||
|
||||
function testScrollCommand(cmd, expectTop) {
|
||||
const { top } = root.getBoundingClientRect();
|
||||
|
||||
// XXX(krosylight): Android scrolls slightly more inside
|
||||
// geckoview-test-verify-e10s CI job
|
||||
const isAndroid = SpecialPowers.Services.appinfo.widgetToolkit == "android";
|
||||
const delta = isAndroid ? .134 : 0;
|
||||
isfuzzy(top, -expectTop, delta, cmd);
|
||||
// http://dev.w3.org/csswg/cssom-view/#dom-element-getboundingclientrect
|
||||
// doesn't explicitly rule out -0 here, but for now assume that only
|
||||
// positive zeroes are permitted.
|
||||
is(root.getBoundingClientRect().top, -expectTop + 0, cmd);
|
||||
}
|
||||
|
||||
function testMoveCommand(cmd, expectNode, expectOffset) {
|
||||
|
@ -135,7 +132,7 @@ async function* runTests() {
|
|||
yield;
|
||||
testScrollCommand("cmd_scrollLineUp", root.scrollHeight - 100 - lineHeight);
|
||||
|
||||
var runSelectionTests = function() {
|
||||
var runSelectionTests = function(selectWordNextNode, selectWordNextOffset) {
|
||||
testMoveCommand("cmd_moveBottom", body, 23);
|
||||
testMoveCommand("cmd_moveTop", node(0), 0);
|
||||
testSelectCommand("cmd_selectBottom", body, 23);
|
||||
|
@ -168,7 +165,7 @@ async function* runTests() {
|
|||
testMoveCommand("cmd_wordNext", body, 23);
|
||||
testSelectCommand("cmd_selectWordPrevious", node(22), 0);
|
||||
SpecialPowers.doCommand(window, "cmd_moveTop");
|
||||
testSelectCommand("cmd_selectWordNext", body, 1);
|
||||
testSelectCommand("cmd_selectWordNext", selectWordNextNode, selectWordNextOffset);
|
||||
|
||||
SpecialPowers.doCommand(window, "cmd_moveTop");
|
||||
var lineNum = testPageMoveCommand("cmd_movePageDown", 0);
|
||||
|
@ -185,9 +182,9 @@ async function* runTests() {
|
|||
};
|
||||
|
||||
await SpecialPowers.pushPrefEnv({set: [["layout.word_select.eat_space_to_next_word", false]]});
|
||||
runSelectionTests();
|
||||
runSelectionTests(body, 1);
|
||||
await SpecialPowers.pushPrefEnv({set: [["layout.word_select.eat_space_to_next_word", true]]});
|
||||
runSelectionTests();
|
||||
runSelectionTests(node(2), 0);
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
|
|
|
@ -8342,9 +8342,6 @@ nsresult nsIFrame::PeekOffsetForCharacter(nsPeekOffsetStruct* aPos,
|
|||
nsresult nsIFrame::PeekOffsetForWord(nsPeekOffsetStruct* aPos,
|
||||
int32_t aOffset) {
|
||||
SelectablePeekReport current{this, aOffset};
|
||||
bool shouldStopAtHardBreak =
|
||||
aPos->mWordMovementType == eDefaultBehavior &&
|
||||
StaticPrefs::layout_word_select_eat_space_to_next_word();
|
||||
bool wordSelectEatSpace = ShouldWordSelectionEatSpace(*aPos);
|
||||
|
||||
PeekWordState state;
|
||||
|
@ -8379,35 +8376,11 @@ nsresult nsIFrame::PeekOffsetForWord(nsPeekOffsetStruct* aPos,
|
|||
break;
|
||||
}
|
||||
|
||||
if (shouldStopAtHardBreak && next.mJumpedHardBreak) {
|
||||
/**
|
||||
* Prev, always: Jump and stop right there
|
||||
* Next, saw inline: just stop
|
||||
* Next, no inline: Jump and consume whitespaces
|
||||
*/
|
||||
if (aPos->mDirection == eDirPrevious) {
|
||||
// Try moving to the previous line if exists
|
||||
current.TransferTo(*aPos);
|
||||
current.mFrame->PeekOffsetForCharacter(aPos, current.mOffset);
|
||||
return NS_OK;
|
||||
}
|
||||
if (state.mSawInlineCharacter || current.mJumpedHardBreak) {
|
||||
if (current.mFrame->HasSignificantTerminalNewline()) {
|
||||
current.mOffset -= 1;
|
||||
}
|
||||
current.TransferTo(*aPos);
|
||||
return NS_OK;
|
||||
}
|
||||
// Mark the state as whitespace and continue
|
||||
state.Update(false, true);
|
||||
}
|
||||
|
||||
if (next.mJumpedLine) {
|
||||
state.mContext.Truncate();
|
||||
}
|
||||
current = next;
|
||||
// Jumping a line is equivalent to encountering whitespace
|
||||
// This affects only when it already met an actual character
|
||||
if (wordSelectEatSpace && next.mJumpedLine) {
|
||||
state.SetSawBeforeType();
|
||||
}
|
||||
|
@ -8769,11 +8742,11 @@ Result<bool, nsresult> nsIFrame::IsVisuallyAtLineEdge(
|
|||
nsIFrame* firstFrame;
|
||||
nsIFrame* lastFrame;
|
||||
|
||||
bool lineIsRTL = aLineIterator->GetDirection();
|
||||
nsAutoLineIterator it = aLineIterator;
|
||||
bool lineIsRTL = it->GetDirection();
|
||||
bool isReordered;
|
||||
|
||||
MOZ_TRY(aLineIterator->CheckLineOrder(aLine, &isReordered, &firstFrame,
|
||||
&lastFrame));
|
||||
MOZ_TRY(it->CheckLineOrder(aLine, &isReordered, &firstFrame, &lastFrame));
|
||||
|
||||
nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
|
||||
if (!*framePtr) {
|
||||
|
@ -8791,7 +8764,8 @@ Result<bool, nsresult> nsIFrame::IsVisuallyAtLineEdge(
|
|||
|
||||
Result<bool, nsresult> nsIFrame::IsLogicallyAtLineEdge(
|
||||
nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
|
||||
auto line = aLineIterator->GetLine(aLine).unwrap();
|
||||
nsAutoLineIterator it = aLineIterator;
|
||||
auto line = it->GetLine(aLine).unwrap();
|
||||
|
||||
if (aDirection == eDirPrevious) {
|
||||
nsIFrame* firstFrame = line.mFirstFrameOnLine;
|
||||
|
@ -8803,7 +8777,7 @@ Result<bool, nsresult> nsIFrame::IsLogicallyAtLineEdge(
|
|||
nsIFrame* lastFrame = line.mFirstFrameOnLine;
|
||||
for (int32_t lineFrameCount = line.mNumFramesOnLine; lineFrameCount > 1;
|
||||
lineFrameCount--) {
|
||||
MOZ_TRY(aLineIterator->GetNextSiblingOnLine(lastFrame, aLine));
|
||||
MOZ_TRY(it->GetNextSiblingOnLine(lastFrame, aLine));
|
||||
if (!lastFrame) {
|
||||
NS_ERROR("should not be reached nsIFrame");
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
|
@ -8838,7 +8812,7 @@ nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
|
|||
MOZ_TRY_VAR(thisLine,
|
||||
traversedFrame->GetLineNumber(aScrollViewStop, &blockFrame));
|
||||
|
||||
nsAutoLineIterator it = blockFrame->GetLineIterator();
|
||||
nsILineIterator* it = blockFrame->GetLineIterator();
|
||||
|
||||
bool atLineEdge;
|
||||
MOZ_TRY_VAR(
|
||||
|
@ -8851,12 +8825,6 @@ nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
|
|||
if (!aJumpLines) {
|
||||
return result; // we are done. cannot jump lines
|
||||
}
|
||||
int32_t lineToCheckWrap =
|
||||
aDirection == eDirPrevious ? thisLine - 1 : thisLine;
|
||||
if (lineToCheckWrap < 0 ||
|
||||
!it->GetLine(lineToCheckWrap).unwrap().mIsWrapped) {
|
||||
result.mJumpedHardBreak = true;
|
||||
}
|
||||
}
|
||||
|
||||
traversedFrame = frameTraversal->Traverse(aDirection == eDirNext);
|
||||
|
@ -8879,9 +8847,7 @@ nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
|
|||
for (nsIFrame* current = traversedFrame->GetPrevSibling(); current;
|
||||
current = current->GetPrevSibling()) {
|
||||
if (!current->IsBlockOutside() && IsSelectable(current)) {
|
||||
if (!current->IsBrFrame()) {
|
||||
canSkipBr = true;
|
||||
}
|
||||
canSkipBr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3776,10 +3776,8 @@ class nsIFrame : public nsQueryFrame {
|
|||
* indicates that we arrived at its end.
|
||||
*/
|
||||
int32_t mOffset = 0;
|
||||
/** whether the input frame and the returned frame are on different lines */
|
||||
/** whether this frame and the returned frame are on different lines */
|
||||
bool mJumpedLine = false;
|
||||
/** whether we met a hard break between the input and the returned frame */
|
||||
bool mJumpedHardBreak = false;
|
||||
/** whether we jumped over a non-selectable frame during the search */
|
||||
bool mMovedOverNonSelectableText = false;
|
||||
|
||||
|
@ -5172,8 +5170,6 @@ class nsIFrame : public nsQueryFrame {
|
|||
// non-whitespace and whitespace), then mSawBeforeType==true means "we
|
||||
// already saw some non-whitespace".
|
||||
bool mSawBeforeType;
|
||||
// true when we've encountered at least one non-newline character
|
||||
bool mSawInlineCharacter;
|
||||
// true when the last character encountered was punctuation
|
||||
bool mLastCharWasPunctuation;
|
||||
// true when the last character encountered was whitespace
|
||||
|
@ -5188,12 +5184,10 @@ class nsIFrame : public nsQueryFrame {
|
|||
PeekWordState()
|
||||
: mAtStart(true),
|
||||
mSawBeforeType(false),
|
||||
mSawInlineCharacter(false),
|
||||
mLastCharWasPunctuation(false),
|
||||
mLastCharWasWhitespace(false),
|
||||
mSeenNonPunctuationSinceWhitespace(false) {}
|
||||
void SetSawBeforeType() { mSawBeforeType = true; }
|
||||
void SetSawInlineCharacter() { mSawInlineCharacter = true; }
|
||||
void Update(bool aAfterPunctuation, bool aAfterWhitespace) {
|
||||
mLastCharWasPunctuation = aAfterPunctuation;
|
||||
mLastCharWasWhitespace = aAfterWhitespace;
|
||||
|
|
|
@ -56,8 +56,6 @@ class nsILineIterator {
|
|||
* positioning then its coordinates may be outside the line bounds)
|
||||
*/
|
||||
nsRect mLineBounds;
|
||||
/** Whether the line is wrapped at the end */
|
||||
bool mIsWrapped;
|
||||
};
|
||||
|
||||
// Return miscellaneous information about a line.
|
||||
|
|
|
@ -617,7 +617,6 @@ Result<nsILineIterator::LineInfo, nsresult> nsLineIterator::GetLine(
|
|||
structure.mFirstFrameOnLine = line->mFirstChild;
|
||||
structure.mNumFramesOnLine = line->GetChildCount();
|
||||
structure.mLineBounds = line->GetPhysicalBounds();
|
||||
structure.mIsWrapped = line->IsLineWrapped();
|
||||
return structure;
|
||||
}
|
||||
|
||||
|
|
|
@ -820,20 +820,12 @@ static bool IsTrimmableSpace(const nsTextFragment* aFrag, uint32_t aPos,
|
|||
}
|
||||
}
|
||||
|
||||
static bool IsSelectionInlineWhitespace(const nsTextFragment* aFrag,
|
||||
uint32_t aPos) {
|
||||
NS_ASSERTION(aPos < aFrag->GetLength(),
|
||||
"No text for IsSelectionInlineWhitespace!");
|
||||
static bool IsSelectionSpace(const nsTextFragment* aFrag, uint32_t aPos) {
|
||||
NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
|
||||
char16_t ch = aFrag->CharAt(aPos);
|
||||
if (ch == ' ' || ch == CH_NBSP)
|
||||
return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
|
||||
return ch == '\t' || ch == '\f';
|
||||
}
|
||||
|
||||
static bool IsSelectionNewline(const nsTextFragment* aFrag, uint32_t aPos) {
|
||||
NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSelectionNewline!");
|
||||
char16_t ch = aFrag->CharAt(aPos);
|
||||
return ch == '\n' || ch == '\r';
|
||||
return ch == '\t' || ch == '\n' || ch == '\f' || ch == '\r';
|
||||
}
|
||||
|
||||
// Count the amount of trimmable whitespace (as per CSS
|
||||
|
@ -7713,8 +7705,7 @@ class MOZ_STACK_CLASS ClusterIterator {
|
|||
bool aTrimSpaces = true);
|
||||
|
||||
bool NextCluster();
|
||||
bool IsInlineWhitespace() const;
|
||||
bool IsNewline() const;
|
||||
bool IsWhitespace() const;
|
||||
bool IsPunctuation() const;
|
||||
bool HaveWordBreakBefore() const { return mHaveWordBreak; }
|
||||
|
||||
|
@ -7859,14 +7850,9 @@ nsIFrame::FrameSearchResult nsTextFrame::PeekOffsetCharacter(
|
|||
return CONTINUE;
|
||||
}
|
||||
|
||||
bool ClusterIterator::IsInlineWhitespace() const {
|
||||
bool ClusterIterator::IsWhitespace() const {
|
||||
NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
|
||||
return IsSelectionInlineWhitespace(mFrag, mCharIndex);
|
||||
}
|
||||
|
||||
bool ClusterIterator::IsNewline() const {
|
||||
NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
|
||||
return IsSelectionNewline(mFrag, mCharIndex);
|
||||
return IsSelectionSpace(mFrag, mCharIndex);
|
||||
}
|
||||
|
||||
bool ClusterIterator::IsPunctuation() const {
|
||||
|
@ -8049,12 +8035,8 @@ nsIFrame::FrameSearchResult nsTextFrame::PeekOffsetWord(
|
|||
|
||||
do {
|
||||
bool isPunctuation = cIter.IsPunctuation();
|
||||
bool isInlineWhitespace = cIter.IsInlineWhitespace();
|
||||
bool isWhitespace = isInlineWhitespace || cIter.IsNewline();
|
||||
bool isWhitespace = cIter.IsWhitespace();
|
||||
bool isWordBreakBefore = cIter.HaveWordBreakBefore();
|
||||
if (!isWhitespace || isInlineWhitespace) {
|
||||
aState->SetSawInlineCharacter();
|
||||
}
|
||||
if (aWordSelectEatSpace == isWhitespace && !aState->mSawBeforeType) {
|
||||
aState->SetSawBeforeType();
|
||||
aState->Update(isPunctuation, isWhitespace);
|
||||
|
|
|
@ -103,7 +103,6 @@ support-files = file_bug1307853.html
|
|||
[test_bug1499961.html]
|
||||
[test_bug1566783.html]
|
||||
support-files = file_bug1566783.html
|
||||
[test_bug1623764.html]
|
||||
[test_bug1642588.html]
|
||||
[test_bug1644511.html]
|
||||
[test_contained_plugin_transplant.html]
|
||||
|
|
|
@ -1,292 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Test Windows conventional caret movement behavior</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
[contenteditable] {
|
||||
font-size: 14px;
|
||||
font-family: monospace;
|
||||
overflow-wrap: normal;
|
||||
outline: none;
|
||||
width: 35ch;
|
||||
}
|
||||
textarea {
|
||||
font-size: 14px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.break-word {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
<div id="testDiv" contenteditable></div>
|
||||
<textarea id="testTextarea" cols=35></textarea>
|
||||
|
||||
<script>
|
||||
// Call testEach(cases[i]) on console when debugging
|
||||
/**
|
||||
* @type {TestCase[]}
|
||||
*
|
||||
* @typedef {object} TestCase
|
||||
* @property {string} title
|
||||
* @property {string} text Text on which the test runs.
|
||||
* The newlines and spaces will be auto-converted if needed.
|
||||
* @property {boolean} [reverse] Test in both forward and backward directions
|
||||
* @property {boolean} [backward] Move backward by cmd_wordPrevious
|
||||
* @property {ElementSpecific} [div] Can be omitted when there is a bug
|
||||
* @property {ElementSpecific} textarea
|
||||
*
|
||||
* @typedef {object} ElementSpecific
|
||||
* @property {number} expectedOffset Expected offset after a caret move
|
||||
* @property {number} [expectedNodeIndex] The index of child node with focus after a caret move.
|
||||
* -1 means the parent node.
|
||||
* @property {number} [initialOffset=0] initialOffset offset before a caret move
|
||||
* @property {number} [initialNodeIndex]
|
||||
*/
|
||||
|
||||
const kFirstWordLength = "Supercalifragilisticexpialidocious".length; // 34
|
||||
const kParentNodeIndex = -1; // special value
|
||||
const cases = [
|
||||
{
|
||||
title: "Eats inline whitespaces after word",
|
||||
text: "Supercalifragilisticexpialidocious foo bar fussball",
|
||||
reverse: true,
|
||||
div: {
|
||||
expectedOffset: kFirstWordLength + 1
|
||||
},
|
||||
textarea: {
|
||||
expectedOffset: kFirstWordLength + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Eats inline whitespaces across a wrapped line after word",
|
||||
text: "Supercalifragilisticexpialidocious foo bar fussball",
|
||||
reverse: true,
|
||||
div: {
|
||||
expectedOffset: kFirstWordLength + 7
|
||||
},
|
||||
textarea: {
|
||||
expectedOffset: kFirstWordLength + 7
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Eats inline whitespaces across multiple wrapped lines after word",
|
||||
text: `Supercalifragilisticexpialidocious foo bar fussball`,
|
||||
reverse: true,
|
||||
div: {
|
||||
expectedOffset: kFirstWordLength + 65
|
||||
},
|
||||
textarea: {
|
||||
expectedOffset: kFirstWordLength + 65
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Eats inline whitespaces after a whole word and stop before a newline",
|
||||
text: "Supercalifragilisticexpialidocious \nfoo bar fussball",
|
||||
reverse: true,
|
||||
div: {
|
||||
expectedOffset: 1,
|
||||
expectedNodeIndex: kParentNodeIndex
|
||||
},
|
||||
textarea: {
|
||||
expectedOffset: kFirstWordLength + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Eats inline whitespaces after a partial word and stop before a newline",
|
||||
text: "Supercalifragilisticexpialidocious \nfoo bar fussball",
|
||||
div: {
|
||||
initialOffset: 5,
|
||||
expectedOffset: 1,
|
||||
expectedNodeIndex: kParentNodeIndex
|
||||
},
|
||||
textarea: {
|
||||
initialOffset: 5,
|
||||
expectedOffset: kFirstWordLength + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Eats inline whitespaces without a word and stop before a newline",
|
||||
text: "Supercalifragilisticexpialidocious \n foo bar fussball",
|
||||
// TODO(krosylight): Currently ClusterIterator internally skips trailing whitespace
|
||||
// div: {
|
||||
// initialOffset: kFirstWordLength,
|
||||
// expectedOffset: 1,
|
||||
// expectedNodeIndex: kParentNodeIndex
|
||||
// },
|
||||
textarea: {
|
||||
initialOffset: kFirstWordLength,
|
||||
expectedOffset: kFirstWordLength + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Jumps to the next line and eats inline whitespaces",
|
||||
text: "Supercalifragilisticexpialidocious \n foo bar fussball",
|
||||
reverse: true,
|
||||
div: {
|
||||
initialOffset: kFirstWordLength + 1,
|
||||
expectedOffset: 6,
|
||||
expectedNodeIndex: 2
|
||||
},
|
||||
textarea: {
|
||||
initialOffset: kFirstWordLength + 1,
|
||||
expectedOffset: kFirstWordLength + 8
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Stops on whitespaces after word",
|
||||
text: "Supercalifragilisticexpialidocious \n foo bar fussball",
|
||||
backward: true,
|
||||
div: {
|
||||
initialOffset: 8,
|
||||
initialNodeIndex: 2,
|
||||
expectedOffset: 6,
|
||||
expectedNodeIndex: 2
|
||||
},
|
||||
textarea: {
|
||||
initialOffset: 44, // middle of "foo"
|
||||
expectedOffset: 42
|
||||
}
|
||||
},
|
||||
// TODO(krosylight): Currently no way to tell a word break is from line wrapping
|
||||
// {
|
||||
// title: "Ignore a word break introduced by line wrapping",
|
||||
// text: "Supercalifragilisticexpialidociouspostfix",
|
||||
// className: "narrow break-word",
|
||||
// div: {
|
||||
// expectedOffset: kFirstWordLength + 7
|
||||
// },
|
||||
// textarea: {
|
||||
// expectedOffset: kFirstWordLength + 7
|
||||
// }
|
||||
// }
|
||||
{
|
||||
title: "Jump only one line at an empty line end",
|
||||
text: "Supercalifragilisticexpialidocious \n\nfoo bar fussball",
|
||||
reverse: true,
|
||||
div: {
|
||||
initialOffset: 2,
|
||||
initialNodeIndex: kParentNodeIndex,
|
||||
expectedOffset: 0,
|
||||
expectedNodeIndex: 3
|
||||
},
|
||||
textarea: {
|
||||
initialOffset: kFirstWordLength + 2,
|
||||
expectedOffset: kFirstWordLength + 3
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Jump only one line at a non-empty line end",
|
||||
text: "Supercalifragilisticexpialidocious \n\nfoo bar fussball",
|
||||
reverse: true,
|
||||
div: {
|
||||
initialOffset: kFirstWordLength + 1,
|
||||
expectedOffset: 2,
|
||||
expectedNodeIndex: -1
|
||||
},
|
||||
textarea: {
|
||||
initialOffset: kFirstWordLength + 1,
|
||||
expectedOffset: kFirstWordLength + 2
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.waitForFocus(async () => {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["layout.word_select.eat_space_to_next_word", true]]
|
||||
});
|
||||
// Test on div first to prevent Bug 1623413
|
||||
for (const testCase of cases) {
|
||||
if (testCase.div) {
|
||||
testOnDiv(testCase);
|
||||
}
|
||||
}
|
||||
for (const testCase of cases) {
|
||||
testOnTextarea(testCase);
|
||||
}
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
/** @param {TestCase} testCase */
|
||||
function testOnDiv(testCase) {
|
||||
const {
|
||||
title,
|
||||
backward = false,
|
||||
reverse = false,
|
||||
} = testCase;
|
||||
const {
|
||||
initialOffset = 0,
|
||||
expectedOffset,
|
||||
} = testCase.div;
|
||||
if (expectedOffset === undefined) {
|
||||
throw new Error("`expectedOffset` must be defined.")
|
||||
}
|
||||
|
||||
testDiv.innerHTML = testCase.text.replaceAll(/ {2}/g, " ").replaceAll(/\n/g, "<br>");
|
||||
const initialNode = childNode(testDiv, testCase.div.initialNodeIndex);
|
||||
const expectedNode = childNode(testDiv, testCase.div.expectedNodeIndex);
|
||||
|
||||
const selection = getSelection();
|
||||
selection.collapse(initialNode, initialOffset);
|
||||
|
||||
moveByWord(backward);
|
||||
|
||||
is(selection.focusNode, expectedNode, `focusNode in "${title}"`);
|
||||
is(selection.focusOffset, expectedOffset, `focusOffset in "${title}"`);
|
||||
|
||||
if (reverse) {
|
||||
selection.collapse(expectedNode, expectedOffset);
|
||||
|
||||
moveByWord(!backward);
|
||||
|
||||
is(selection.focusNode, initialNode, `focusNode with reversed selection in "${title}"`);
|
||||
is(selection.focusOffset, initialOffset, `focusOffset with reversed selection in "${title}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function testOnTextarea(testCase) {
|
||||
const {
|
||||
title,
|
||||
backward = false,
|
||||
reverse = false,
|
||||
} = testCase;
|
||||
const {
|
||||
initialOffset = 0,
|
||||
expectedOffset,
|
||||
} = testCase.textarea;
|
||||
if (expectedOffset === undefined) {
|
||||
throw new Error("`expectedOffset` must be defined.")
|
||||
}
|
||||
|
||||
testTextarea.value = testCase.text;
|
||||
testTextarea.selectionStart = testTextarea.selectionEnd = initialOffset;
|
||||
testTextarea.focus();
|
||||
|
||||
moveByWord(backward);
|
||||
|
||||
is(testTextarea.selectionStart, expectedOffset, `selectionStart in "${title}"`);
|
||||
|
||||
if (reverse) {
|
||||
testTextarea.selectionStart = testTextarea.selectionEnd = expectedOffset;
|
||||
|
||||
moveByWord(!backward);
|
||||
|
||||
is(testTextarea.selectionStart, initialOffset, `selectionStart with reversed selection in "${title}"`);
|
||||
}
|
||||
}
|
||||
|
||||
function childNode(parent, index = 0) {
|
||||
if (index === kParentNodeIndex) {
|
||||
return parent;
|
||||
}
|
||||
return parent.childNodes[index];
|
||||
}
|
||||
|
||||
/** @param {boolean} backward */
|
||||
function moveByWord(backward) {
|
||||
const dir = backward ? "Previous" : "Next";
|
||||
SpecialPowers.doCommand(window, `cmd_word${dir}`);
|
||||
}
|
||||
</script>
|
Загрузка…
Ссылка в новой задаче