mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
+78
-19
@@ -928,12 +928,38 @@ using namespace std;
|
||||
void
|
||||
imgCacheQueue::Remove(imgCacheEntry* entry)
|
||||
{
|
||||
queueContainer::iterator it = find(mQueue.begin(), mQueue.end(), entry);
|
||||
if (it != mQueue.end()) {
|
||||
mSize -= (*it)->GetDataSize();
|
||||
mQueue.erase(it);
|
||||
MarkDirty();
|
||||
uint64_t index = mQueue.IndexOf(entry);
|
||||
if (index == queueContainer::NoIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
mSize -= mQueue[index]->GetDataSize();
|
||||
|
||||
// If the queue is clean and this is the first entry,
|
||||
// then we can efficiently remove the entry without
|
||||
// dirtying the sort order.
|
||||
if (!IsDirty() && index == 0) {
|
||||
std::pop_heap(mQueue.begin(), mQueue.end(),
|
||||
imgLoader::CompareCacheEntries);
|
||||
mQueue.RemoveElementAt(mQueue.Length() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove from the middle of the list. This potentially
|
||||
// breaks the binary heap sort order.
|
||||
mQueue.RemoveElementAt(index);
|
||||
|
||||
// If we only have one entry or the queue is empty, though,
|
||||
// then the sort order is still effectively good.
|
||||
// Simply refresh the list to clear the dirty flag.
|
||||
if (mQueue.Length() <= 1) {
|
||||
Refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we must mark the queue dirty and potentially
|
||||
// trigger an expensive sort later.
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -942,23 +968,26 @@ imgCacheQueue::Push(imgCacheEntry* entry)
|
||||
mSize += entry->GetDataSize();
|
||||
|
||||
RefPtr<imgCacheEntry> refptr(entry);
|
||||
mQueue.push_back(refptr);
|
||||
MarkDirty();
|
||||
mQueue.AppendElement(Move(refptr));
|
||||
// If we're not dirty already, then we can efficiently add this to the binary heap immediately.
|
||||
if (!IsDirty()) {
|
||||
std::push_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<imgCacheEntry>
|
||||
imgCacheQueue::Pop()
|
||||
{
|
||||
if (mQueue.empty()) {
|
||||
if (mQueue.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (IsDirty()) {
|
||||
Refresh();
|
||||
}
|
||||
|
||||
RefPtr<imgCacheEntry> entry = mQueue[0];
|
||||
std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
|
||||
mQueue.pop_back();
|
||||
RefPtr<imgCacheEntry> entry = Move(mQueue.LastElement());
|
||||
mQueue.RemoveElementAt(mQueue.Length() - 1);
|
||||
|
||||
mSize -= entry->GetDataSize();
|
||||
return entry.forget();
|
||||
@@ -967,6 +996,7 @@ imgCacheQueue::Pop()
|
||||
void
|
||||
imgCacheQueue::Refresh()
|
||||
{
|
||||
// Re-heap the list. This is an O(3 * n) operation and best avoided if possible.
|
||||
std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
|
||||
mDirty = false;
|
||||
}
|
||||
@@ -986,7 +1016,13 @@ imgCacheQueue::IsDirty()
|
||||
uint32_t
|
||||
imgCacheQueue::GetNumElements() const
|
||||
{
|
||||
return mQueue.size();
|
||||
return mQueue.Length();
|
||||
}
|
||||
|
||||
bool
|
||||
imgCacheQueue::Contains(imgCacheEntry* aEntry) const
|
||||
{
|
||||
return mQueue.Contains(aEntry);
|
||||
}
|
||||
|
||||
imgCacheQueue::iterator
|
||||
@@ -1540,7 +1576,11 @@ void
|
||||
imgLoader::CacheEntriesChanged(bool aForChrome, int32_t aSizeDiff /* = 0 */)
|
||||
{
|
||||
imgCacheQueue& queue = GetCacheQueue(aForChrome);
|
||||
queue.MarkDirty();
|
||||
// We only need to dirty the queue if there is any sorting taking place.
|
||||
// Empty or single-entry lists can't become dirty.
|
||||
if (queue.GetNumElements() > 1) {
|
||||
queue.MarkDirty();
|
||||
}
|
||||
queue.UpdateSize(aSizeDiff);
|
||||
}
|
||||
|
||||
@@ -1569,7 +1609,9 @@ imgLoader::CheckCacheLimits(imgCacheTable& cache, imgCacheQueue& queue)
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
RemoveFromCache(entry);
|
||||
// We just popped this entry from the queue, so pass AlreadyRemoved
|
||||
// to avoid searching the queue again in RemoveFromCache.
|
||||
RemoveFromCache(entry, QueueState::AlreadyRemoved);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1870,7 +1912,7 @@ imgLoader::RemoveFromCache(const ImageCacheKey& aKey)
|
||||
}
|
||||
|
||||
bool
|
||||
imgLoader::RemoveFromCache(imgCacheEntry* entry)
|
||||
imgLoader::RemoveFromCache(imgCacheEntry* entry, QueueState aQueueState)
|
||||
{
|
||||
LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
|
||||
|
||||
@@ -1886,13 +1928,24 @@ imgLoader::RemoveFromCache(imgCacheEntry* entry)
|
||||
|
||||
cache.Remove(key);
|
||||
|
||||
if (queue.IsDirty()) {
|
||||
queue.Refresh();
|
||||
}
|
||||
|
||||
if (entry->HasNoProxies()) {
|
||||
LOG_STATIC_FUNC(gImgLog,
|
||||
"imgLoader::RemoveFromCache removing from tracker");
|
||||
if (mCacheTracker) {
|
||||
mCacheTracker->RemoveObject(entry);
|
||||
}
|
||||
queue.Remove(entry);
|
||||
// Only search the queue to remove the entry if its possible it might
|
||||
// be in the queue. If we know its not in the queue this would be
|
||||
// wasted work.
|
||||
MOZ_ASSERT_IF(aQueueState == QueueState::AlreadyRemoved,
|
||||
!queue.Contains(entry));
|
||||
if (aQueueState == QueueState::MaybeExists) {
|
||||
queue.Remove(entry);
|
||||
}
|
||||
}
|
||||
|
||||
entry->SetEvicted(true);
|
||||
@@ -1937,13 +1990,13 @@ imgLoader::EvictEntries(imgCacheQueue& aQueueToClear)
|
||||
// We have to make a temporary, since RemoveFromCache removes the element
|
||||
// from the queue, invalidating iterators.
|
||||
nsTArray<RefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
|
||||
for (imgCacheQueue::const_iterator i = aQueueToClear.begin();
|
||||
i != aQueueToClear.end(); ++i) {
|
||||
for (auto i = aQueueToClear.begin(); i != aQueueToClear.end(); ++i) {
|
||||
entries.AppendElement(*i);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < entries.Length(); ++i) {
|
||||
if (!RemoveFromCache(entries[i])) {
|
||||
// Iterate in reverse order to minimize array copying.
|
||||
for (auto& entry : entries) {
|
||||
if (!RemoveFromCache(entry)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
@@ -2732,6 +2785,12 @@ imgCacheValidator::AddProxy(imgRequestProxy* aProxy)
|
||||
mProxies.AppendObject(aProxy);
|
||||
}
|
||||
|
||||
void
|
||||
imgCacheValidator::RemoveProxy(imgRequestProxy* aProxy)
|
||||
{
|
||||
mProxies.RemoveObject(aProxy);
|
||||
}
|
||||
|
||||
/** nsIRequestObserver methods **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
||||
+13
-2
@@ -177,7 +177,8 @@ public:
|
||||
uint32_t GetSize() const;
|
||||
void UpdateSize(int32_t diff);
|
||||
uint32_t GetNumElements() const;
|
||||
typedef std::vector<RefPtr<imgCacheEntry> > queueContainer;
|
||||
bool Contains(imgCacheEntry* aEntry) const;
|
||||
typedef nsTArray<RefPtr<imgCacheEntry> > queueContainer;
|
||||
typedef queueContainer::iterator iterator;
|
||||
typedef queueContainer::const_iterator const_iterator;
|
||||
|
||||
@@ -315,7 +316,16 @@ public:
|
||||
nsresult InitCache();
|
||||
|
||||
bool RemoveFromCache(const ImageCacheKey& aKey);
|
||||
bool RemoveFromCache(imgCacheEntry* entry);
|
||||
|
||||
// Enumeration describing if a given entry is in the cache queue or not.
|
||||
// There are some cases we know the entry is definitely not in the queue.
|
||||
enum class QueueState {
|
||||
MaybeExists,
|
||||
AlreadyRemoved
|
||||
};
|
||||
|
||||
bool RemoveFromCache(imgCacheEntry* entry,
|
||||
QueueState aQueueState = QueueState::MaybeExists);
|
||||
|
||||
bool PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* aEntry);
|
||||
|
||||
@@ -515,6 +525,7 @@ public:
|
||||
bool forcePrincipalCheckForCacheEntry);
|
||||
|
||||
void AddProxy(imgRequestProxy* aProxy);
|
||||
void RemoveProxy(imgRequestProxy* aProxy);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
|
||||
|
||||
@@ -342,8 +342,14 @@ imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
|
||||
bool oldIsInLoadGroup = mIsInLoadGroup;
|
||||
mIsInLoadGroup = false;
|
||||
|
||||
if (GetOwner()) {
|
||||
GetOwner()->RemoveProxy(this, aStatus);
|
||||
imgRequest* owner = GetOwner();
|
||||
if (owner) {
|
||||
imgCacheValidator* validator = owner->GetValidator();
|
||||
if (validator) {
|
||||
validator->RemoveProxy(this);
|
||||
}
|
||||
|
||||
owner->RemoveProxy(this, aStatus);
|
||||
}
|
||||
|
||||
mIsInLoadGroup = oldIsInLoadGroup;
|
||||
|
||||
@@ -253,6 +253,67 @@ function ArraySort(comparefn) {
|
||||
return MergeSort(O, len, comparefn);
|
||||
}
|
||||
|
||||
// ES2023 22.1.3.30 Array.prototype.toSorted ( comparefn )
|
||||
function ArrayToSorted(comparefn) {
|
||||
if (comparefn !== undefined) {
|
||||
if (!IsCallable(comparefn)) {
|
||||
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, comparefn));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1: Let O be ? ToObject(this). Let len be ? ToLength(O.length).
|
||||
var O = ToObject(this);
|
||||
var len = ToLength(O.length);
|
||||
|
||||
// Step 2: Snapshot values in ascending index order into a List.
|
||||
var items = new List();
|
||||
var itemsLen = len;
|
||||
for (var k = 0; k < len; k++) {
|
||||
items[k] = O[k];
|
||||
}
|
||||
|
||||
// Step 3: Create SortCompare per spec.
|
||||
var wrappedCompareFn = comparefn;
|
||||
var sortCompare;
|
||||
if (wrappedCompareFn === undefined) {
|
||||
sortCompare = function(x, y) {
|
||||
if (x === undefined)
|
||||
return y === undefined ? 0 : 1;
|
||||
if (y === undefined)
|
||||
return -1;
|
||||
|
||||
var xString = ToString(x);
|
||||
var yString = ToString(y);
|
||||
if (xString < yString)
|
||||
return -1;
|
||||
if (xString > yString)
|
||||
return 1;
|
||||
return 0;
|
||||
};
|
||||
} else {
|
||||
sortCompare = function(x, y) {
|
||||
if (x === undefined)
|
||||
return y === undefined ? 0 : 1;
|
||||
if (y === undefined)
|
||||
return -1;
|
||||
|
||||
var v = ToNumber(wrappedCompareFn(x, y));
|
||||
return v !== v ? 0 : v;
|
||||
};
|
||||
}
|
||||
|
||||
// Step 4: Sort the snapshot List using SortCompare.
|
||||
if (itemsLen > 1)
|
||||
MergeSort(items, itemsLen, sortCompare);
|
||||
|
||||
// Step 5: Create result array and write sorted values.
|
||||
var A = ArraySpeciesCreate(O, len);
|
||||
for (var j = 0; j < itemsLen; j++)
|
||||
_DefineDataProperty(A, j, items[j]);
|
||||
|
||||
return A;
|
||||
}
|
||||
|
||||
/* ES5 15.4.4.18. */
|
||||
function ArrayForEach(callbackfn/*, thisArg*/) {
|
||||
/* Step 1. */
|
||||
|
||||
+3
-1
@@ -3208,6 +3208,7 @@ static const JSFunctionSpec array_methods[] = {
|
||||
/* ES2023 proposals */
|
||||
JS_SELF_HOSTED_FN("findLast", "ArrayFindLast", 1,0),
|
||||
JS_SELF_HOSTED_FN("findLastIndex", "ArrayFindLastIndex", 1,0),
|
||||
JS_SELF_HOSTED_FN("toSorted", "ArrayToSorted", 1,0),
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
@@ -3379,7 +3380,8 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto)
|
||||
!DefineProperty(cx, unscopables, cx->names().flatMap, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().includes, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().keys, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().values, value))
|
||||
!DefineProperty(cx, unscopables, cx->names().values, value) ||
|
||||
!DefineProperty(cx, unscopables, cx->names().toSorted, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>Array.prototype.toSorted test</title>
|
||||
<style>
|
||||
body { font: 14px/1.4 sans-serif; padding: 16px; }
|
||||
.pass { color: #0a0; }
|
||||
.fail { color: #c00; }
|
||||
pre { white-space: pre-wrap; }
|
||||
</style>
|
||||
<pre id="log"></pre>
|
||||
<script>
|
||||
(function() {
|
||||
const log = document.getElementById("log");
|
||||
function write(msg, cls) {
|
||||
const line = document.createElement("div");
|
||||
if (cls) line.className = cls;
|
||||
line.textContent = msg;
|
||||
log.appendChild(line);
|
||||
}
|
||||
|
||||
window.assertEq = function(actual, expected, msg) {
|
||||
if (actual !== expected) {
|
||||
throw new Error((msg ? msg + ": " : "") +
|
||||
"expected " + expected + ", got " + actual);
|
||||
}
|
||||
};
|
||||
|
||||
window.assertThrowsInstanceOf = function(fn, ctor, msg) {
|
||||
let threw = false;
|
||||
try {
|
||||
fn();
|
||||
} catch (e) {
|
||||
if (e instanceof ctor) {
|
||||
threw = true;
|
||||
} else {
|
||||
throw new Error((msg ? msg + ": " : "") +
|
||||
"threw " + e + ", expected " + ctor.name);
|
||||
}
|
||||
}
|
||||
if (!threw) {
|
||||
throw new Error((msg ? msg + ": " : "") + "did not throw");
|
||||
}
|
||||
};
|
||||
|
||||
window.reportCompare = function() {};
|
||||
|
||||
let hadError = false;
|
||||
window.addEventListener("error", function(e) {
|
||||
hadError = true;
|
||||
write("FAIL: " + e.message, "fail");
|
||||
});
|
||||
window.addEventListener("load", function() {
|
||||
write("PASS: toSorted.js loaded", "pass");
|
||||
if (!hadError)
|
||||
write("PASS: all toSorted tests passed", "pass");
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="toSorted.js"></script>
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/ */
|
||||
|
||||
assertEq(typeof Array.prototype.toSorted, "function");
|
||||
|
||||
// Non-mutating behavior.
|
||||
let original = [3, 1, 2];
|
||||
let sorted = original.toSorted();
|
||||
assertEq(original !== sorted, true);
|
||||
assertEq(original.join(","), "3,1,2");
|
||||
assertEq(sorted.join(","), "1,2,3");
|
||||
|
||||
// Compare function.
|
||||
let nums = [10, 1, 5];
|
||||
let desc = nums.toSorted((a, b) => b - a);
|
||||
assertEq(desc.join(","), "10,5,1");
|
||||
|
||||
// Stable sort.
|
||||
let stableInput = [
|
||||
{v: 1, id: "a"},
|
||||
{v: 1, id: "b"},
|
||||
{v: 1, id: "c"}
|
||||
];
|
||||
let stableSorted = stableInput.toSorted((x, y) => x.v - y.v);
|
||||
assertEq(stableSorted.map(o => o.id).join(""), "abc");
|
||||
|
||||
// Holes are treated as undefined (properties are created).
|
||||
let sparse = [3, , 1];
|
||||
let sparseSorted = sparse.toSorted();
|
||||
assertEq(sparseSorted.length, 3);
|
||||
assertEq(sparseSorted[0], 1);
|
||||
assertEq(sparseSorted[1], 3);
|
||||
assertEq(2 in sparseSorted, true);
|
||||
assertEq(sparseSorted[2], undefined);
|
||||
|
||||
// Array-like input.
|
||||
let arrayLike = {0: 2, 1: 1, length: 2};
|
||||
let arrayLikeSorted = Array.prototype.toSorted.call(arrayLike);
|
||||
assertEq(Array.isArray(arrayLikeSorted), true);
|
||||
assertEq(arrayLikeSorted.join(","), "1,2");
|
||||
|
||||
// Getter access order (ascending indices).
|
||||
let accessLog = [];
|
||||
let getterArr = {
|
||||
length: 3,
|
||||
get 0() { accessLog.push(0); return 3; },
|
||||
get 1() { accessLog.push(1); return 1; },
|
||||
get 2() { accessLog.push(2); return 2; }
|
||||
};
|
||||
Array.prototype.toSorted.call(getterArr);
|
||||
assertEq(accessLog.join(","), "0,1,2");
|
||||
|
||||
// Comparator errors propagate.
|
||||
assertThrowsInstanceOf(() => [1, 2].toSorted(1), TypeError);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
@@ -428,6 +428,7 @@
|
||||
macro(toJSON, toJSON, "toJSON") \
|
||||
macro(toLocaleString, toLocaleString, "toLocaleString") \
|
||||
macro(toSource, toSource, "toSource") \
|
||||
macro(toSorted, toSorted, "toSorted") \
|
||||
macro(toString, toString, "toString") \
|
||||
macro(toUTCString, toUTCString, "toUTCString") \
|
||||
macro(true, true_, "true") \
|
||||
|
||||
@@ -206,11 +206,7 @@ pref("dom.permissions.revoke.enable", false);
|
||||
pref("dom.performance.time_to_non_blank_paint.enabled", false);
|
||||
|
||||
// Enable Performance Observer API
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("dom.enable_performance_observer", true);
|
||||
#else
|
||||
pref("dom.enable_performance_observer", false);
|
||||
#endif
|
||||
|
||||
// Enable requestIdleCallback API
|
||||
pref("dom.requestIdleCallback.enabled", true);
|
||||
|
||||
@@ -3,5 +3,4 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
|
||||
FINAL_TARGET_FILES.fonts += ['TwemojiMozilla.ttf']
|
||||
FINAL_TARGET_FILES.fonts += ['TwemojiMozilla.ttf']
|
||||
|
||||
@@ -127,9 +127,15 @@ public:
|
||||
*/
|
||||
nsresult AddObjectLocked(T* aObj, const AutoLock& aAutoLock)
|
||||
{
|
||||
if (NS_WARN_IF(!aObj)) {
|
||||
// Invalid object to add
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
nsExpirationState* state = aObj->GetExpirationState();
|
||||
NS_ASSERTION(!state->IsTracked(),
|
||||
"Tried to add an object that's already tracked");
|
||||
if (NS_WARN_IF(state->IsTracked())) {
|
||||
// Tried to add an object that's already tracked.
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
nsTArray<T*>& generation = mGenerations[mNewestGeneration];
|
||||
uint32_t index = generation.Length();
|
||||
if (index > nsExpirationState::MAX_INDEX_IN_GENERATION) {
|
||||
@@ -156,8 +162,15 @@ public:
|
||||
*/
|
||||
void RemoveObjectLocked(T* aObj, const AutoLock& aAutoLock)
|
||||
{
|
||||
if (NS_WARN_IF(!aObj)) {
|
||||
// Invalid object to remove
|
||||
return;
|
||||
}
|
||||
nsExpirationState* state = aObj->GetExpirationState();
|
||||
NS_ASSERTION(state->IsTracked(), "Tried to remove an object that's not tracked");
|
||||
if (NS_WARN_IF(!state->IsTracked())) {
|
||||
// Tried to remove an object that's not tracked
|
||||
return;
|
||||
}
|
||||
nsTArray<T*>& generation = mGenerations[state->mGeneration];
|
||||
uint32_t index = state->mIndexInGeneration;
|
||||
NS_ASSERTION(generation.Length() > index &&
|
||||
@@ -458,11 +471,13 @@ class nsExpirationTracker : protected ::detail::SingleThreadedExpirationTracker<
|
||||
Lock mLock;
|
||||
|
||||
AutoLock FakeLock() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
||||
return AutoLock(mLock);
|
||||
}
|
||||
|
||||
Lock& GetMutex() override
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
||||
return mLock;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user