Files
roytam1 67e04cfead import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1236750 - Introduce a new unit type CSSTransformedLayerPixel. r=kats (4e9bba3d19)
- Bug 1236750 - Add a ViewAs() overload for casting (with a justification) one typed matrix to another. r=kats (c48e143508)
- Bug 1236750 - Add some specialized typedefs of Matrix4x4 to represent layer transform matrices. r=kats (cc50113c98)
- Bug 1239300 - reject promise with null while creating imagebitmap from empty blob; r=smaug (bff7483bf1)
- Bug 1233056 - Long tapping on a link will select a different link from the page r=tnikkel (3a727b33c1)
- Bug 1245674. Null-check mGlobal before dereferencing it in one more place in Promise code. r=smaug (68cf5312dc)
- Bug 1236750 - Use strongly-typed matrices to represent layer transforms in APZ code. r=kats (63931eb2a1)
- Bug 1236750 - Add typed getters for layer transform matrices. r=kats (baef978fe1)
- Bug 1147673 - Relax the ancestor transform assertion a little. r=kats (ec9ce47ba4)
- Bug 1154161 Initialize WidgetQueryContentEvent::mReply r=smaug (6086291313)
- Bug 1240921 - Use nsAutoTArray in nsStyleSet::RuleNodeWithReplacement. r=bz. (66f6823b46)
- Bug 1229437 part 1 - Add a helper function to get the float containing block of a given frame. r=dbaron (72de452e5d)
- Bug 1229437 part 2 - Reparent floats inside pulled ruby segment. r=dbaron (82cc44632d)
- Bug 1229437 part 3 - Support iterating frames of RubyColumn. r=dholbert (5d43e7f6ff)
- Bug 1229437 part 4 - Reparent floats inside pulled ruby column. r=dbaron (4c1a7ff20b)
- Bug 1229437 part 5 - Add crashtests for this bug. r=dbaron (6afabe1604)
- Bug 1229437 followup - Fix sign-compare error in RubyColumn::Iterator on CLOSED TREE. (e93453d00a)
- Bug 1229437 followup 2 - Fix another sign-compare error in RubyColumn::Iterator on CLOSED TREE. (c0bf6a2a7b)
- Bug 1229437 followup 3 - Fix a mistake in RubyColumn::Iterator::SkipUntilExistingFrame(). a=me (1ce408e194)
- bits of Bug 1072150 - Use the opt-out for various sloppy consumers (29d97c59ca)
- Make test_bug946632 compatible with asynchronous scrolling. (bug 1140293, r=mstange) (e975a8350c)
- fix misspatch of 1072150 (a3e580fa4b)
- Bug 686281 - Implement nsStyleImageLayers; r=dbaron (85bb33c8e6)
- Bug 686281 - Rename *background* to *imagelayer*; r=dbaron. (36d90f112d)
- Bug 1230034 part 4 - Make FramePropertyDescriptor to be a template. r=froydnj,dbaron (271cd19b6e)
- Bug 1230034 part 5 - Convert all frame properties which use DeleteValue and ReleaseValue as destructor to be typesafe. r=dbaron (efc8d63c9d)
- Bug 1230034 part 6 - Convert all frame properties which do not hold pointer to be typed. r=dbaron (b5541775f7)
- Bug 1230034 part 7 - Convert nsIFrame::GenConProperty to be typed. r=dbaron (2b71527b2c)
- Bug 1230034 part 8 - Convert frame properties which assert on destructor to be typed. r=dbaron (0f125a3414)
- Bug 1230034 part 9 - Convert FrameLayerBuilder::LayerManagerDataProperty to be typed. r=dbaron (1147498c2f)
- Bug 1230034 part 10 - Convert remaining frame properties to by typed and remove the unsafe declaring macro. r=dbaron (d59d94eac4)
- Bug 1072501: Unmap file mapping on source surface destruction. r=jrmuizel (19fd63890a)
- Bug 1235613 - Make gfxCriticalError/Note strings in gfx/ unique. r=jrmuizel (780c6ff862)
- Bug 1247535 - Fix -Wunreachable-code warning in mfbt/Poison.cpp. r=froydnj (0e7cf60b6d)
- Bug 1239479: Add comments to mfbt/XorShift128PlusRNG.h from the RNG's designer. DONTBUILD r=Waldo (bb674b07ce)
- Add an assertion message to the assert-is-empty in LinkedList::~LinkedList, to indicate to users who hit it that it's the fault of the caller, not the fault of MFBT code. No bug, rs=froydnj (4cad80874c)
- Bug 1221103 - Add a comment to nsIChannel::securityInfo noting that this info may appear on non-nsHttpChannels and how that may happen. r=bz IGNORE IDL (cd9cebc3f2)
- Bug 1001765 - Make login credentials in Saved Passwords manager editable. r=MattN (09eec4f6f8)
- Bug 1188478 - Add an Import button to the password manager to open the browser migrator. r=dolske (d1126a89fc)
- Bug 1199382 - Rename some strings from "password" to "login" in preferences and the manager. r=markh (60638f5e2a)
- Bug 1207733 - Update @disabled on the Remove Password button when selection changes. r=MattN (64ac9f22f6)
- bits of 1124472 and 1166840 (26e2681183)
- Bug 1219707 - fix argument passing to migration.js, r=jaws (55d332f5c6)
- bug 1215657 - make AccessibleWrap::get_accSelection work with proxies=davidb (4e72111032)
- fix missing telemetry entry (4fcfabb3e6)
and follow-up API changes of TFF#493.
2023-11-09 16:35:18 +08:00

539 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
/* A type-safe doubly-linked list class. */
/*
* The classes LinkedList<T> and LinkedListElement<T> together form a
* convenient, type-safe doubly-linked list implementation.
*
* The class T which will be inserted into the linked list must inherit from
* LinkedListElement<T>. A given object may be in only one linked list at a
* time.
*
* A LinkedListElement automatically removes itself from the list upon
* destruction, and a LinkedList will fatally assert in debug builds if it's
* non-empty when it's destructed.
*
* For example, you might use LinkedList in a simple observer list class as
* follows.
*
* class Observer : public LinkedListElement<Observer>
* {
* public:
* void observe(char* aTopic) { ... }
* };
*
* class ObserverContainer
* {
* private:
* LinkedList<Observer> list;
*
* public:
* void addObserver(Observer* aObserver)
* {
* // Will assert if |aObserver| is part of another list.
* list.insertBack(aObserver);
* }
*
* void removeObserver(Observer* aObserver)
* {
* // Will assert if |aObserver| is not part of some list.
* aObserver.remove();
* // Or, will assert if |aObserver| is not part of |list| specifically.
* // aObserver.removeFrom(list);
* }
*
* void notifyObservers(char* aTopic)
* {
* for (Observer* o = list.getFirst(); o != nullptr; o = o->getNext()) {
* o->observe(aTopic);
* }
* }
* };
*
* Additionally, the class AutoCleanLinkedList<T> is a LinkedList<T> that will
* remove and delete each element still within itself upon destruction. Note
* that because each element is deleted, elements must have been allocated
* using |new|.
*/
#ifndef mozilla_LinkedList_h
#define mozilla_LinkedList_h
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Move.h"
#ifdef __cplusplus
namespace mozilla {
template<typename T>
class LinkedList;
template<typename T>
class LinkedListElement
{
/*
* It's convenient that we return nullptr when getNext() or getPrevious()
* hits the end of the list, but doing so costs an extra word of storage in
* each linked list node (to keep track of whether |this| is the sentinel
* node) and a branch on this value in getNext/getPrevious.
*
* We could get rid of the extra word of storage by shoving the "is
* sentinel" bit into one of the pointers, although this would, of course,
* have performance implications of its own.
*
* But the goal here isn't to win an award for the fastest or slimmest
* linked list; rather, we want a *convenient* linked list. So we won't
* waste time guessing which micro-optimization strategy is best.
*
*
* Speaking of unnecessary work, it's worth addressing here why we wrote
* mozilla::LinkedList in the first place, instead of using stl::list.
*
* The key difference between mozilla::LinkedList and stl::list is that
* mozilla::LinkedList stores the mPrev/mNext pointers in the object itself,
* while stl::list stores the mPrev/mNext pointers in a list element which
* itself points to the object being stored.
*
* mozilla::LinkedList's approach makes it harder to store an object in more
* than one list. But the upside is that you can call next() / prev() /
* remove() directly on the object. With stl::list, you'd need to store a
* pointer to its iterator in the object in order to accomplish this. Not
* only would this waste space, but you'd have to remember to update that
* pointer every time you added or removed the object from a list.
*
* In-place, constant-time removal is a killer feature of doubly-linked
* lists, and supporting this painlessly was a key design criterion.
*/
private:
LinkedListElement* mNext;
LinkedListElement* mPrev;
const bool mIsSentinel;
public:
LinkedListElement()
: mNext(this),
mPrev(this),
mIsSentinel(false)
{ }
LinkedListElement(LinkedListElement<T>&& other)
: mIsSentinel(other.mIsSentinel)
{
if (!other.isInList()) {
mNext = this;
mPrev = this;
return;
}
MOZ_ASSERT(other.mNext->mPrev == &other);
MOZ_ASSERT(other.mPrev->mNext == &other);
/*
* Initialize |this| with |other|'s mPrev/mNext pointers, and adjust those
* element to point to this one.
*/
mNext = other.mNext;
mPrev = other.mPrev;
mNext->mPrev = this;
mPrev->mNext = this;
/*
* Adjust |other| so it doesn't think it's in a list. This makes it
* safely destructable.
*/
other.mNext = &other;
other.mPrev = &other;
}
~LinkedListElement()
{
if (!mIsSentinel && isInList()) {
remove();
}
}
/*
* Get the next element in the list, or nullptr if this is the last element
* in the list.
*/
T* getNext() { return mNext->asT(); }
const T* getNext() const { return mNext->asT(); }
/*
* Get the previous element in the list, or nullptr if this is the first
* element in the list.
*/
T* getPrevious() { return mPrev->asT(); }
const T* getPrevious() const { return mPrev->asT(); }
/*
* Insert aElem after this element in the list. |this| must be part of a
* linked list when you call setNext(); otherwise, this method will assert.
*/
void setNext(T* aElem)
{
MOZ_ASSERT(isInList());
setNextUnsafe(aElem);
}
/*
* Insert aElem before this element in the list. |this| must be part of a
* linked list when you call setPrevious(); otherwise, this method will
* assert.
*/
void setPrevious(T* aElem)
{
MOZ_ASSERT(isInList());
setPreviousUnsafe(aElem);
}
/*
* Remove this element from the list which contains it. If this element is
* not currently part of a linked list, this method asserts.
*/
void remove()
{
MOZ_ASSERT(isInList());
mPrev->mNext = mNext;
mNext->mPrev = mPrev;
mNext = this;
mPrev = this;
}
/*
* Identical to remove(), but also asserts in debug builds that this element
* is in aList.
*/
void removeFrom(const LinkedList<T>& aList)
{
aList.assertContains(asT());
remove();
}
/*
* Return true if |this| part is of a linked list, and false otherwise.
*/
bool isInList() const
{
MOZ_ASSERT((mNext == this) == (mPrev == this));
return mNext != this;
}
private:
friend class LinkedList<T>;
enum NodeKind {
NODE_KIND_NORMAL,
NODE_KIND_SENTINEL
};
explicit LinkedListElement(NodeKind nodeKind)
: mNext(this),
mPrev(this),
mIsSentinel(nodeKind == NODE_KIND_SENTINEL)
{ }
/*
* Return |this| cast to T* if we're a normal node, or return nullptr if
* we're a sentinel node.
*/
T* asT()
{
return mIsSentinel ? nullptr : static_cast<T*>(this);
}
const T* asT() const
{
return mIsSentinel ? nullptr : static_cast<const T*>(this);
}
/*
* Insert aElem after this element, but don't check that this element is in
* the list. This is called by LinkedList::insertFront().
*/
void setNextUnsafe(T* aElem)
{
LinkedListElement *listElem = static_cast<LinkedListElement*>(aElem);
MOZ_ASSERT(!listElem->isInList());
listElem->mNext = this->mNext;
listElem->mPrev = this;
this->mNext->mPrev = listElem;
this->mNext = listElem;
}
/*
* Insert aElem before this element, but don't check that this element is in
* the list. This is called by LinkedList::insertBack().
*/
void setPreviousUnsafe(T* aElem)
{
LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(aElem);
MOZ_ASSERT(!listElem->isInList());
listElem->mNext = this;
listElem->mPrev = this->mPrev;
this->mPrev->mNext = listElem;
this->mPrev = listElem;
}
private:
LinkedListElement& operator=(const LinkedListElement<T>& aOther) = delete;
LinkedListElement(const LinkedListElement<T>& aOther) = delete;
};
template<typename T>
class LinkedList
{
private:
LinkedListElement<T> sentinel;
public:
class Iterator {
T* mCurrent;
public:
explicit Iterator(T* aCurrent) : mCurrent(aCurrent) {}
T* operator *() const {
return mCurrent;
}
const Iterator& operator++() {
mCurrent = mCurrent->getNext();
return *this;
}
bool operator!=(Iterator& aOther) const {
return mCurrent != aOther.mCurrent;
}
};
LinkedList() : sentinel(LinkedListElement<T>::NODE_KIND_SENTINEL) { }
LinkedList(LinkedList<T>&& aOther)
: sentinel(mozilla::Move(aOther.sentinel))
{ }
~LinkedList() {
MOZ_ASSERT(isEmpty(),
"failing this assertion means this LinkedList's creator is "
"buggy: it should have removed all this list's elements before "
"the list's destruction");
}
/*
* Add aElem to the front of the list.
*/
void insertFront(T* aElem)
{
/* Bypass setNext()'s this->isInList() assertion. */
sentinel.setNextUnsafe(aElem);
}
/*
* Add aElem to the back of the list.
*/
void insertBack(T* aElem)
{
sentinel.setPreviousUnsafe(aElem);
}
/*
* Get the first element of the list, or nullptr if the list is empty.
*/
T* getFirst() { return sentinel.getNext(); }
const T* getFirst() const { return sentinel.getNext(); }
/*
* Get the last element of the list, or nullptr if the list is empty.
*/
T* getLast() { return sentinel.getPrevious(); }
const T* getLast() const { return sentinel.getPrevious(); }
/*
* Get and remove the first element of the list. If the list is empty,
* return nullptr.
*/
T* popFirst()
{
T* ret = sentinel.getNext();
if (ret) {
static_cast<LinkedListElement<T>*>(ret)->remove();
}
return ret;
}
/*
* Get and remove the last element of the list. If the list is empty,
* return nullptr.
*/
T* popLast()
{
T* ret = sentinel.getPrevious();
if (ret) {
static_cast<LinkedListElement<T>*>(ret)->remove();
}
return ret;
}
/*
* Return true if the list is empty, or false otherwise.
*/
bool isEmpty() const
{
return !sentinel.isInList();
}
/*
* Remove all the elements from the list.
*
* This runs in time linear to the list's length, because we have to mark
* each element as not in the list.
*/
void clear()
{
while (popFirst()) {
continue;
}
}
/*
* Allow range-based iteration:
*
* for (MyElementType* elt : myList) { ... }
*/
Iterator begin() {
return Iterator(getFirst());
}
Iterator end() {
return Iterator(nullptr);
}
/*
* Measures the memory consumption of the list excluding |this|. Note that
* it only measures the list elements themselves. If the list elements
* contain pointers to other memory blocks, those blocks must be measured
* separately during a subsequent iteration over the list.
*/
size_t sizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
for (const T* t = getFirst(); t; t = t->getNext()) {
n += aMallocSizeOf(t);
}
return n;
}
/*
* Like sizeOfExcludingThis(), but measures |this| as well.
*/
size_t sizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf);
}
/*
* In a debug build, make sure that the list is sane (no cycles, consistent
* mNext/mPrev pointers, only one sentinel). Has no effect in release builds.
*/
void debugAssertIsSane() const
{
#ifdef DEBUG
const LinkedListElement<T>* slow;
const LinkedListElement<T>* fast1;
const LinkedListElement<T>* fast2;
/*
* Check for cycles in the forward singly-linked list using the
* tortoise/hare algorithm.
*/
for (slow = sentinel.mNext,
fast1 = sentinel.mNext->mNext,
fast2 = sentinel.mNext->mNext->mNext;
slow != &sentinel && fast1 != &sentinel && fast2 != &sentinel;
slow = slow->mNext, fast1 = fast2->mNext, fast2 = fast1->mNext) {
MOZ_ASSERT(slow != fast1);
MOZ_ASSERT(slow != fast2);
}
/* Check for cycles in the backward singly-linked list. */
for (slow = sentinel.mPrev,
fast1 = sentinel.mPrev->mPrev,
fast2 = sentinel.mPrev->mPrev->mPrev;
slow != &sentinel && fast1 != &sentinel && fast2 != &sentinel;
slow = slow->mPrev, fast1 = fast2->mPrev, fast2 = fast1->mPrev) {
MOZ_ASSERT(slow != fast1);
MOZ_ASSERT(slow != fast2);
}
/*
* Check that |sentinel| is the only node in the list with
* mIsSentinel == true.
*/
for (const LinkedListElement<T>* elem = sentinel.mNext;
elem != &sentinel;
elem = elem->mNext) {
MOZ_ASSERT(!elem->mIsSentinel);
}
/* Check that the mNext/mPrev pointers match up. */
const LinkedListElement<T>* prev = &sentinel;
const LinkedListElement<T>* cur = sentinel.mNext;
do {
MOZ_ASSERT(cur->mPrev == prev);
MOZ_ASSERT(prev->mNext == cur);
prev = cur;
cur = cur->mNext;
} while (cur != &sentinel);
#endif /* ifdef DEBUG */
}
private:
friend class LinkedListElement<T>;
void assertContains(const T* aValue) const
{
#ifdef DEBUG
for (const T* elem = getFirst(); elem; elem = elem->getNext()) {
if (elem == aValue) {
return;
}
}
MOZ_CRASH("element wasn't found in this list!");
#endif
}
LinkedList& operator=(const LinkedList<T>& aOther) = delete;
LinkedList(const LinkedList<T>& aOther) = delete;
};
template <typename T>
class AutoCleanLinkedList : public LinkedList<T>
{
public:
~AutoCleanLinkedList()
{
while (T* element = this->popFirst()) {
delete element;
}
}
};
} /* namespace mozilla */
#endif /* __cplusplus */
#endif /* mozilla_LinkedList_h */