Bug 1680548 - Cache the max length in TextControlState::SelectionProperties r=masayuki

GetValue() is not a cheap operation, and this allows to skip it in SetSelectionRange.

Differential Revision: https://phabricator.services.mozilla.com/D98659
This commit is contained in:
Kagami Sascha Rosylight
2020-12-05 09:34:59 +00:00
parent 1ddd56ea7e
commit f8cb249c67
3 changed files with 41 additions and 24 deletions

View File

@@ -2069,17 +2069,6 @@ void TextControlState::SetSelectionRange(
bool changed = false; bool changed = false;
nsresult rv = NS_OK; // For the ScrollSelectionIntoView() return value. nsresult rv = NS_OK; // For the ScrollSelectionIntoView() return value.
if (IsSelectionCached()) { if (IsSelectionCached()) {
nsAutoString value;
// XXXbz is "false" the right thing to pass here? Hard to tell, given the
// various mismatches between our impl and the spec.
GetValue(value, false);
uint32_t length = value.Length();
if (aStart > length) {
aStart = length;
}
if (aEnd > length) {
aEnd = length;
}
SelectionProperties& props = GetSelectionProperties(); SelectionProperties& props = GetSelectionProperties();
changed = props.GetStart() != aStart || props.GetEnd() != aEnd || changed = props.GetStart() != aStart || props.GetEnd() != aEnd ||
props.GetDirection() != aDirection; props.GetDirection() != aDirection;
@@ -2402,6 +2391,7 @@ void TextControlState::UnbindFromFrame(nsTextControlFrame* aFrame) {
GetSelectionDirection(IgnoreErrors()); GetSelectionDirection(IgnoreErrors());
SelectionProperties& props = GetSelectionProperties(); SelectionProperties& props = GetSelectionProperties();
props.SetMaxLength(value.Length());
props.SetStart(start); props.SetStart(start);
props.SetEnd(end); props.SetEnd(end);
props.SetDirection(direction); props.SetDirection(direction);
@@ -2902,6 +2892,7 @@ bool TextControlState::SetValueWithoutTextEditor(
aHandlingSetValue.GetSetValueFlags())); aHandlingSetValue.GetSetValueFlags()));
SelectionProperties& props = GetSelectionProperties(); SelectionProperties& props = GetSelectionProperties();
props.SetMaxLength(aHandlingSetValue.GetSettingValue().Length());
if (aHandlingSetValue.GetSetValueFlags() & if (aHandlingSetValue.GetSetValueFlags() &
eSetValue_MoveCursorToEndIfValueChanged) { eSetValue_MoveCursorToEndIfValueChanged) {
props.SetStart(aHandlingSetValue.GetSettingValue().Length()); props.SetStart(aHandlingSetValue.GetSettingValue().Length());
@@ -2912,13 +2903,6 @@ bool TextControlState::SetValueWithoutTextEditor(
props.SetStart(0); props.SetStart(0);
props.SetEnd(0); props.SetEnd(0);
props.SetDirection(nsITextControlFrame::eForward); props.SetDirection(nsITextControlFrame::eForward);
} else {
// Make sure our cached selection position is not outside the new
// value.
props.SetStart(std::min(
props.GetStart(), aHandlingSetValue.GetSettingValue().Length()));
props.SetEnd(std::min(props.GetEnd(),
aHandlingSetValue.GetSettingValue().Length()));
} }
} }

View File

@@ -275,8 +275,6 @@ class TextControlState final : public SupportsWeakPtr {
struct SelectionProperties { struct SelectionProperties {
public: public:
SelectionProperties()
: mStart(0), mEnd(0), mDirection(nsITextControlFrame::eForward) {}
bool IsDefault() const { bool IsDefault() const {
return mStart == 0 && mEnd == 0 && return mStart == 0 && mEnd == 0 &&
mDirection == nsITextControlFrame::eForward; mDirection == nsITextControlFrame::eForward;
@@ -284,12 +282,12 @@ class TextControlState final : public SupportsWeakPtr {
uint32_t GetStart() const { return mStart; } uint32_t GetStart() const { return mStart; }
void SetStart(uint32_t value) { void SetStart(uint32_t value) {
mIsDirty = true; mIsDirty = true;
mStart = value; mStart = std::min(value, mMaxLength);
} }
uint32_t GetEnd() const { return mEnd; } uint32_t GetEnd() const { return mEnd; }
void SetEnd(uint32_t value) { void SetEnd(uint32_t value) {
mIsDirty = true; mIsDirty = true;
mEnd = value; mEnd = std::min(value, mMaxLength);
} }
nsITextControlFrame::SelectionDirection GetDirection() const { nsITextControlFrame::SelectionDirection GetDirection() const {
return mDirection; return mDirection;
@@ -298,15 +296,25 @@ class TextControlState final : public SupportsWeakPtr {
mIsDirty = true; mIsDirty = true;
mDirection = value; mDirection = value;
} }
void SetMaxLength(uint32_t aMax) {
mMaxLength = aMax;
// recompute against the new max length
SetStart(GetStart());
SetEnd(GetEnd());
}
// return true only if mStart, mEnd, or mDirection have been modified, // return true only if mStart, mEnd, or mDirection have been modified,
// or if SetIsDirty() was explicitly called. // or if SetIsDirty() was explicitly called.
bool IsDirty() const { return mIsDirty; } bool IsDirty() const { return mIsDirty; }
void SetIsDirty() { mIsDirty = true; } void SetIsDirty() { mIsDirty = true; }
private: private:
uint32_t mStart, mEnd; uint32_t mStart = 0;
uint32_t mEnd = 0;
uint32_t mMaxLength = 0;
bool mIsDirty = false; bool mIsDirty = false;
nsITextControlFrame::SelectionDirection mDirection; nsITextControlFrame::SelectionDirection mDirection =
nsITextControlFrame::eForward;
}; };
bool IsSelectionCached() const { return mSelectionCached; } bool IsSelectionCached() const { return mSelectionCached; }

View File

@@ -144,6 +144,31 @@
} }
}, "selectionEnd edge-case values"); }, "selectionEnd edge-case values");
test(() => {
for (const el of createTestElements(testValue)) {
el.selectionStart = 200;
assert_equals(el.selectionStart, testValue.length);
el.remove();
}
}, "selectionStart should be clamped by the current value length");
test(() => {
for (const el of createTestElements(testValue)) {
el.selectionStart = 300;
assert_equals(el.selectionEnd, testValue.length);
el.remove();
}
}, "selectionEnd should be clamped by the current value length");
test(() => {
for (const el of createTestElements(testValue)) {
el.setSelectionRange(200, 300);
assert_equals(el.selectionStart, testValue.length);
assert_equals(el.selectionEnd, testValue.length);
el.remove();
}
}, "setSelectionRange should be clamped by the current value length");
test(() => { test(() => {
for (let el of createTestElements(testValue)) { for (let el of createTestElements(testValue)) {
const start = 1; const start = 1;