mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-26 00:57:30 +09:00
More optimisations n shit
This commit is contained in:
parent
414ce93828
commit
7792452105
3 changed files with 86 additions and 9 deletions
|
|
@ -127,7 +127,29 @@ void
|
||||||
nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
|
nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
|
||||||
const nsTArray<nsString>& aTokens)
|
const nsTArray<nsString>& aTokens)
|
||||||
{
|
{
|
||||||
if (!mElement) {
|
if (!mElement || aTokens.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hot path: single-token classList.add() when the class is already present.
|
||||||
|
// Skip attribute rebuild and SetAttr entirely (avoids mutation observers /
|
||||||
|
// style invalidation). Frameworks hit this constantly.
|
||||||
|
if (aTokens.Length() == 1) {
|
||||||
|
const nsString& token = aTokens[0];
|
||||||
|
if (aAttr && aAttr->Contains(token)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoString resultStr;
|
||||||
|
if (aAttr) {
|
||||||
|
aAttr->ToString(resultStr);
|
||||||
|
if (!resultStr.IsEmpty() &&
|
||||||
|
!nsContentUtils::IsHTMLWhitespace(resultStr.Last())) {
|
||||||
|
resultStr.Append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultStr.Append(token);
|
||||||
|
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,6 +183,11 @@ nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
|
||||||
addedClasses.AppendElement(aToken);
|
addedClasses.AppendElement(aToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No new tokens: leave the attribute untouched (do not create class="").
|
||||||
|
if (!oneWasAdded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
|
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,20 +217,37 @@ nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr,
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aAttr, "Need an attribute");
|
MOZ_ASSERT(aAttr, "Need an attribute");
|
||||||
|
|
||||||
|
if (aTokens.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hot path: single-token classList.remove() when the class is absent.
|
||||||
|
if (aTokens.Length() == 1 && !aAttr->Contains(aTokens[0])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nsAutoString input;
|
nsAutoString input;
|
||||||
aAttr->ToString(input);
|
aAttr->ToString(input);
|
||||||
|
|
||||||
WhitespaceTokenizer tokenizer(input);
|
WhitespaceTokenizer tokenizer(input);
|
||||||
nsAutoString output;
|
nsAutoString output;
|
||||||
|
bool removed = false;
|
||||||
|
|
||||||
while (tokenizer.hasMoreTokens()) {
|
while (tokenizer.hasMoreTokens()) {
|
||||||
auto& currentToken = tokenizer.nextToken();
|
auto& currentToken = tokenizer.nextToken();
|
||||||
if (!aTokens.Contains(currentToken)) {
|
if (aTokens.Contains(currentToken)) {
|
||||||
if (!output.IsEmpty()) {
|
removed = true;
|
||||||
output.Append(char16_t(' '));
|
continue;
|
||||||
}
|
|
||||||
output.Append(currentToken);
|
|
||||||
}
|
}
|
||||||
|
if (!output.IsEmpty()) {
|
||||||
|
output.Append(char16_t(' '));
|
||||||
|
}
|
||||||
|
output.Append(currentToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token set unchanged: skip SetAttr to avoid style/mutation work.
|
||||||
|
if (!removed) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, output, true);
|
mElement->SetAttr(kNameSpaceID_None, mAttrAtom, output, true);
|
||||||
|
|
@ -302,6 +346,16 @@ nsDOMTokenList::ReplaceInternal(const nsAttrValue* aAttr,
|
||||||
const nsAString& aToken,
|
const nsAString& aToken,
|
||||||
const nsAString& aNewToken)
|
const nsAString& aNewToken)
|
||||||
{
|
{
|
||||||
|
// Replacing a token with itself cannot change the token set.
|
||||||
|
if (aToken.Equals(aNewToken)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old token absent: no update (and avoid rewriting whitespace).
|
||||||
|
if (!aAttr->Contains(aToken)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nsAutoString attribute;
|
nsAutoString attribute;
|
||||||
aAttr->ToString(attribute);
|
aAttr->ToString(attribute);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1221,7 +1221,12 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp)
|
||||||
sepstr = cx->names().comma;
|
sepstr = cx->names().comma;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6 is implicit in the loops below.
|
// Step 6: empty arrays always join to the empty string.
|
||||||
|
// (Separator ToString above still runs for side effects / OOM.)
|
||||||
|
if (length == 0) {
|
||||||
|
args.rval().setString(cx->names().empty);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// An optimized version of a special case of steps 7-11: when length==1 and
|
// An optimized version of a special case of steps 7-11: when length==1 and
|
||||||
// the 0th element is a string, ToString() of that element is a no-op and
|
// the 0th element is a string, ToString() of that element is a no-op and
|
||||||
|
|
@ -1381,8 +1386,8 @@ template <JSValueType Type>
|
||||||
DenseElementResult
|
DenseElementResult
|
||||||
ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
|
ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length)
|
||||||
{
|
{
|
||||||
/* An empty array or an array with no elements is already reversed. */
|
/* Empty, singleton, or uninitialized arrays are already reversed. */
|
||||||
if (length == 0 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
|
if (length <= 1 || GetBoxedOrUnboxedInitializedLength<Type>(obj) == 0)
|
||||||
return DenseElementResult::Success;
|
return DenseElementResult::Success;
|
||||||
|
|
||||||
if (Type == JSVAL_TYPE_MAGIC) {
|
if (Type == JSVAL_TYPE_MAGIC) {
|
||||||
|
|
@ -1451,6 +1456,12 @@ js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (!GetLengthProperty(cx, obj, &len))
|
if (!GetLengthProperty(cx, obj, &len))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// length 0/1: reverse is a no-op; return this immediately.
|
||||||
|
if (len <= 1) {
|
||||||
|
args.rval().setObject(*obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
if (!ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||||
ArrayReverseDenseKernelFunctor functor(cx, obj, len);
|
ArrayReverseDenseKernelFunctor functor(cx, obj, len);
|
||||||
DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
|
||||||
|
|
|
||||||
|
|
@ -2329,6 +2329,12 @@ js::str_startsWith(JSContext* cx, unsigned argc, Value* vp)
|
||||||
// Step 13
|
// Step 13
|
||||||
uint32_t searchLen = searchStr->length();
|
uint32_t searchLen = searchStr->length();
|
||||||
|
|
||||||
|
// Empty search string always matches (and is extremely common).
|
||||||
|
if (searchLen == 0) {
|
||||||
|
args.rval().setBoolean(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Step 14
|
// Step 14
|
||||||
if (searchLen + start < searchLen || searchLen + start > textLen) {
|
if (searchLen + start < searchLen || searchLen + start > textLen) {
|
||||||
args.rval().setBoolean(false);
|
args.rval().setBoolean(false);
|
||||||
|
|
@ -2395,6 +2401,12 @@ js::str_endsWith(JSContext* cx, unsigned argc, Value* vp)
|
||||||
// Step 13
|
// Step 13
|
||||||
uint32_t searchLen = searchStr->length();
|
uint32_t searchLen = searchStr->length();
|
||||||
|
|
||||||
|
// Empty search string always matches.
|
||||||
|
if (searchLen == 0) {
|
||||||
|
args.rval().setBoolean(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Step 15 (reordered)
|
// Step 15 (reordered)
|
||||||
if (searchLen > end) {
|
if (searchLen > end) {
|
||||||
args.rval().setBoolean(false);
|
args.rval().setBoolean(false);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue