import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1131308 (part 0) - Fix minor problems with RemovingIterator. r=froydnj. (5c232c9ca)
- Bug 1176163 - Remove remaining uses of PL_DHashTableEnumerate() from xpcom/. r=froydnj. (bd2478b3a)
- Bug 1179657 - Remove PL_DHASHMETER. r=froydnj. (30f581478)
- Bug 1131308 (part 1) - Improve PLDHashTable's internal checking. r=froydnj. (faba4a3b9)
- Bug 1180084 - Convert TestPLDHash.cpp to a gtest. r=froydnj. (de6ee3c5c)
- Bug 1171578 - Avoid crashing if indexedDB is used at a particular point in the worker shutdown sequence, r=baku. (f325c218e)
- Bug 804975 - Part 1: Expose WritingMode bits. r=dbaron (0635d01dd)
- Bug 1072101 - Part 1: Implement FontFaceSet.size. r=peterv (7e0b4848a)
- Bug 1144977 - Part 1: Don't include FontFace objects in more than one loadingdone/loadingerror event. r=jdaggett (4d10b3269)
- Bug 1072101 - Part 2: Implement FontFaceSet.{entries,values}. r=peterv (1c636f4f1)
- Bug 1072101 - Part 3: Implement FontFaceSet.forEach. r=peterv (842a5cdbe)
- Bug 1072101 - Part 4: Implement FontFaceSet.{keys,@@iterator}. r=peterv (4dfb3afe9)
- Bug 1072101 - Part 5: Remove indexed property access on FontFaceSet. r=peterv (edef47d70)
- Bug 1072101 - Unified build fix; no review. (7c1538f6c)
- Bug 861449 - Incremental css::Rule destroyer. r=dbaron (5e60f4394)
- Bug 1175800 - Remove typedefs from nsComputedDOMStyle.cpp as they interfere with others under unified compilation. r=bzbarsky (2bd45e7ed)
- Bug 1147766 - Part 1: Add a mozilla::RangedArray class, for fixed length arrays with a non-zero base index. r=waldo (42be428bc)
- Bug 1147766 - Part 2: Replace FixedStyleStructArray with mozilla::RangedArray. r=dbaron (4d28a0a5e)
- Bug 1171282 - Avoid some unnecessary |operator new| null-checks in layout/. r=dholbert. (60e1690c1)
- Bug 1168664 - Stop mistakenly using the transform reference box's width to calculate the Z component in ProcessMatrix3D. r=mattwoodrow (18f9021b1)
- Bug 1171842 - Use jump table instead of nested if statements for peeking compute function of style struct. r=dbaron (a5160b962)
- Bug 1170173 - Parse CSS 'contain' property. r=dholbert (3d8a37dee)
- Bug 1144607 part 1 - Remove CounterStyleManager::BuildCounterStyle for anonymous counter styles. r=dbaron (331397d01)
- Bug 1144607 part 2 - Support string value for list-style-tyle. r=dbaron (0b4bca0c7)
- Bug 1144607 part 3 - Reftests for string value on list-style-type. r=dbaron (2a483f621)
- Bug 1144607 followup - Fix submitted reftest for string value of list-style-type. DONTBUILD (ad23ac443)
- Bug 1151220 - [css-grid] Fix a couple of typos in InitializeTrackSize. r=dholbert (840f3b8a4)
- Bug 1151201 - [css-grid] Update parsing of 'auto' in track-sizing functions to latest version of the spec. r=simon.sapin (abdb0c0ea)
This commit is contained in:
2021-02-02 20:52:33 +08:00
parent 6fd9a39304
commit 3a02ab4d4d
58 changed files with 1126 additions and 777 deletions
+4
View File
@@ -488,6 +488,10 @@ DOMInterfaces = {
'wrapperCache': False,
},
'FontFaceSetIterator': {
'wrapperCache': False,
},
'FormData': {
'nativeType': 'nsFormData'
},
+5
View File
@@ -765,6 +765,11 @@ IDBFactory::OpenInternal(nsIPrincipal* aPrincipal,
JS::Rooted<JSObject*> scriptOwner(autoJS.cx(), mOwningObject);
request = IDBOpenDBRequest::CreateForJS(this, scriptOwner);
if (!request) {
MOZ_ASSERT(!NS_IsMainThread());
aRv.ThrowUncatchableException();
return nullptr;
}
}
MOZ_ASSERT(request);
+24 -2
View File
@@ -442,11 +442,19 @@ class IDBOpenDBRequest::WorkerFeature final
: public mozilla::dom::workers::WorkerFeature
{
WorkerPrivate* mWorkerPrivate;
#ifdef DEBUG
// This is only here so that assertions work in the destructor even if
// NoteAddFeatureFailed was called.
WorkerPrivate* mWorkerPrivateDEBUG;
#endif
public:
explicit
WorkerFeature(WorkerPrivate* aWorkerPrivate)
: mWorkerPrivate(aWorkerPrivate)
#ifdef DEBUG
, mWorkerPrivateDEBUG(aWorkerPrivate)
#endif
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
@@ -456,11 +464,24 @@ public:
~WorkerFeature()
{
mWorkerPrivate->AssertIsOnWorkerThread();
#ifdef DEBUG
mWorkerPrivateDEBUG->AssertIsOnWorkerThread();
#endif
MOZ_COUNT_DTOR(IDBOpenDBRequest::WorkerFeature);
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
if (mWorkerPrivate) {
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
}
}
void
NoteAddFeatureFailed()
{
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
mWorkerPrivate = nullptr;
}
private:
@@ -527,6 +548,7 @@ IDBOpenDBRequest::CreateForJS(IDBFactory* aFactory,
nsAutoPtr<WorkerFeature> feature(new WorkerFeature(workerPrivate));
if (NS_WARN_IF(!workerPrivate->AddFeature(cx, feature))) {
feature->NoteAddFeatureFailed();
return nullptr;
}
+20 -17
View File
@@ -10,6 +10,21 @@
* liability, trademark and document use rules apply.
*/
// To implement FontFaceSet's iterator until we can use setlike.
dictionary FontFaceSetIteratorResult
{
required any value;
required boolean done;
};
// To implement FontFaceSet's iterator until we can use setlike.
[NoInterfaceObject]
interface FontFaceSetIterator {
[Throws] FontFaceSetIteratorResult next();
};
callback FontFaceSetForEachCallback = void (FontFace value, FontFace key, FontFaceSet set);
enum FontFaceSetLoadStatus { "loading", "loaded" };
// Bug 1072762 is for the FontFaceSet constructor.
@@ -17,19 +32,16 @@ enum FontFaceSetLoadStatus { "loading", "loaded" };
[Pref="layout.css.font-loading-api.enabled"]
interface FontFaceSet : EventTarget {
// Emulate the Set interface, until we can extend Set correctly.
// Implementing these commented out operations and the iterator is
// bug 1072101.
// readonly attribute unsigned long size;
// Emulate setlike behavior until we can use that directly.
readonly attribute unsigned long size;
[Throws] void add(FontFace font);
boolean has(FontFace font);
[Throws] boolean delete(FontFace font);
void clear();
// Iterator entries();
[NewObject] FontFaceSetIterator entries();
// Iterator keys();
// Iterator values();
// void forEach(ForEachCallback cb, optional any thisArg);
// FontFace iterator;
[NewObject, Alias=keys, Alias="@@iterator"] FontFaceSetIterator values();
[Throws] void forEach(FontFaceSetForEachCallback cb, optional any thisArg);
// -- events for when loading state changes
attribute EventHandler onloading;
@@ -52,12 +64,3 @@ interface FontFaceSet : EventTarget {
// loading state, "loading" while one or more fonts loading, "loaded" otherwise
readonly attribute FontFaceSetLoadStatus status;
};
// This provides access to the FontFace objects in the FontFaceSet until we
// get iterators working (bug 1072101). Don't enable the pref for the CSS Font
// Loading API until the iterator is available, as we don't want to expose more
// indexed properties on the Web.
partial interface FontFaceSet {
getter FontFace (unsigned long index);
readonly attribute unsigned long length;
};
+6 -4
View File
@@ -5829,11 +5829,13 @@ WorkerPrivate::AddFeature(JSContext* aCx, WorkerFeature* aFeature)
}
MOZ_ASSERT(!mFeatures.Contains(aFeature), "Already know about this one!");
mFeatures.AppendElement(aFeature);
return mFeatures.Length() == 1 ?
ModifyBusyCountFromWorker(aCx, true) :
true;
if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(aCx, true)) {
return false;
}
mFeatures.AppendElement(aFeature);
return true;
}
void
+1 -1
View File
@@ -55,7 +55,7 @@ nsCounterUseNode::GetCounterStyle()
style.GetStringValue(ident);
mCounterStyle = manager->BuildCounterStyle(ident);
} else if (style.GetUnit() == eCSSUnit_Symbols) {
mCounterStyle = manager->BuildCounterStyle(style.GetArrayValue());
mCounterStyle = new AnonymousCounterStyle(style.GetArrayValue());
} else {
NS_NOTREACHED("Unknown counter style");
mCounterStyle = CounterStyleManager::GetDecimalStyle();
+2
View File
@@ -468,6 +468,8 @@ public:
return IsVertical() != aOther.IsVertical();
}
uint8_t GetBits() const { return mWritingMode; }
private:
friend class LogicalPoint;
friend class LogicalSize;
+1 -1
View File
@@ -73,7 +73,7 @@ public:
}
/**
* Allocate a nsFrameList from the shell arena.
* Infallibly allocate a nsFrameList from the shell arena.
*/
void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW;
+4 -2
View File
@@ -950,7 +950,8 @@ InitializeTrackSize(nscoord aPercentageBasis,
{
// http://dev.w3.org/csswg/css-grid/#algo-init
nscoord& base = aTrackSize->mBase;
switch (aMaxCoord.GetUnit()) {
switch (aMinCoord.GetUnit()) {
case eStyleUnit_Auto:
case eStyleUnit_Enumerated:
case eStyleUnit_FlexFraction:
base = 0;
@@ -960,6 +961,7 @@ InitializeTrackSize(nscoord aPercentageBasis,
}
nscoord& limit = aTrackSize->mLimit;
switch (aMaxCoord.GetUnit()) {
case eStyleUnit_Auto:
case eStyleUnit_Enumerated:
limit = NS_UNCONSTRAINEDSIZE;
break;
@@ -988,7 +990,7 @@ InitializeTrackSizes(nscoord aPercentageBasis,
size_t i = 0;
for (; i < len; ++i) {
InitializeTrackSize(aPercentageBasis,
aMinSizingFunctions[i], aMinSizingFunctions[i],
aMinSizingFunctions[i], aMaxSizingFunctions[i],
&aResults[i]);
}
for (; i < aResults.Length(); ++i) {
-2
View File
@@ -145,8 +145,6 @@ nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
}
}
// Overloaded new operator. Uses an arena (which comes from the presShell)
// to perform the allocation.
void*
nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
{
+2 -2
View File
@@ -206,8 +206,8 @@ private:
nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock);
~nsLineBox();
// Overloaded new operator. Uses an arena (which comes from the presShell)
// to perform the allocation.
// Infallible overloaded new operator. Uses an arena (which comes from the
// presShell) to perform the allocation.
void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW;
void operator delete(void* aPtr, size_t sz) = delete;
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Reference: String value of list-style-type</title>
<link rel="author" title="Xidorn Quan" href="mailto:quanxunzhen@gmail.com">
<style>
ol, ul { list-style: none; }
li::before {
content: "# ";
font-variant-numeric: tabular-nums;
}
</style>
</head>
<body>
<ol start="-1">
<li>item -1</li>
<li>item 0</li>
<li>item 1</li>
<li>item 2</li>
</ol>
<ul start="-1">
<li>item -1</li>
<li>item 0</li>
<li>item 1</li>
<li>item 2</li>
</ul>
</body>
</html>
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Test: String value of list-style-type</title>
<link rel="author" title="Xidorn Quan" href="mailto:quanxunzhen@gmail.com">
<link rel="help" href="http://dev.w3.org/csswg/css-lists-3/#valdef-list-style-type-string">
<link rel="match" href="list-style-type-string-001-ref.html">
<style>
ol, ul { list-style-type: "# "; list-style-position: inside; }
</style>
</head>
<body>
<ol start="-1">
<li>item -1</li>
<li>item 0</li>
<li>item 1</li>
<li>item 2</li>
</ol>
<ul start="-1">
<li>item -1</li>
<li>item 0</li>
<li>item 1</li>
<li>item 2</li>
</ul>
</body>
</html>
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSS Test: String value of list-style</title>
<link rel="author" title="Xidorn Quan" href="mailto:quanxunzhen@gmail.com">
<link rel="help" href="http://dev.w3.org/csswg/css-lists-3/#valdef-list-style-type-string">
<link rel="match" href="list-style-type-string-001-ref.html">
<style>
ol, ul { list-style: inside "# "; }
</style>
</head>
<body>
<ol start="-1">
<li>item -1</li>
<li>item 0</li>
<li>item 1</li>
<li>item 2</li>
</ol>
<ul start="-1">
<li>item -1</li>
<li>item 0</li>
<li>item 1</li>
<li>item 2</li>
</ul>
</body>
</html>
@@ -0,0 +1,3 @@
# Tests for list-style-type
== list-style-type-string-001a.html list-style-type-string-001-ref.html
== list-style-type-string-001b.html list-style-type-string-001-ref.html
@@ -28,6 +28,9 @@ include fonts3/reftest.list
# Image Values and Replaced Content Level 3
include images3/reftest.list
# Lists and Counters Level 3
include lists-3/reftest.list
# Media Queries Level 3
# include mediaqueries3/reftest.list
+2 -2
View File
@@ -10,6 +10,7 @@
#define mozilla_CSSStyleSheet_h
#include "mozilla/Attributes.h"
#include "mozilla/IncrementalClearCOMRuleArray.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/Element.h"
@@ -19,7 +20,6 @@
#include "nsIStyleSheet.h"
#include "nsIDOMCSSStyleSheet.h"
#include "nsICSSLoaderObserver.h"
#include "nsCOMArray.h"
#include "nsTArrayForwardDeclare.h"
#include "nsString.h"
#include "mozilla/CORSMode.h"
@@ -84,7 +84,7 @@ private:
nsCOMPtr<nsIURI> mOriginalSheetURI; // for GetHref. Can be null.
nsCOMPtr<nsIURI> mBaseURI; // for resolving relative URIs
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMArray<css::Rule> mOrderedRules;
IncrementalClearCOMRuleArray mOrderedRules;
nsAutoPtr<nsXMLNameSpaceMap> mNameSpaceMap;
// Linked list of child sheets. This is al fundamentally broken, because
// each of the child sheets has a unique parent... We can only hope (and
+16 -8
View File
@@ -1696,10 +1696,20 @@ CustomCounterStyle::GetExtendsRoot()
return mExtendsRoot;
}
AnonymousCounterStyle::AnonymousCounterStyle(const nsSubstring& aContent)
: CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM)
, mSingleString(true)
, mSystem(NS_STYLE_COUNTER_SYSTEM_CYCLIC)
{
mSymbols.SetCapacity(1);
mSymbols.AppendElement(aContent);
}
AnonymousCounterStyle::AnonymousCounterStyle(const nsCSSValue::Array* aParams)
: CounterStyle(NS_STYLE_LIST_STYLE_CUSTOM)
, mSingleString(false)
, mSystem(aParams->Item(0).GetIntValue())
{
mSystem = aParams->Item(0).GetIntValue();
for (const nsCSSValueList* item = aParams->Item(1).GetListValue();
item; item = item->mNext) {
item->mValue.GetStringValue(*mSymbols.AppendElement());
@@ -1716,7 +1726,11 @@ AnonymousCounterStyle::GetPrefix(nsAString& aResult)
/* virtual */ void
AnonymousCounterStyle::GetSuffix(nsAString& aResult)
{
aResult = ' ';
if (IsSingleString()) {
aResult.Truncate();
} else {
aResult = ' ';
}
}
/* virtual */ bool
@@ -2028,12 +2042,6 @@ CounterStyleManager::BuildCounterStyle(const nsSubstring& aName)
return data;
}
CounterStyle*
CounterStyleManager::BuildCounterStyle(const nsCSSValue::Array* aParams)
{
return new AnonymousCounterStyle(aParams);
}
/* static */ CounterStyle*
CounterStyleManager::GetBuiltinStyle(int32_t aStyle)
{
+3 -1
View File
@@ -105,6 +105,7 @@ protected:
class AnonymousCounterStyle final : public CounterStyle
{
public:
explicit AnonymousCounterStyle(const nsSubstring& aContent);
explicit AnonymousCounterStyle(const nsCSSValue::Array* aValue);
virtual void GetPrefix(nsAString& aResult) override;
@@ -126,6 +127,7 @@ public:
virtual AnonymousCounterStyle* AsAnonymous() override { return this; }
bool IsSingleString() const { return mSingleString; }
uint8_t GetSystem() const { return mSystem; }
const nsTArray<nsString>& GetSymbols() const { return mSymbols; }
@@ -134,6 +136,7 @@ public:
private:
~AnonymousCounterStyle() {}
bool mSingleString;
uint8_t mSystem;
nsTArray<nsString> mSymbols;
};
@@ -156,7 +159,6 @@ public:
}
CounterStyle* BuildCounterStyle(const nsSubstring& aName);
CounterStyle* BuildCounterStyle(const nsCSSValue::Array* aParams);
static CounterStyle* GetBuiltinStyle(int32_t aStyle);
static CounterStyle* GetNoneStyle()
+77 -17
View File
@@ -12,6 +12,7 @@
#include "mozilla/dom/CSSFontFaceLoadEvent.h"
#include "mozilla/dom/CSSFontFaceLoadEventBinding.h"
#include "mozilla/dom/FontFaceSetBinding.h"
#include "mozilla/dom/FontFaceSetIterator.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Preferences.h"
@@ -50,7 +51,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHel
for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleFaces[i].mFontFace);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces);
for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonRuleFaces[i].mFontFace);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelper)
@@ -60,7 +63,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FontFaceSet, DOMEventTargetHelpe
for (size_t i = 0; i < tmp->mRuleFaces.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRuleFaces[i].mFontFace);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces);
for (size_t i = 0; i < tmp->mNonRuleFaces.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonRuleFaces[i].mFontFace);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_ADDREF_INHERITED(FontFaceSet, DOMEventTargetHelper)
@@ -234,10 +239,19 @@ FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv)
aFontFace.SetIsInFontFaceSet(true);
MOZ_ASSERT(!mNonRuleFaces.Contains(&aFontFace),
"FontFace should not occur in mNonRuleFaces twice");
#ifdef DEBUG
for (const FontFaceRecord& rec : mNonRuleFaces) {
MOZ_ASSERT(rec.mFontFace != &aFontFace,
"FontFace should not occur in mNonRuleFaces twice");
}
#endif
mNonRuleFaces.AppendElement(&aFontFace);
FontFaceRecord* rec = mNonRuleFaces.AppendElement();
rec->mFontFace = &aFontFace;
rec->mSheetType = 0; // unused for mNonRuleFaces
rec->mLoadEventShouldFire =
aFontFace.Status() == FontFaceLoadStatus::Unloaded ||
aFontFace.Status() == FontFaceLoadStatus::Loading;
mNonRuleFacesDirty = true;
mPresContext->RebuildUserFontSet();
@@ -256,7 +270,7 @@ FontFaceSet::Clear()
}
for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
FontFace* f = mNonRuleFaces[i];
FontFace* f = mNonRuleFaces[i].mFontFace;
f->SetIsInFontFaceSet(false);
MOZ_ASSERT(!mUnavailableFaces.Contains(f),
@@ -282,7 +296,15 @@ FontFaceSet::Delete(FontFace& aFontFace, ErrorResult& aRv)
return false;
}
if (!mNonRuleFaces.RemoveElement(&aFontFace)) {
bool removed = false;
for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
if (mNonRuleFaces[i].mFontFace == &aFontFace) {
mNonRuleFaces.RemoveElementAt(i);
removed = true;
break;
}
}
if (!removed) {
return false;
}
@@ -316,27 +338,24 @@ FontFaceSet::Has(FontFace& aFontFace)
}
FontFace*
FontFaceSet::IndexedGetter(uint32_t aIndex, bool& aFound)
FontFaceSet::GetFontFaceAt(uint32_t aIndex)
{
mPresContext->FlushUserFontSet();
if (aIndex < mRuleFaces.Length()) {
aFound = true;
return mRuleFaces[aIndex].mFontFace;
}
aIndex -= mRuleFaces.Length();
if (aIndex < mNonRuleFaces.Length()) {
aFound = true;
return mNonRuleFaces[aIndex];
return mNonRuleFaces[aIndex].mFontFace;
}
aFound = false;
return nullptr;
}
uint32_t
FontFaceSet::Length()
FontFaceSet::Size()
{
mPresContext->FlushUserFontSet();
@@ -346,6 +365,34 @@ FontFaceSet::Length()
return std::min<size_t>(total, INT32_MAX);
}
FontFaceSetIterator*
FontFaceSet::Entries()
{
return new FontFaceSetIterator(this, true);
}
FontFaceSetIterator*
FontFaceSet::Values()
{
return new FontFaceSetIterator(this, false);
}
void
FontFaceSet::ForEach(JSContext* aCx,
FontFaceSetForEachCallback& aCallback,
JS::Handle<JS::Value> aThisArg,
ErrorResult& aRv)
{
JS::Rooted<JS::Value> thisArg(aCx, aThisArg);
for (size_t i = 0; i < Size(); i++) {
FontFace* face = GetFontFaceAt(i);
aCallback.Call(thisArg, *face, *face, *this, aRv);
if (aRv.Failed()) {
return;
}
}
}
static PLDHashOperator DestroyIterator(nsPtrHashKey<nsFontFaceLoader>* aKey,
void* aUserArg)
{
@@ -365,7 +412,7 @@ FontFaceSet::DestroyUserFontSet()
mRuleFaces[i].mFontFace->SetUserFontEntry(nullptr);
}
for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
mNonRuleFaces[i]->SetUserFontEntry(nullptr);
mNonRuleFaces[i].mFontFace->SetUserFontEntry(nullptr);
}
for (size_t i = 0; i < mUnavailableFaces.Length(); i++) {
mUnavailableFaces[i]->SetUserFontEntry(nullptr);
@@ -565,7 +612,7 @@ FontFaceSet::UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules)
for (size_t i = 0, i_end = mNonRuleFaces.Length(); i < i_end; ++i) {
// Do the same for the non rule backed FontFace objects.
InsertNonRuleFontFace(mNonRuleFaces[i], modified);
InsertNonRuleFontFace(mNonRuleFaces[i].mFontFace, modified);
}
// Remove any residual families that have no font entries (i.e., they were
@@ -761,6 +808,9 @@ FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, uint8_t aSheetType,
FontFaceRecord rec;
rec.mFontFace = aFontFace;
rec.mSheetType = aSheetType;
rec.mLoadEventShouldFire =
aFontFace->Status() == FontFaceLoadStatus::Unloaded ||
aFontFace->Status() == FontFaceLoadStatus::Loading;
aFontFace->SetUserFontEntry(entry);
@@ -1376,7 +1426,7 @@ FontFaceSet::UpdateHasLoadingFontFaces()
}
}
for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
if (mNonRuleFaces[i]->Status() == FontFaceLoadStatus::Loading) {
if (mNonRuleFaces[i].mFontFace->Status() == FontFaceLoadStatus::Loading) {
mHasLoadingFontFaces = true;
return;
}
@@ -1448,20 +1498,30 @@ FontFaceSet::CheckLoadingFinished()
nsTArray<FontFace*> failed;
for (size_t i = 0; i < mRuleFaces.Length(); i++) {
if (!mRuleFaces[i].mLoadEventShouldFire) {
continue;
}
FontFace* f = mRuleFaces[i].mFontFace;
if (f->Status() == FontFaceLoadStatus::Loaded) {
loaded.AppendElement(f);
mRuleFaces[i].mLoadEventShouldFire = false;
} else if (f->Status() == FontFaceLoadStatus::Error) {
failed.AppendElement(f);
mRuleFaces[i].mLoadEventShouldFire = false;
}
}
for (size_t i = 0; i < mNonRuleFaces.Length(); i++) {
FontFace* f = mNonRuleFaces[i];
if (!mNonRuleFaces[i].mLoadEventShouldFire) {
continue;
}
FontFace* f = mNonRuleFaces[i].mFontFace;
if (f->Status() == FontFaceLoadStatus::Loaded) {
loaded.AppendElement(f);
mNonRuleFaces[i].mLoadEventShouldFire = false;
} else if (f->Status() == FontFaceLoadStatus::Error) {
failed.AppendElement(f);
mNonRuleFaces[i].mLoadEventShouldFire = false;
}
}
+16 -5
View File
@@ -167,6 +167,8 @@ public:
bool aWasAlternate,
nsresult aStatus) override;
FontFace* GetFontFaceAt(uint32_t aIndex);
// -- Web IDL --------------------------------------------------------------
IMPL_EVENT_HANDLER(loading)
@@ -185,8 +187,12 @@ public:
void Clear();
bool Delete(FontFace& aFontFace, mozilla::ErrorResult& aRv);
bool Has(FontFace& aFontFace);
FontFace* IndexedGetter(uint32_t aIndex, bool& aFound);
uint32_t Length();
uint32_t Size();
mozilla::dom::FontFaceSetIterator* Entries();
mozilla::dom::FontFaceSetIterator* Values();
void ForEach(JSContext* aCx, FontFaceSetForEachCallback& aCallback,
JS::Handle<JS::Value> aThisArg,
mozilla::ErrorResult& aRv);
private:
~FontFaceSet();
@@ -233,7 +239,12 @@ private:
// accordingly.
struct FontFaceRecord {
nsRefPtr<FontFace> mFontFace;
uint8_t mSheetType;
uint8_t mSheetType; // only relevant for mRuleFaces entries
// When true, indicates that when finished loading, the FontFace should be
// included in the subsequent loadingdone/loadingerror event fired at the
// FontFaceSet.
bool mLoadEventShouldFire;
};
static already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntryFromFontFace(
@@ -300,8 +311,8 @@ private:
nsTArray<FontFaceRecord> mRuleFaces;
// The non rule backed FontFace objects that have been added to this
// FontFaceSet and their corresponding user font entries.
nsTArray<nsRefPtr<FontFace>> mNonRuleFaces;
// FontFaceSet.
nsTArray<FontFaceRecord> mNonRuleFaces;
// The non rule backed FontFace objects that have not been added to
// this FontFaceSet.
+75
View File
@@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "mozilla/dom/FontFaceSetIterator.h"
namespace mozilla {
namespace dom {
FontFaceSetIterator::FontFaceSetIterator(FontFaceSet* aFontFaceSet,
bool aIsKeyAndValue)
: mFontFaceSet(aFontFaceSet)
, mNextIndex(0)
, mIsKeyAndValue(aIsKeyAndValue)
{
MOZ_COUNT_CTOR(FontFaceSetIterator);
}
FontFaceSetIterator::~FontFaceSetIterator()
{
MOZ_COUNT_DTOR(FontFaceSetIterator);
}
bool
FontFaceSetIterator::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector)
{
return FontFaceSetIteratorBinding::Wrap(aCx, this, aGivenProto, aReflector);
}
void
FontFaceSetIterator::Next(JSContext* aCx, FontFaceSetIteratorResult& aResult,
ErrorResult& aRv)
{
if (!mFontFaceSet) {
aResult.mDone = true;
return;
}
FontFace* face = mFontFaceSet->GetFontFaceAt(mNextIndex++);
if (!face) {
aResult.mValue.setUndefined();
aResult.mDone = true;
mFontFaceSet = nullptr;
return;
}
JS::Rooted<JS::Value> value(aCx);
if (!ToJSValue(aCx, face, &value)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (mIsKeyAndValue) {
JS::AutoValueArray<2> values(aCx);
values[0].set(value);
values[1].set(value);
JS::Rooted<JSObject*> array(aCx);
array = JS_NewArrayObject(aCx, values);
if (array) {
aResult.mValue.setObject(*array);
}
} else {
aResult.mValue = value;
}
aResult.mDone = false;
}
} // namespace dom
} // namespace mozilla
+39
View File
@@ -0,0 +1,39 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef mozilla_dom_FontFaceSetIterator_h
#define mozilla_dom_FontFaceSetIterator_h
#include "mozilla/dom/FontFaceSetBinding.h"
#include "mozilla/dom/NonRefcountedDOMObject.h"
namespace mozilla {
namespace dom {
class FontFaceSetIterator final : public NonRefcountedDOMObject
{
public:
FontFaceSetIterator(mozilla::dom::FontFaceSet* aFontFaceSet,
bool aIsKeyAndValue);
~FontFaceSetIterator();
bool WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
// WebIDL
void Next(JSContext* aCx, FontFaceSetIteratorResult& aResult,
mozilla::ErrorResult& aRv);
private:
nsRefPtr<FontFaceSet> mFontFaceSet;
uint32_t mNextIndex;
bool mIsKeyAndValue;
};
} // namespace dom
} // namespace mozilla
#endif // !defined(mozilla_dom_FontFaceSetIterator_h)
+4 -4
View File
@@ -12,9 +12,9 @@
#define mozilla_css_GroupRule_h__
#include "mozilla/Attributes.h"
#include "mozilla/IncrementalClearCOMRuleArray.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/Rule.h"
#include "nsCOMArray.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
@@ -57,7 +57,7 @@ public:
int32_t StyleRuleCount() const { return mRules.Count(); }
Rule* GetStyleRuleAt(int32_t aIndex) const;
typedef nsCOMArray<Rule>::nsCOMArrayEnumFunc RuleEnumFunc;
typedef IncrementalClearCOMRuleArray::nsCOMArrayEnumFunc RuleEnumFunc;
bool EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const;
/*
@@ -80,7 +80,7 @@ public:
CloneRuleInto(Rule* aRule, void* aArray)
{
nsRefPtr<Rule> clone = aRule->Clone();
static_cast<nsCOMArray<Rule>*>(aArray)->AppendObject(clone);
static_cast<IncrementalClearCOMRuleArray*>(aArray)->AppendObject(clone);
return true;
}
@@ -95,7 +95,7 @@ protected:
uint32_t* _retval);
nsresult DeleteRule(uint32_t aIndex);
nsCOMArray<Rule> mRules;
IncrementalClearCOMRuleArray mRules;
nsRefPtr<GroupRuleRuleList> mRuleCollection; // lazily constructed
};
@@ -0,0 +1,80 @@
/* -*- 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/. */
#include "mozilla/IncrementalClearCOMRuleArray.h"
#include "nsCycleCollector.h"
#include "mozilla/DeferredFinalize.h"
#include "nsTArray.h"
#include "nsCCUncollectableMarker.h"
using namespace mozilla;
typedef nsCOMArray<css::Rule> RuleArray;
typedef nsTArray<RuleArray> RuleArrayArray;
// These methods are based on those in DeferredFinalizerImpl.
static void*
AppendRulesArrayPointer(void* aData, void* aObject)
{
RuleArrayArray* rulesArray = static_cast<RuleArrayArray*>(aData);
RuleArray* oldRules = static_cast<RuleArray*>(aObject);
if (!rulesArray) {
rulesArray = new RuleArrayArray();
}
RuleArray* newRules = rulesArray->AppendElement();
newRules->SwapElements(*oldRules);
return rulesArray;
}
// Remove up to |aSliceBudget| css::Rules from the arrays, starting at
// the end of the last array.
static bool
DeferredFinalizeRulesArray(uint32_t aSliceBudget, void* aData)
{
MOZ_ASSERT(aSliceBudget > 0, "nonsensical/useless call with aSliceBudget == 0");
RuleArrayArray* rulesArray = static_cast<RuleArrayArray*>(aData);
size_t newOuterLen = rulesArray->Length();
while (aSliceBudget > 0 && newOuterLen > 0) {
RuleArray& lastArray = rulesArray->ElementAt(newOuterLen - 1);
uint32_t innerLen = lastArray.Length();
uint32_t currSlice = std::min(innerLen, aSliceBudget);
uint32_t newInnerLen = innerLen - currSlice;
lastArray.TruncateLength(newInnerLen);
aSliceBudget -= currSlice;
if (newInnerLen == 0) {
newOuterLen -= 1;
}
}
rulesArray->TruncateLength(newOuterLen);
if (newOuterLen == 0) {
delete rulesArray;
return true;
}
return false;
}
void
IncrementalClearCOMRuleArray::Clear()
{
// Destroy the array incrementally if it is long and we
// haven't started shutting down.
if (Length() > 10 && nsCCUncollectableMarker::sGeneration) {
DeferredFinalize(AppendRulesArrayPointer, DeferredFinalizeRulesArray, this);
} else {
nsCOMArray<css::Rule>::Clear();
}
MOZ_ASSERT(Length() == 0);
}
@@ -0,0 +1,28 @@
/* -*- 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/. */
#ifndef mozilla_IncrementalClearCOMRuleArray_h
#define mozilla_IncrementalClearCOMRuleArray_h
#include "nsCOMArray.h"
namespace mozilla {
namespace css {
class Rule;
} // namespace css
class IncrementalClearCOMRuleArray : public nsCOMArray<css::Rule>
{
public:
IncrementalClearCOMRuleArray() {}
~IncrementalClearCOMRuleArray() { Clear(); }
void Clear();
};
} // namespace mozilla
#endif /* !defined(mozilla_IncrementalClearCOMRuleArray_h) */
+3 -69
View File
@@ -9,33 +9,7 @@
#
# 1. To generate code for each inherited style struct.
# 2. To generate code for each reset style struct.
# 3. To generate a tree of nested if statements that can be used to run
# some code on each style struct.
#
# As an example, if we assume that we have only four style structs, the
# generated tree of nested if statements looks like this:
#
# if (STYLE_STRUCT_TEST < 4) {
# if (STYLE_STRUCT_TEST < 2) {
# if (STYLE_STRUCT_TEST == 0) {
# ... code for style struct with id 0 ...
# } else {
# ... code for style struct with id 1 ...
# }
# } else {
# if (STYLE_STRUCT_TEST == 2) {
# ... code for style struct with id 2 ...
# } else {
# ... code for style struct with id 3 ...
# }
# }
# }
#
# The TOPLEVELBRANCHES variable controls how widely we branch on the outermost
# if statement. In the example above, it splits the search space in 2, but with
# a larger number of style structs to test -- particularly when the number is
# closer to one power of two than the next higher one -- the average number of
# comparisons can be reduced by splitting the top level check into more than 2.
# 3. To generate the dependency of each style struct.
from __future__ import print_function
@@ -77,9 +51,6 @@ STYLE_STRUCTS = [("INHERITED",) + x for x in [
("Column", "nullptr", NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
]]
# How widely to branch on the outermost if statement.
TOPLEVELBRANCHES = 4
# ---- Generate nsStyleStructList.h ----
@@ -116,42 +87,12 @@ for i in range(count):
"the following structs:", " ".join(unsolved_items), file=sys.stderr)
exit(1)
def nextPowerOf2(x):
return int(pow(2, math.ceil(math.log(x, 2))))
def printEntry(header, i):
print("STYLE_STRUCT_%s(%s, %s)" % STYLE_STRUCTS[i][:3], file=header)
for dep in STYLE_STRUCTS[i][3]:
print("STYLE_STRUCT_DEP(%s)" % (dep,), file=header)
print("STYLE_STRUCT_END()", file=header)
def printTestTree(header, min, max, depth, branches):
indent = " " * depth
if min == count - 1 and max >= count:
print(" STYLE_STRUCT_TEST_CODE(%sNS_ASSERTION(STYLE_STRUCT_TEST == %d, \"out of range\");)" % (indent, min), file=header)
printEntry(header, min)
elif max - min == 2:
print(" STYLE_STRUCT_TEST_CODE(%sif (STYLE_STRUCT_TEST == %d) {)" % (indent, min), file=header)
printEntry(header, min)
print(" STYLE_STRUCT_TEST_CODE(%s} else {)" % indent, file=header)
printEntry(header, min + 1)
print(" STYLE_STRUCT_TEST_CODE(%s})" % indent, file=header)
elif min < count:
mid = min + (max - min) / branches
print(" STYLE_STRUCT_TEST_CODE(%sif (STYLE_STRUCT_TEST < %d) {)" % (indent, mid), file=header)
printTestTree(header, min, mid, depth + 1, 2)
for branch in range(1, branches):
lo = min + branch * (max - min) / branches
hi = min + (branch + 1) * (max - min) / branches
if lo >= count:
break
if branch == branches - 1 or hi >= count:
print(" STYLE_STRUCT_TEST_CODE(%s} else {)" % indent, file=header)
else:
print(" STYLE_STRUCT_TEST_CODE(%s} else if (STYLE_STRUCT_TEST < %d) {)" % (indent, hi), file=header)
printTestTree(header, lo, hi, depth + 1, 2)
print(" STYLE_STRUCT_TEST_CODE(%s})" % indent, file=header)
HEADER = """/* THIS FILE IS AUTOGENERATED BY generate-stylestructlist.py - DO NOT EDIT */
// IWYU pragma: private, include "nsStyleStructFwd.h"
@@ -189,12 +130,6 @@ HEADER = """/* THIS FILE IS AUTOGENERATED BY generate-stylestructlist.py - DO NO
#define UNDEF_STYLE_STRUCT_END
#endif
#ifdef STYLE_STRUCT_TEST
#define STYLE_STRUCT_TEST_CODE(c) c
#else
#define STYLE_STRUCT_TEST_CODE(c)
#endif
// The inherited structs are listed before the Reset structs.
// nsStyleStructID assumes this is the case, and callers other than
// nsStyleStructFwd.h that want the structs in id-order just define
@@ -221,11 +156,10 @@ FOOTER = """
#undef STYLE_STRUCT_END
#undef UNDEF_STYLE_STRUCT_END
#endif
#undef STYLE_STRUCT_TEST_CODE
"""
def main(header):
print(HEADER, file=header)
printTestTree(header, 0, nextPowerOf2(count), 0, TOPLEVELBRANCHES)
for i in range(count):
printEntry(header, i)
print(FOOTER, file=header)
+4
View File
@@ -84,6 +84,7 @@ EXPORTS.mozilla += [
'CSSVariableDeclarations.h',
'CSSVariableResolver.h',
'CSSVariableValues.h',
'IncrementalClearCOMRuleArray.h',
'StyleAnimationValue.h',
]
@@ -93,6 +94,7 @@ EXPORTS.mozilla.dom += [
'CSSValue.h',
'FontFace.h',
'FontFaceSet.h',
'FontFaceSetIterator.h',
'MediaQueryList.h',
]
@@ -120,7 +122,9 @@ UNIFIED_SOURCES += [
'Declaration.cpp',
'ErrorReporter.cpp',
'FontFace.cpp',
'FontFaceSetIterator.cpp',
'ImageLoader.cpp',
'IncrementalClearCOMRuleArray.cpp',
'Loader.cpp',
'MediaQueryList.cpp',
'nsAnimationManager.cpp',
+3
View File
@@ -332,6 +332,7 @@ CSS_KEY(landscape, landscape)
CSS_KEY(large, large)
CSS_KEY(larger, larger)
CSS_KEY(last-baseline, last_baseline)
CSS_KEY(layout, layout)
CSS_KEY(left, left)
CSS_KEY(legacy, legacy)
CSS_KEY(lighten, lighten)
@@ -402,6 +403,7 @@ CSS_KEY(outside, outside)
CSS_KEY(over, over)
CSS_KEY(overlay, overlay)
CSS_KEY(overline, overline)
CSS_KEY(paint, paint)
CSS_KEY(padding-box, padding_box)
CSS_KEY(painted, painted)
CSS_KEY(pan-x, pan_x)
@@ -516,6 +518,7 @@ CSS_KEY(sticky, sticky)
CSS_KEY(stretch, stretch)
CSS_KEY(stretch-to-fit, stretch_to_fit)
CSS_KEY(stretched, stretched)
CSS_KEY(strict, strict)
CSS_KEY(stroke, stroke)
CSS_KEY(stroke-box, stroke_box)
CSS_KEY(style, style)
+34 -9
View File
@@ -891,6 +891,7 @@ protected:
// for 'clip' and '-moz-image-region'
bool ParseRect(nsCSSProperty aPropID);
bool ParseColumns();
bool ParseContain(nsCSSValue& aValue);
bool ParseContent();
bool ParseCounterData(nsCSSProperty aPropID);
bool ParseCursor();
@@ -8096,17 +8097,17 @@ CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue)
}
}
// Parse a <track-breadth>
// Parse a <track-breadth>.
bool
CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
{
if (ParseNonNegativeVariant(aValue,
VARIANT_LPCALC | VARIANT_KEYWORD,
VARIANT_AUTO | VARIANT_LPCALC | VARIANT_KEYWORD,
nsCSSProps::kGridTrackBreadthKTable)) {
return true;
}
// Attempt to parse <flex> (a dimension with the "fr" unit)
// Attempt to parse <flex> (a dimension with the "fr" unit).
if (!GetToken(true)) {
return false;
}
@@ -8120,17 +8121,16 @@ CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue)
return true;
}
// Parse a <track-size>
// Parse a <track-size>.
CSSParseResult
CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue)
{
// Attempt to parse 'auto' or a single <track-breadth>
if (ParseGridTrackBreadth(aValue) ||
ParseVariant(aValue, VARIANT_AUTO, nullptr)) {
// Attempt to parse a single <track-breadth>.
if (ParseGridTrackBreadth(aValue)) {
return CSSParseResult::Ok;
}
// Attempt to parse a minmax() function
// Attempt to parse a minmax() function.
if (!GetToken(true)) {
return CSSParseResult::NotFound;
}
@@ -10312,6 +10312,8 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
return ParseTextOverflow(aValue);
case eCSSProperty_touch_action:
return ParseTouchAction(aValue);
case eCSSProperty_contain:
return ParseContain(aValue);
default:
MOZ_ASSERT(false, "should not reach here");
return false;
@@ -12415,6 +12417,29 @@ CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue)
maskEastAsian);
}
bool
CSSParserImpl::ParseContain(nsCSSValue& aValue)
{
if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, nullptr)) {
return true;
}
static const int32_t maskContain[] = { MASK_END_VALUE };
if (!ParseBitmaskValues(aValue, nsCSSProps::kContainKTable, maskContain)) {
return false;
}
if (aValue.GetIntValue() & NS_STYLE_CONTAIN_STRICT) {
if (aValue.GetIntValue() != NS_STYLE_CONTAIN_STRICT) {
// Disallow any other keywords in combination with 'strict'.
return false;
}
// Strict implies layout, style, and paint.
// However, for serialization purposes, we keep the strict bit around.
aValue.SetIntValue(NS_STYLE_CONTAIN_STRICT |
NS_STYLE_CONTAIN_ALL_BITS, eCSSUnit_Enumerated);
}
return true;
}
static const int32_t maskLigatures[] = {
NS_FONT_VARIANT_LIGATURES_COMMON_MASK,
NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK,
@@ -13145,7 +13170,7 @@ CSSParserImpl::ParseListStyle()
bool
CSSParserImpl::ParseListStyleType(nsCSSValue& aValue)
{
if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_STRING, nullptr)) {
return true;
}
+12
View File
@@ -1487,6 +1487,18 @@ CSS_PROP_COLUMN(
kBorderWidthKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_Custom)
CSS_PROP_DISPLAY(
contain,
contain,
Contain,
CSS_PROPERTY_PARSE_VALUE |
CSS_PROPERTY_VALUE_PARSER_FUNCTION,
"layout.css.contain.enabled",
// Does not affect parsing, but is needed for tab completion in devtools:
VARIANT_HK | VARIANT_NONE,
kContainKTable,
CSS_PROP_NO_OFFSET,
eStyleAnimType_None)
CSS_PROP_CONTENT(
content,
content,
+9
View File
@@ -1522,6 +1522,15 @@ const KTableValue nsCSSProps::kMathDisplayKTable[] = {
eCSSKeyword_UNKNOWN,-1
};
const KTableValue nsCSSProps::kContainKTable[] = {
eCSSKeyword_none, NS_STYLE_CONTAIN_NONE,
eCSSKeyword_strict, NS_STYLE_CONTAIN_STRICT,
eCSSKeyword_layout, NS_STYLE_CONTAIN_LAYOUT,
eCSSKeyword_style, NS_STYLE_CONTAIN_STYLE,
eCSSKeyword_paint, NS_STYLE_CONTAIN_PAINT,
eCSSKeyword_UNKNOWN,-1
};
const KTableValue nsCSSProps::kContextOpacityKTable[] = {
eCSSKeyword_context_fill_opacity, NS_STYLE_CONTEXT_FILL_OPACITY,
eCSSKeyword_context_stroke_opacity, NS_STYLE_CONTEXT_STROKE_OPACITY,
+1
View File
@@ -656,6 +656,7 @@ public:
static const KTableValue kMaskTypeKTable[];
static const KTableValue kMathVariantKTable[];
static const KTableValue kMathDisplayKTable[];
static const KTableValue kContainKTable[];
static const KTableValue kContextOpacityKTable[];
static const KTableValue kContextPatternKTable[];
static const KTableValue kObjectFitKTable[];
+14
View File
@@ -1315,6 +1315,20 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult,
aResult);
break;
case eCSSProperty_contain:
if (intValue & NS_STYLE_CONTAIN_STRICT) {
NS_ASSERTION(intValue == (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS),
"contain: strict should imply contain: layout style paint");
// Only output strict.
intValue = NS_STYLE_CONTAIN_STRICT;
}
nsStyleUtil::AppendBitmaskCSSValue(aProperty,
intValue,
NS_STYLE_CONTAIN_STRICT,
NS_STYLE_CONTAIN_PAINT,
aResult);
break;
case eCSSProperty_justify_items:
AppendAlignJustifyValueToString(intValue, aResult);
break;
+42 -15
View File
@@ -51,9 +51,6 @@
using namespace mozilla;
using namespace mozilla::dom;
typedef const nsStyleBackground::Position Position;
typedef const nsStyleBackground::Position::PositionCoord PositionCoord;
typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
#if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon)
#define DEBUG_ComputedDOMStyle
@@ -1274,7 +1271,8 @@ nsComputedDOMStyle::DoGetTransform()
* using the named transforms. Until a real solution is found, we'll just
* use this approach.
*/
TransformReferenceBox refBox(mInnerFrame, nsSize(0, 0));
nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame,
nsSize(0, 0));
bool dummy;
gfx3DMatrix matrix =
@@ -2091,8 +2089,9 @@ nsComputedDOMStyle::DoGetBackgroundOrigin()
}
void
nsComputedDOMStyle::SetValueToPositionCoord(const PositionCoord& aCoord,
nsROCSSPrimitiveValue* aValue)
nsComputedDOMStyle::SetValueToPositionCoord(
const nsStyleBackground::Position::PositionCoord& aCoord,
nsROCSSPrimitiveValue* aValue)
{
if (!aCoord.mHasPercent) {
MOZ_ASSERT(aCoord.mPercent == 0.0f,
@@ -2106,8 +2105,9 @@ nsComputedDOMStyle::SetValueToPositionCoord(const PositionCoord& aCoord,
}
void
nsComputedDOMStyle::SetValueToPosition(const Position& aPosition,
nsDOMCSSValueList* aValueList)
nsComputedDOMStyle::SetValueToPosition(
const nsStyleBackground::Position& aPosition,
nsDOMCSSValueList* aValueList)
{
nsROCSSPrimitiveValue* valX = new nsROCSSPrimitiveValue;
aValueList->AppendCSSValue(valX);
@@ -3121,15 +3121,17 @@ nsComputedDOMStyle::DoGetListStyleType()
nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
CounterStyle* style = StyleList()->GetCounterStyle();
AnonymousCounterStyle* anonymous = style->AsAnonymous();
nsString tmp;
if (!anonymous) {
// want SetIdent
nsString type;
StyleList()->GetListStyleType(type);
nsString value;
nsStyleUtil::AppendEscapedCSSIdent(type, value);
val->SetString(value);
nsStyleUtil::AppendEscapedCSSIdent(type, tmp);
} else if (anonymous->IsSingleString()) {
const nsTArray<nsString>& symbols = anonymous->GetSymbols();
MOZ_ASSERT(symbols.Length() == 1);
nsStyleUtil::AppendEscapedCSSString(symbols[0], tmp);
} else {
nsAutoString tmp;
tmp.AppendLiteral("symbols(");
uint8_t system = anonymous->GetSystem();
@@ -3153,8 +3155,8 @@ nsComputedDOMStyle::DoGetListStyleType()
tmp.Append(' ');
}
tmp.Replace(tmp.Length() - 1, 1, char16_t(')'));
val->SetString(tmp);
}
val->SetString(tmp);
return val;
}
@@ -4028,6 +4030,31 @@ nsComputedDOMStyle::DoGetDisplay()
return val;
}
CSSValue*
nsComputedDOMStyle::DoGetContain()
{
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
int32_t mask = StyleDisplay()->mContain;
if (mask == 0) {
val->SetIdent(eCSSKeyword_none);
} else if (mask & NS_STYLE_CONTAIN_STRICT) {
NS_ASSERTION(mask == (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS),
"contain: strict should imply contain: layout style paint");
val->SetIdent(eCSSKeyword_strict);
} else {
nsAutoString valueStr;
nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_contain,
mask, NS_STYLE_CONTAIN_LAYOUT,
NS_STYLE_CONTAIN_PAINT, valueStr);
val->SetString(valueStr);
}
return val;
}
CSSValue*
nsComputedDOMStyle::DoGetPosition()
{
@@ -4994,7 +5021,7 @@ nsComputedDOMStyle::GetFrameBoundsWidthForTransform(nscoord& aWidth)
AssertFlushedPendingReflows();
aWidth = TransformReferenceBox(mInnerFrame).Width();
aWidth = nsStyleTransformMatrix::TransformReferenceBox(mInnerFrame).Width();
return true;
}
@@ -5008,7 +5035,7 @@ nsComputedDOMStyle::GetFrameBoundsHeightForTransform(nscoord& aHeight)
AssertFlushedPendingReflows();
aHeight = TransformReferenceBox(mInnerFrame).Height();
aHeight = nsStyleTransformMatrix::TransformReferenceBox(mInnerFrame).Height();
return true;
}
+1
View File
@@ -392,6 +392,7 @@ private:
mozilla::dom::CSSValue* DoGetClear();
mozilla::dom::CSSValue* DoGetFloat();
mozilla::dom::CSSValue* DoGetDisplay();
mozilla::dom::CSSValue* DoGetContain();
mozilla::dom::CSSValue* DoGetPosition();
mozilla::dom::CSSValue* DoGetClip();
mozilla::dom::CSSValue* DoGetImageOrientation();
@@ -110,6 +110,7 @@ COMPUTED_STYLE_PROP(column_rule_color, ColumnRuleColor)
COMPUTED_STYLE_PROP(column_rule_style, ColumnRuleStyle)
COMPUTED_STYLE_PROP(column_rule_width, ColumnRuleWidth)
COMPUTED_STYLE_PROP(column_width, ColumnWidth)
COMPUTED_STYLE_PROP(contain, Contain)
COMPUTED_STYLE_PROP(content, Content)
COMPUTED_STYLE_PROP(counter_increment, CounterIncrement)
COMPUTED_STYLE_PROP(counter_reset, CounterReset)
+31 -33
View File
@@ -1394,7 +1394,6 @@ SetFactor(const nsCSSValue& aValue, float& aField, bool& aCanStoreInRuleTree,
NS_NOTREACHED("SetFactor: inappropriate unit");
}
// Overloaded new operator that allocates from a presShell arena.
void*
nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
{
@@ -1554,20 +1553,11 @@ nsRuleNode::Transition(nsIStyleRule* aRule, uint8_t aLevel,
else {
next = entry->mRuleNode = new (mPresContext)
nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule);
if (!next) {
PL_DHashTableRawRemove(ChildrenHash(), entry);
NS_WARNING("out of memory");
return this;
}
}
} else if (!next) {
// Create the new entry in our list.
next = new (mPresContext)
nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule);
if (!next) {
NS_WARNING("out of memory");
return this;
}
next->mNextSibling = ChildrenList();
SetChildrenList(next);
}
@@ -2398,18 +2388,19 @@ nsRuleNode::WalkRuleTree(const nsStyleStructID aSID,
return SetDefaultOnRoot(aSID, aContext);
}
// We need to compute the data from the information that the rules specified.
const void* res;
#define STYLE_STRUCT_TEST aSID
#define STYLE_STRUCT(name, checkdata_cb) \
res = Compute##name##Data(startStruct, &ruleData, aContext, \
highestNode, detail, ruleData.mCanStoreInRuleTree);
typedef const void* (nsRuleNode::*ComputeFunc)(void*, const nsRuleData*,
nsStyleContext*, nsRuleNode*,
RuleDetail, const bool);
static const ComputeFunc sComputeFuncs[] = {
#define STYLE_STRUCT(name, checkdata_cb) &nsRuleNode::Compute##name##Data,
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_TEST
};
// Now return the result.
return res;
// We need to compute the data from the information that the rules specified.
return (this->*sComputeFuncs[aSID])(startStruct, &ruleData, aContext,
highestNode, detail,
ruleData.mCanStoreInRuleTree);
}
const void*
@@ -5259,6 +5250,12 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct,
parentDisplay->mDisplay,
NS_STYLE_DISPLAY_INLINE, 0, 0, 0, 0);
// contain: none, enum, inherit, initial
SetDiscrete(*aRuleData->ValueForContain(), display->mContain, canStoreInRuleTree,
SETDSC_ENUMERATED | SETDSC_NONE | SETDSC_UNSET_INITIAL,
parentDisplay->mContain,
NS_STYLE_CONTAIN_NONE, 0, NS_STYLE_CONTAIN_NONE, 0, 0);
// mix-blend-mode: enum, inherit, initial
SetDiscrete(*aRuleData->ValueForMixBlendMode(), display->mMixBlendMode,
canStoreInRuleTree,
@@ -7215,6 +7212,13 @@ nsRuleNode::ComputeListData(void* aStartStruct,
list->SetListStyleType(typeIdent, mPresContext);
break;
}
case eCSSUnit_String: {
nsString str;
typeValue->GetStringValue(str);
list->SetListStyleType(NS_LITERAL_STRING(""),
new AnonymousCounterStyle(str));
break;
}
case eCSSUnit_Enumerated: {
// For compatibility with html attribute map.
// This branch should never be called for value from CSS.
@@ -7242,9 +7246,9 @@ nsRuleNode::ComputeListData(void* aStartStruct,
break;
}
case eCSSUnit_Symbols:
list->SetListStyleType(NS_LITERAL_STRING(""),
mPresContext->CounterStyleManager()->
BuildCounterStyle(typeValue->GetArrayValue()));
list->SetListStyleType(
NS_LITERAL_STRING(""),
new AnonymousCounterStyle(typeValue->GetArrayValue()));
break;
case eCSSUnit_Null:
break;
@@ -7343,6 +7347,8 @@ SetGridTrackBreadth(const nsCSSValue& aValue,
nsCSSUnit unit = aValue.GetUnit();
if (unit == eCSSUnit_FlexFraction) {
aResult.SetFlexFractionValue(aValue.GetFloatValue());
} else if (unit == eCSSUnit_Auto) {
aResult.SetAutoValue();
} else {
MOZ_ASSERT(unit != eCSSUnit_Inherit && unit != eCSSUnit_Unset,
"Unexpected value that would use dummyParentCoord");
@@ -7370,12 +7376,6 @@ SetGridTrackSize(const nsCSSValue& aValue,
aStyleContext, aPresContext, aCanStoreInRuleTree);
SetGridTrackBreadth(func->Item(2), aResultMax,
aStyleContext, aPresContext, aCanStoreInRuleTree);
} else if (aValue.GetUnit() == eCSSUnit_Auto) {
// 'auto' computes to 'minmax(min-content, max-content)'
aResultMin.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT,
eStyleUnit_Enumerated);
aResultMax.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT,
eStyleUnit_Enumerated);
} else {
// A single <track-breadth>,
// specifies identical min and max sizing functions.
@@ -7409,12 +7409,10 @@ SetGridAutoColumnsRows(const nsCSSValue& aValue,
case eCSSUnit_Initial:
case eCSSUnit_Unset:
// The initial value is 'auto',
// which computes to 'minmax(min-content, max-content)'.
// which computes to 'minmax(auto, auto)'.
// (Explicitly-specified 'auto' values are handled in SetGridTrackSize.)
aResultMin.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT,
eStyleUnit_Enumerated);
aResultMax.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT,
eStyleUnit_Enumerated);
aResultMin.SetAutoValue();
aResultMax.SetAutoValue();
break;
default:
+9 -24
View File
@@ -11,6 +11,7 @@
#ifndef nsRuleNode_h___
#define nsRuleNode_h___
#include "mozilla/RangedArray.h"
#include "nsPresContext.h"
#include "nsStyleStruct.h"
@@ -24,29 +25,11 @@ class nsCSSValue;
class nsStyleCoord;
struct nsCSSValuePairList;
template <nsStyleStructID MinIndex, nsStyleStructID Count>
class FixedStyleStructArray
{
private:
void* mArray[Count];
public:
void*& operator[](nsStyleStructID aIndex) {
MOZ_ASSERT(MinIndex <= aIndex && aIndex < (MinIndex + Count),
"out of range");
return mArray[aIndex - MinIndex];
}
const void* operator[](nsStyleStructID aIndex) const {
MOZ_ASSERT(MinIndex <= aIndex && aIndex < (MinIndex + Count),
"out of range");
return mArray[aIndex - MinIndex];
}
};
struct nsInheritedStyleData
{
FixedStyleStructArray<nsStyleStructID_Inherited_Start,
nsStyleStructID_Inherited_Count> mStyleStructs;
mozilla::RangedArray<void*,
nsStyleStructID_Inherited_Start,
nsStyleStructID_Inherited_Count> mStyleStructs;
void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
return aContext->PresShell()->
@@ -83,8 +66,9 @@ struct nsInheritedStyleData
struct nsResetStyleData
{
FixedStyleStructArray<nsStyleStructID_Reset_Start,
nsStyleStructID_Reset_Count> mStyleStructs;
mozilla::RangedArray<void*,
nsStyleStructID_Reset_Start,
nsStyleStructID_Reset_Count> mStyleStructs;
nsResetStyleData()
{
@@ -410,7 +394,7 @@ private:
uint32_t mRefCnt;
public:
// Overloaded new operator that allocates from a presShell arena.
// Infallible overloaded new operator that allocates from a presShell arena.
void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW;
void Destroy() { DestroyInternal(nullptr); }
@@ -667,6 +651,7 @@ private:
~nsRuleNode();
public:
// This is infallible; it will never return nullptr.
static nsRuleNode* CreateRootNode(nsPresContext* aPresContext);
static void EnsureBlockDisplay(uint8_t& display,
+13
View File
@@ -448,6 +448,19 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) {
#define NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER 37
#define NS_STYLE_DISPLAY_CONTENTS 38
// See nsStyleDisplay
// If these are re-ordered, nsComputedDOMStyle::DoGetContain() and
// nsCSSValue::AppendToString() must be updated.
#define NS_STYLE_CONTAIN_NONE 0
#define NS_STYLE_CONTAIN_STRICT 0x1
#define NS_STYLE_CONTAIN_LAYOUT 0x2
#define NS_STYLE_CONTAIN_STYLE 0x4
#define NS_STYLE_CONTAIN_PAINT 0x8
// NS_STYLE_CONTAIN_ALL_BITS does not correspond to a keyword.
#define NS_STYLE_CONTAIN_ALL_BITS (NS_STYLE_CONTAIN_LAYOUT | \
NS_STYLE_CONTAIN_STYLE | \
NS_STYLE_CONTAIN_PAINT)
// Shared constants for all align/justify properties (nsStylePosition):
#define NS_STYLE_ALIGN_AUTO 0
#define NS_STYLE_ALIGN_START 1
+1 -4
View File
@@ -218,10 +218,7 @@ nsStyleSet::BeginReconstruct()
NS_ASSERTION(mRuleTree, "Reconstructing before first construction?");
// Create a new rule tree root
nsRuleNode* newTree =
nsRuleNode::CreateRootNode(mRuleTree->PresContext());
if (!newTree)
return NS_ERROR_OUT_OF_MEMORY;
nsRuleNode* newTree = nsRuleNode::CreateRootNode(mRuleTree->PresContext());
// Save the old rule tree so we can destroy it later
if (!mOldRuleTrees.AppendElement(mRuleTree)) {
+10 -9
View File
@@ -32,6 +32,8 @@
#include "nsIDocument.h"
#include <algorithm>
using namespace mozilla;
static_assert((((1 << nsStyleStructID_Length) - 1) &
~(NS_STYLE_INHERIT_MASK)) == 0,
"Not enough bits in NS_STYLE_INHERIT_MASK");
@@ -1411,15 +1413,11 @@ nsStylePosition::nsStylePosition(void)
mFlexBasis.SetAutoValue();
// The initial value of grid-auto-columns and grid-auto-rows is 'auto',
// which computes to 'minmax(min-content, max-content)'.
mGridAutoColumnsMin.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT,
eStyleUnit_Enumerated);
mGridAutoColumnsMax.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT,
eStyleUnit_Enumerated);
mGridAutoRowsMin.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT,
eStyleUnit_Enumerated);
mGridAutoRowsMax.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT,
eStyleUnit_Enumerated);
// which computes to 'minmax(auto, auto)'.
mGridAutoColumnsMin.SetAutoValue();
mGridAutoColumnsMax.SetAutoValue();
mGridAutoRowsMin.SetAutoValue();
mGridAutoRowsMax.SetAutoValue();
mGridAutoFlow = NS_STYLE_GRID_AUTO_FLOW_ROW;
mBoxSizing = NS_STYLE_BOX_SIZING_CONTENT;
@@ -2605,6 +2603,7 @@ nsStyleDisplay::nsStyleDisplay()
mAppearance = NS_THEME_NONE;
mDisplay = NS_STYLE_DISPLAY_INLINE;
mOriginalDisplay = mDisplay;
mContain = NS_STYLE_CONTAIN_NONE;
mPosition = NS_STYLE_POSITION_STATIC;
mFloats = NS_STYLE_FLOAT_NONE;
mOriginalFloats = mFloats;
@@ -2670,6 +2669,7 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
, mOpacity(aSource.mOpacity)
, mDisplay(aSource.mDisplay)
, mOriginalDisplay(aSource.mOriginalDisplay)
, mContain(aSource.mContain)
, mAppearance(aSource.mAppearance)
, mPosition(aSource.mPosition)
, mFloats(aSource.mFloats)
@@ -2733,6 +2733,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const
if (!EqualURIs(mBinding, aOther.mBinding)
|| mPosition != aOther.mPosition
|| mDisplay != aOther.mDisplay
|| mContain != aOther.mContain
|| (mFloats == NS_STYLE_FLOAT_NONE) != (aOther.mFloats == NS_STYLE_FLOAT_NONE)
|| mOverflowX != aOther.mOverflowX
|| mOverflowY != aOther.mOverflowY
+1
View File
@@ -1927,6 +1927,7 @@ struct nsStyleDisplay {
uint8_t mOriginalDisplay; // [reset] saved mDisplay for position:absolute/fixed
// and float:left/right; otherwise equal
// to mDisplay
uint8_t mContain; // [reset] see nsStyleConsts.h NS_STYLE_CONTAIN_*
uint8_t mAppearance; // [reset]
uint8_t mPosition; // [reset] see nsStyleConsts.h
uint8_t mFloats; // [reset] see nsStyleConsts.h NS_STYLE_FLOAT_*
+1 -2
View File
@@ -227,8 +227,7 @@ ProcessMatrix3D(gfx3DMatrix& aMatrix,
&aRefBox, &TransformReferenceBox::Height);
temp._43 = ProcessTranslatePart(aData->Item(15),
aContext, aPresContext, aCanStoreInRuleTree,
&aRefBox, &TransformReferenceBox::Height);
// XXXjwatt why are we using TransformReferenceBox::Height for the z compontent?!
nullptr);
aMatrix.PreMultiply(temp);
}
+46 -2
View File
@@ -2764,6 +2764,7 @@ var gCSSProperties = {
subproperties: [ "list-style-type", "list-style-position", "list-style-image" ],
initial_values: [ "outside", "disc", "disc outside", "outside disc", "disc none", "none disc", "none disc outside", "none outside disc", "disc none outside", "disc outside none", "outside none disc", "outside disc none" ],
other_values: [ "inside none", "none inside", "none none inside", "square", "none", "none none", "outside none none", "none outside none", "none none outside", "none outside", "outside none", "outside outside", "outside inside", "\\32 style", "\\32 style inside",
'"-"', "'-'", "inside '-'", "'-' outside", "none '-'", "inside none '-'",
"symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\")",
"symbols(cyclic \"*\" \"\\2020\" \"\\2021\" \"\\A7\")",
"inside symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\")",
@@ -2841,6 +2842,7 @@ var gCSSProperties = {
"-moz-ethiopic-halehame-am",
"-moz-ethiopic-halehame-ti-er", "-moz-ethiopic-halehame-ti-et",
"other-style", "inside", "outside", "\\32 style",
'"-"', "'-'",
"symbols(\"*\" \"\\2020\" \"\\2021\" \"\\A7\")",
"symbols(cyclic '*' '\\2020' '\\2021' '\\A7')"
],
@@ -5835,6 +5837,8 @@ if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) {
"max-content",
"calc(20px + 10%)",
"minmax(20px, max-content)",
"minmax(min-content, auto)",
"minmax(auto, max-content)",
"m\\69nmax(20px, 4Fr)",
"MinMax(min-content, calc(20px + 10%))",
],
@@ -5851,7 +5855,6 @@ if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) {
"mİnmax(20px, 100px)",
"minmax(20px, 100px, 200px)",
"maxmin(100px, 20px)",
"minmax(min-content, auto)",
"minmax(min-content, minmax(30px, max-content))",
]
};
@@ -5876,6 +5879,8 @@ if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) {
"(normal) 40px () auto ( ) 12%",
"(foo) 40px min-content ( bar ) calc(20px + 10%) max-content",
"40px min-content calc(20px + 10%) max-content",
"minmax(min-content, auto)",
"minmax(auto, max-content)",
"m\\69nmax(20px, 4Fr)",
"40px MinMax(min-content, calc(20px + 10%)) max-content",
"40px 2em",
@@ -5922,7 +5927,6 @@ if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) {
"mİnmax(20px, 100px)",
"minmax(20px, 100px, 200px)",
"maxmin(100px, 20px)",
"minmax(min-content, auto)",
"minmax(min-content, minmax(30px, max-content))",
"repeat(0, 20px)",
"repeat(-3, 20px)",
@@ -6225,6 +6229,46 @@ if (SpecialPowers.getBoolPref("layout.css.display-contents.enabled")) {
gCSSProperties["display"].other_values.push("contents");
}
if (SpecialPowers.getBoolPref("layout.css.contain.enabled")) {
gCSSProperties["contain"] = {
domProp: "contain",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "none" ],
other_values: [
"strict",
"layout",
"style",
"layout style",
"style layout",
"paint",
"layout paint",
"paint layout",
"style paint",
"paint style",
"layout style paint",
"layout paint style",
"style paint layout",
"paint style layout",
],
invalid_values: [
"none strict",
"strict layout",
"strict layout style",
"layout strict",
"layout style strict",
"layout style paint strict",
"paint strict",
"style strict",
"paint paint",
"strict strict",
"auto",
"10px",
"0",
]
};
}
if (SpecialPowers.getBoolPref("layout.css.image-orientation.enabled")) {
gCSSProperties["image-orientation"] = {
domProp: "imageOrientation",
@@ -18,8 +18,8 @@ var initial_values = {
gridTemplateRows: "none",
gridAutoFlow: "row",
// Computed value for 'auto'
gridAutoColumns: "minmax(min-content, max-content)",
gridAutoRows: "minmax(min-content, max-content)",
gridAutoColumns: "auto",
gridAutoRows: "auto",
};
// For various specified values of the grid-template shorthand,
@@ -106,24 +106,24 @@ var grid_template_test_cases = [
{
specified: "'fizz'",
gridTemplateAreas: "\"fizz\"",
gridTemplateRows: "minmax(min-content, max-content)",
gridTemplateRows: "auto",
},
{
specified: "(bar) 'fizz'",
gridTemplateAreas: "\"fizz\"",
gridTemplateRows: "(bar) minmax(min-content, max-content)",
gridTemplateRows: "(bar) auto",
},
{
specified: "(foo) 40px / 'fizz'",
gridTemplateAreas: "\"fizz\"",
gridTemplateColumns: "(foo) 40px",
gridTemplateRows: "minmax(min-content, max-content)",
gridTemplateRows: "auto",
},
{
specified: "(foo) 40px / (bar) 'fizz'",
gridTemplateAreas: "\"fizz\"",
gridTemplateColumns: "(foo) 40px",
gridTemplateRows: "(bar) minmax(min-content, max-content)",
gridTemplateRows: "(bar) auto",
},
{
specified: "(foo) 40px / 'fizz' 100px",
@@ -195,7 +195,7 @@ grid_test_cases = grid_template_test_cases.concat([
{
specified: "column dense auto",
gridAutoFlow: "column dense",
gridAutoColumns: "minmax(min-content, max-content)",
gridAutoColumns: "auto",
},
{
specified: "dense row minmax(min-content, 2fr)",
+44
View File
@@ -0,0 +1,44 @@
/* -*- 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 compile-time constant-length array, with bounds-checking assertions -- but
* unlike mozilla::Array, with indexes biased by a constant.
*
* Thus where mozilla::Array<int, 3> is a three-element array indexed by [0, 3),
* mozilla::RangedArray<int, 8, 3> is a three-element array indexed by [8, 11).
*/
#ifndef mozilla_RangedArray_h
#define mozilla_RangedArray_h
#include "mozilla/Array.h"
namespace mozilla {
template<typename T, size_t MinIndex, size_t Length>
class RangedArray
{
public:
T& operator[](size_t aIndex)
{
MOZ_ASSERT(aIndex == MinIndex || aIndex > MinIndex);
return mArr[aIndex - MinIndex];
}
const T& operator[](size_t aIndex) const
{
MOZ_ASSERT(aIndex == MinIndex || aIndex > MinIndex);
return mArr[aIndex - MinIndex];
}
private:
Array<T, Length> mArr;
};
} // namespace mozilla
#endif // mozilla_RangedArray_h
+1
View File
@@ -68,6 +68,7 @@ EXPORTS.mozilla = [
'PodOperations.h',
'Poison.h',
'Range.h',
'RangedArray.h',
'RangedPtr.h',
'RefCounted.h',
'RefCountType.h',
+3
View File
@@ -2561,6 +2561,9 @@ pref("layout.css.overflow-clip-box.enabled", false);
// Is support for CSS grid enabled?
pref("layout.css.grid.enabled", false);
// Is support for CSS contain enabled?
pref("layout.css.contain.enabled", false);
// Is support for CSS Ruby enabled?
//
// When this pref is removed, make sure that the pref callback registration
+3
View File
@@ -147,6 +147,9 @@ user_pref("layout.css.report_errors", true);
// Enable CSS Grid for testing
user_pref("layout.css.grid.enabled", true);
// Enable CSS 'contain' for testing
user_pref("layout.css.contain.enabled", true);
// Enable CSS object-fit & object-position for testing
user_pref("layout.css.object-fit-and-position.enabled", true);
@@ -25,7 +25,7 @@ let testData = [
["VK_TAB", {shiftKey: true}, "display", -1, 0],
["VK_BACK_SPACE", {}, "", -1, 0],
["c", {}, "caption-side", 0, 10],
["o", {}, "color", 0, 6],
["o", {}, "color", 0, 7],
["VK_TAB", {}, "none", -1, 0],
["r", {}, "rebeccapurple", 0, 6],
["VK_DOWN", {}, "red", 1, 6],
+24 -66
View File
@@ -167,10 +167,17 @@ public:
*/
uint32_t EnumerateRead(EnumReadFunction aEnumFunc, void* aUserArg) const
{
s_EnumReadArgs enumData = { aEnumFunc, aUserArg };
return PL_DHashTableEnumerate(const_cast<PLDHashTable*>(&this->mTable),
s_EnumReadStub,
&enumData);
uint32_t n = 0;
for (auto iter = this->mTable.Iter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<EntryType*>(iter.Get());
PLDHashOperator op = aEnumFunc(entry->GetKey(), entry->mData, aUserArg);
n++;
MOZ_ASSERT(!(op & PL_DHASH_REMOVE));
if (op & PL_DHASH_STOP) {
break;
}
}
return n;
}
/**
@@ -196,10 +203,19 @@ public:
*/
uint32_t Enumerate(EnumFunction aEnumFunc, void* aUserArg)
{
s_EnumArgs enumData = { aEnumFunc, aUserArg };
return PL_DHashTableEnumerate(&this->mTable,
s_EnumStub,
&enumData);
uint32_t n = 0;
for (auto iter = this->mTable.RemovingIter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<EntryType*>(iter.Get());
PLDHashOperator op = aEnumFunc(entry->GetKey(), entry->mData, aUserArg);
n++;
if (op & PL_DHASH_REMOVE) {
iter.Remove();
}
if (op & PL_DHASH_STOP) {
break;
}
}
return n;
}
/**
@@ -273,33 +289,6 @@ public:
#endif
protected:
/**
* used internally during EnumerateRead. Allocated on the stack.
* @param func the enumerator passed to EnumerateRead
* @param userArg the userArg passed to EnumerateRead
*/
struct s_EnumReadArgs
{
EnumReadFunction func;
void* userArg;
};
static PLDHashOperator s_EnumReadStub(PLDHashTable* aTable,
PLDHashEntryHdr* aHdr,
uint32_t aNumber,
void* aArg);
struct s_EnumArgs
{
EnumFunction func;
void* userArg;
};
static PLDHashOperator s_EnumStub(PLDHashTable* aTable,
PLDHashEntryHdr* aHdr,
uint32_t aNumber,
void* aArg);
struct s_SizeOfArgs
{
SizeOfEntryExcludingThisFun func;
@@ -374,37 +363,6 @@ nsBaseHashtableET<KeyClass, DataType>::~nsBaseHashtableET()
// nsBaseHashtable definitions
//
template<class KeyClass, class DataType, class UserDataType>
PLDHashOperator
nsBaseHashtable<KeyClass, DataType, UserDataType>::s_EnumReadStub(
PLDHashTable* aTable, PLDHashEntryHdr* aHdr, uint32_t aNumber, void* aArg)
{
EntryType* ent = static_cast<EntryType*>(aHdr);
s_EnumReadArgs* eargs = (s_EnumReadArgs*)aArg;
PLDHashOperator res = (eargs->func)(ent->GetKey(), ent->mData, eargs->userArg);
NS_ASSERTION(!(res & PL_DHASH_REMOVE),
"PL_DHASH_REMOVE return during const enumeration; ignoring.");
if (res & PL_DHASH_STOP) {
return PL_DHASH_STOP;
}
return PL_DHASH_NEXT;
}
template<class KeyClass, class DataType, class UserDataType>
PLDHashOperator
nsBaseHashtable<KeyClass, DataType, UserDataType>::s_EnumStub(
PLDHashTable* aTable, PLDHashEntryHdr* aHdr, uint32_t aNumber, void* aArg)
{
EntryType* ent = static_cast<EntryType*>(aHdr);
s_EnumArgs* eargs = (s_EnumArgs*)aArg;
return (eargs->func)(ent->GetKey(), ent->mData, eargs->userArg);
}
template<class KeyClass, class DataType, class UserDataType>
size_t
nsBaseHashtable<KeyClass, DataType, UserDataType>::s_SizeOfStub(
+13 -32
View File
@@ -207,8 +207,19 @@ public:
*/
uint32_t EnumerateEntries(Enumerator aEnumFunc, void* aUserArg)
{
s_EnumArgs args = { aEnumFunc, aUserArg };
return PL_DHashTableEnumerate(&mTable, s_EnumStub, &args);
uint32_t n = 0;
for (auto iter = mTable.RemovingIter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<EntryType*>(iter.Get());
PLDHashOperator op = aEnumFunc(entry, aUserArg);
n++;
if (op & PL_DHASH_REMOVE) {
iter.Remove();
}
if (op & PL_DHASH_STOP) {
break;
}
}
return n;
}
/**
@@ -324,23 +335,6 @@ protected:
static void s_InitEntry(PLDHashEntryHdr* aEntry, const void* aKey);
/**
* passed internally during enumeration. Allocated on the stack.
*
* @param userFunc the Enumerator function passed to
* EnumerateEntries by the client
* @param userArg the userArg passed unaltered
*/
struct s_EnumArgs
{
Enumerator userFunc;
void* userArg;
};
static PLDHashOperator s_EnumStub(PLDHashTable* aTable,
PLDHashEntryHdr* aEntry,
uint32_t aNumber, void* aArg);
/**
* passed internally during sizeOf counting. Allocated on the stack.
*
@@ -473,19 +467,6 @@ nsTHashtable<EntryType>::s_InitEntry(PLDHashEntryHdr* aEntry,
new (aEntry) EntryType(reinterpret_cast<KeyTypePointer>(aKey));
}
template<class EntryType>
PLDHashOperator
nsTHashtable<EntryType>::s_EnumStub(PLDHashTable* aTable,
PLDHashEntryHdr* aEntry,
uint32_t aNumber,
void* aArg)
{
// dereferences the function-pointer to the user's enumeration function
return (*reinterpret_cast<s_EnumArgs*>(aArg)->userFunc)(
static_cast<EntryType*>(aEntry),
reinterpret_cast<s_EnumArgs*>(aArg)->userArg);
}
template<class EntryType>
size_t
nsTHashtable<EntryType>::s_SizeOfStub(PLDHashEntryHdr* aEntry,
+104 -261
View File
@@ -20,56 +20,51 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/ChaosMode.h"
#ifdef PL_DHASHMETER
# define METER(x) x
#else
# define METER(x) /* nothing */
#endif
using namespace mozilla;
/*
* The following DEBUG-only code is used to assert that calls to one of
* table->mOps or to an enumerator do not cause re-entry into a call that
* can mutate the table.
*/
#ifdef DEBUG
/*
* Most callers that assert about the recursion level don't care about
* this magical value because they are asserting that mutation is
* allowed (and therefore the level is 0 or 1, depending on whether they
* incremented it).
*
* Only the destructor needs to allow this special value.
*/
#define IMMUTABLE_RECURSION_LEVEL UINT32_MAX
class AutoReadOp
{
Checker& mChk;
public:
explicit AutoReadOp(Checker& aChk) : mChk(aChk) { mChk.StartReadOp(); }
~AutoReadOp() { mChk.EndReadOp(); }
};
#define RECURSION_LEVEL_SAFE_TO_FINISH(table_) \
(table_->mRecursionLevel == 0 || \
table_->mRecursionLevel == IMMUTABLE_RECURSION_LEVEL)
class AutoWriteOp
{
Checker& mChk;
public:
explicit AutoWriteOp(Checker& aChk) : mChk(aChk) { mChk.StartWriteOp(); }
~AutoWriteOp() { mChk.EndWriteOp(); }
};
#define INCREMENT_RECURSION_LEVEL(table_) \
do { \
if (table_->mRecursionLevel != IMMUTABLE_RECURSION_LEVEL) { \
const uint32_t oldRecursionLevel = table_->mRecursionLevel++; \
MOZ_ASSERT(oldRecursionLevel < IMMUTABLE_RECURSION_LEVEL - 1); \
} \
} while(0)
#define DECREMENT_RECURSION_LEVEL(table_) \
do { \
if (table_->mRecursionLevel != IMMUTABLE_RECURSION_LEVEL) { \
const uint32_t oldRecursionLevel = table_->mRecursionLevel--; \
MOZ_ASSERT(oldRecursionLevel > 0); \
} \
} while(0)
class AutoIteratorRemovalOp
{
Checker& mChk;
public:
explicit AutoIteratorRemovalOp(Checker& aChk)
: mChk(aChk)
{
mChk.StartIteratorRemovalOp();
}
~AutoIteratorRemovalOp() { mChk.EndIteratorRemovalOp(); }
};
#else
class AutoDestructorOp
{
Checker& mChk;
public:
explicit AutoDestructorOp(Checker& aChk)
: mChk(aChk)
{
mChk.StartDestructorOp();
}
~AutoDestructorOp() { mChk.EndDestructorOp(); }
};
#define INCREMENT_RECURSION_LEVEL(table_) do { } while(0)
#define DECREMENT_RECURSION_LEVEL(table_) do { } while(0)
#endif /* defined(DEBUG) */
using namespace mozilla;
#endif
PLDHashNumber
PL_DHashStringKey(PLDHashTable* aTable, const void* aKey)
@@ -232,10 +227,9 @@ PLDHashTable::PLDHashTable(const PLDHashTableOps* aOps, uint32_t aEntrySize,
, mGeneration(0)
, mEntryStore(nullptr)
#ifdef DEBUG
, mRecursionLevel(0)
, mChecker()
#endif
{
METER(memset(&mStats, 0, sizeof(mStats)));
}
PLDHashTable&
@@ -262,19 +256,17 @@ PLDHashTable::operator=(PLDHashTable&& aOther)
mRemovedCount = Move(aOther.mRemovedCount);
mGeneration = Move(aOther.mGeneration);
mEntryStore = Move(aOther.mEntryStore);
#ifdef PL_DHASHMETER
mStats = Move(aOther.mStats);
#endif
#ifdef DEBUG
// Atomic<> doesn't have an |operator=(Atomic<>&&)|.
mRecursionLevel = uint32_t(aOther.mRecursionLevel);
mChecker = Move(aOther.mChecker);
#endif
// Clear up |aOther| so its destruction will be a no-op.
aOther.mEntryStore = nullptr;
{
#ifdef DEBUG
aOther.mRecursionLevel = 0;
AutoDestructorOp op(mChecker);
#endif
aOther.mEntryStore = nullptr;
}
return *this;
}
@@ -317,27 +309,25 @@ PLDHashTable::EntryIsFree(PLDHashEntryHdr* aEntry)
PLDHashTable::~PLDHashTable()
{
#ifdef DEBUG
AutoDestructorOp op(mChecker);
#endif
if (!mEntryStore) {
return;
}
INCREMENT_RECURSION_LEVEL(this);
/* Clear any remaining live entries. */
char* entryAddr = mEntryStore;
char* entryLimit = entryAddr + Capacity() * mEntrySize;
while (entryAddr < entryLimit) {
PLDHashEntryHdr* entry = (PLDHashEntryHdr*)entryAddr;
if (ENTRY_IS_LIVE(entry)) {
METER(mStats.mRemoveEnums++);
mOps->clearEntry(this, entry);
}
entryAddr += mEntrySize;
}
DECREMENT_RECURSION_LEVEL(this);
MOZ_ASSERT(RECURSION_LEVEL_SAFE_TO_FINISH(this));
/* Free entry storage last. */
free(mEntryStore);
mEntryStore = nullptr;
@@ -370,7 +360,6 @@ PLDHashEntryHdr* PL_DHASH_FASTCALL
PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
{
MOZ_ASSERT(mEntryStore);
METER(mStats.mSearches++);
NS_ASSERTION(!(aKeyHash & COLLISION_FLAG),
"!(aKeyHash & COLLISION_FLAG)");
@@ -380,7 +369,6 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
/* Miss: return space for a new entry. */
if (EntryIsFree(entry)) {
METER(mStats.mMisses++);
return (Reason == ForAdd) ? entry : nullptr;
}
@@ -388,7 +376,6 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
PLDHashMatchEntry matchEntry = mOps->matchEntry;
if (MATCH_ENTRY_KEYHASH(entry, aKeyHash) &&
matchEntry(this, entry, aKey)) {
METER(mStats.mHits++);
return entry;
}
@@ -414,20 +401,17 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
}
}
METER(mStats.mSteps++);
hash1 -= hash2;
hash1 &= sizeMask;
entry = ADDRESS_ENTRY(this, hash1);
if (EntryIsFree(entry)) {
METER(mStats.mMisses++);
return (Reason == ForAdd) ? (firstRemoved ? firstRemoved : entry)
: nullptr;
}
if (MATCH_ENTRY_KEYHASH(entry, aKeyHash) &&
matchEntry(this, entry, aKey)) {
METER(mStats.mHits++);
return entry;
}
}
@@ -449,7 +433,6 @@ PLDHashTable::SearchTable(const void* aKey, PLDHashNumber aKeyHash)
PLDHashEntryHdr* PL_DHASH_FASTCALL
PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
{
METER(mStats.mSearches++);
MOZ_ASSERT(mEntryStore);
NS_ASSERTION(!(aKeyHash & COLLISION_FLAG),
"!(aKeyHash & COLLISION_FLAG)");
@@ -460,7 +443,6 @@ PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
/* Miss: return space for a new entry. */
if (EntryIsFree(entry)) {
METER(mStats.mMisses++);
return entry;
}
@@ -474,13 +456,11 @@ PLDHashTable::FindFreeEntry(PLDHashNumber aKeyHash)
"!ENTRY_IS_REMOVED(entry)");
entry->mKeyHash |= COLLISION_FLAG;
METER(mStats.mSteps++);
hash1 -= hash2;
hash1 &= sizeMask;
entry = ADDRESS_ENTRY(this, hash1);
if (EntryIsFree(entry)) {
METER(mStats.mMisses++);
return entry;
}
}
@@ -561,28 +541,23 @@ PLDHashTable::ComputeKeyHash(const void* aKey)
MOZ_ALWAYS_INLINE PLDHashEntryHdr*
PLDHashTable::Search(const void* aKey)
{
INCREMENT_RECURSION_LEVEL(this);
METER(mStats.mSearches++);
#ifdef DEBUG
AutoReadOp op(mChecker);
#endif
PLDHashEntryHdr* entry =
mEntryStore ? SearchTable<ForSearchOrRemove>(aKey, ComputeKeyHash(aKey))
: nullptr;
DECREMENT_RECURSION_LEVEL(this);
return entry;
}
MOZ_ALWAYS_INLINE PLDHashEntryHdr*
PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
{
PLDHashNumber keyHash;
PLDHashEntryHdr* entry;
uint32_t capacity;
MOZ_ASSERT(mRecursionLevel == 0);
INCREMENT_RECURSION_LEVEL(this);
#ifdef DEBUG
AutoWriteOp op(mChecker);
#endif
// Allocate the entry storage if it hasn't already been allocated.
if (!mEntryStore) {
@@ -592,9 +567,7 @@ PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
&nbytes));
mEntryStore = (char*)malloc(nbytes);
if (!mEntryStore) {
METER(mStats.mAddFailures++);
entry = nullptr;
goto exit;
return nullptr;
}
memset(mEntryStore, 0, nbytes);
}
@@ -604,15 +577,13 @@ PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
* in the table, we may grow once more than necessary, but only if we
* are on the edge of being overloaded.
*/
capacity = Capacity();
uint32_t capacity = Capacity();
if (mEntryCount + mRemovedCount >= MaxLoad(capacity)) {
/* Compress if a quarter or more of all entries are removed. */
int deltaLog2;
if (mRemovedCount >= capacity >> 2) {
METER(mStats.mCompresses++);
deltaLog2 = 0;
} else {
METER(mStats.mGrows++);
deltaLog2 = 1;
}
@@ -623,9 +594,7 @@ PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
*/
if (!ChangeTable(deltaLog2) &&
mEntryCount + mRemovedCount >= MaxLoadOnGrowthFailure(capacity)) {
METER(mStats.mAddFailures++);
entry = nullptr;
goto exit;
return nullptr;
}
}
@@ -633,13 +602,11 @@ PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
* Look for entry after possibly growing, so we don't have to add it,
* then skip it while growing the table and re-add it after.
*/
keyHash = ComputeKeyHash(aKey);
entry = SearchTable<ForAdd>(aKey, keyHash);
PLDHashNumber keyHash = ComputeKeyHash(aKey);
PLDHashEntryHdr* entry = SearchTable<ForAdd>(aKey, keyHash);
if (!ENTRY_IS_LIVE(entry)) {
/* Initialize the entry, indicating that it's no longer free. */
METER(mStats.mAddMisses++);
if (ENTRY_IS_REMOVED(entry)) {
METER(mStats.mAddOverRemoved++);
mRemovedCount--;
keyHash |= COLLISION_FLAG;
}
@@ -649,12 +616,7 @@ PLDHashTable::Add(const void* aKey, const mozilla::fallible_t&)
entry->mKeyHash = keyHash;
mEntryCount++;
}
METER(else {
mStats.mAddHits++;
});
exit:
DECREMENT_RECURSION_LEVEL(this);
return entry;
}
@@ -682,30 +644,24 @@ PLDHashTable::Add(const void* aKey)
MOZ_ALWAYS_INLINE void
PLDHashTable::Remove(const void* aKey)
{
MOZ_ASSERT(mRecursionLevel == 0);
INCREMENT_RECURSION_LEVEL(this);
#ifdef DEBUG
AutoWriteOp op(mChecker);
#endif
PLDHashEntryHdr* entry =
mEntryStore ? SearchTable<ForSearchOrRemove>(aKey, ComputeKeyHash(aKey))
: nullptr;
if (entry) {
/* Clear this entry and mark it as "removed". */
METER(mStats.mRemoveHits++);
PL_DHashTableRawRemove(this, entry);
/* Shrink if alpha is <= .25 and the table isn't too small already. */
uint32_t capacity = Capacity();
if (capacity > PL_DHASH_MIN_CAPACITY &&
mEntryCount <= MinLoad(capacity)) {
METER(mStats.mShrinks++);
(void) ChangeTable(-1);
}
}
METER(else {
mStats.mRemoveMisses++;
});
DECREMENT_RECURSION_LEVEL(this);
}
PLDHashEntryHdr* PL_DHASH_FASTCALL
@@ -736,9 +692,12 @@ PL_DHashTableRemove(PLDHashTable* aTable, const void* aKey)
MOZ_ALWAYS_INLINE void
PLDHashTable::RawRemove(PLDHashEntryHdr* aEntry)
{
MOZ_ASSERT(mEntryStore);
// Unfortunately, we can only do weak checking here. That's because
// RawRemove() can be called legitimately while an Enumerate() call is
// active, which doesn't fit well into how Checker's mState variable works.
MOZ_ASSERT(mChecker.IsWritable());
MOZ_ASSERT(mRecursionLevel != IMMUTABLE_RECURSION_LEVEL);
MOZ_ASSERT(mEntryStore);
NS_ASSERTION(ENTRY_IS_LIVE(aEntry), "ENTRY_IS_LIVE(aEntry)");
@@ -749,7 +708,6 @@ PLDHashTable::RawRemove(PLDHashEntryHdr* aEntry)
MARK_ENTRY_REMOVED(aEntry);
mRemovedCount++;
} else {
METER(mStats.mRemoveFrees++);
MARK_ENTRY_FREE(aEntry);
}
mEntryCount--;
@@ -770,7 +728,6 @@ PLDHashTable::ShrinkIfAppropriate()
uint32_t capacity = Capacity();
if (mRemovedCount >= capacity >> 2 ||
(capacity > PL_DHASH_MIN_CAPACITY && mEntryCount <= MinLoad(capacity))) {
METER(mStats.mEnumShrinks++);
uint32_t log2;
BestCapacity(mEntryCount, &capacity, &log2);
@@ -785,12 +742,19 @@ PLDHashTable::ShrinkIfAppropriate()
MOZ_ALWAYS_INLINE uint32_t
PLDHashTable::Enumerate(PLDHashEnumerator aEtor, void* aArg)
{
#ifdef DEBUG
// Enumerate() can be a read operation or a write operation, depending on
// whether PL_DHASH_REMOVE is used. Assume for now it's a read; once the
// enumeration is done we'll know if that is false, and if so,
// retrospectively check it as a write.
bool wasIdleAndWritableAtStart = mChecker.IsIdle() && mChecker.IsWritable();
AutoReadOp op(mChecker);
#endif
if (!mEntryStore) {
return 0;
}
INCREMENT_RECURSION_LEVEL(this);
char* entryAddr = mEntryStore;
uint32_t capacity = Capacity();
uint32_t tableSize = capacity * mEntrySize;
@@ -810,7 +774,6 @@ PLDHashTable::Enumerate(PLDHashEnumerator aEtor, void* aArg)
if (ENTRY_IS_LIVE(entry)) {
PLDHashOperator op = aEtor(this, entry, i++, aArg);
if (op & PL_DHASH_REMOVE) {
METER(mStats.mRemoveEnums++);
PL_DHashTableRawRemove(this, entry);
didRemove = true;
}
@@ -824,7 +787,9 @@ PLDHashTable::Enumerate(PLDHashEnumerator aEtor, void* aArg)
}
}
MOZ_ASSERT(!didRemove || mRecursionLevel == 1);
// This is the retrospective check mentioned above. If we removed anything,
// then the table should have been idle and writable before we did so.
MOZ_ASSERT_IF(didRemove, wasIdleAndWritableAtStart);
// Shrink the table if appropriate. Do this only if we removed above, so
// non-removing enumerations can count on stable |mEntryStore| until the next
@@ -833,8 +798,6 @@ PLDHashTable::Enumerate(PLDHashEnumerator aEtor, void* aArg)
ShrinkIfAppropriate();
}
DECREMENT_RECURSION_LEVEL(this);
return i;
}
@@ -849,42 +812,26 @@ PL_DHashTableEnumerate(PLDHashTable* aTable, PLDHashEnumerator aEtor,
return aTable->Enumerate(aEtor, aArg);
}
struct SizeOfEntryExcludingThisArg
{
size_t total;
PLDHashSizeOfEntryExcludingThisFun sizeOfEntryExcludingThis;
MallocSizeOf mallocSizeOf;
void* arg; // the arg passed by the user
};
static PLDHashOperator
SizeOfEntryExcludingThisEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aHdr,
uint32_t aNumber, void* aArg)
{
SizeOfEntryExcludingThisArg* e = (SizeOfEntryExcludingThisArg*)aArg;
e->total += e->sizeOfEntryExcludingThis(aHdr, e->mallocSizeOf, e->arg);
return PL_DHASH_NEXT;
}
MOZ_ALWAYS_INLINE size_t
PLDHashTable::SizeOfExcludingThis(
PLDHashSizeOfEntryExcludingThisFun aSizeOfEntryExcludingThis,
MallocSizeOf aMallocSizeOf, void* aArg /* = nullptr */) const
{
#ifdef DEBUG
AutoReadOp op(mChecker);
#endif
if (!mEntryStore) {
return 0;
}
size_t n = 0;
n += aMallocSizeOf(mEntryStore);
size_t n = aMallocSizeOf(mEntryStore);
if (aSizeOfEntryExcludingThis) {
SizeOfEntryExcludingThisArg arg2 = {
0, aSizeOfEntryExcludingThis, aMallocSizeOf, aArg
};
PL_DHashTableEnumerate(const_cast<PLDHashTable*>(this),
SizeOfEntryExcludingThisEnumerator, &arg2);
n += arg2.total;
for (auto iter = Iter(); !iter.Done(); iter.Next()) {
n += aSizeOfEntryExcludingThis(iter.Get(), aMallocSizeOf, aArg);
}
}
return n;
}
@@ -922,7 +869,7 @@ PLDHashTable::Iterator::Iterator(Iterator&& aOther)
, mCurrent(aOther.mCurrent)
, mLimit(aOther.mLimit)
{
// No need to change mRecursionLevel here.
// No need to change |mChecker| here.
aOther.mTable = nullptr;
aOther.mCurrent = nullptr;
aOther.mLimit = nullptr;
@@ -933,9 +880,9 @@ PLDHashTable::Iterator::Iterator(const PLDHashTable* aTable)
, mCurrent(mTable->mEntryStore)
, mLimit(mTable->mEntryStore + mTable->Capacity() * mTable->mEntrySize)
{
// Make sure that modifications can't simultaneously happen while the
// iterator is active.
INCREMENT_RECURSION_LEVEL(mTable);
#ifdef DEBUG
mTable->mChecker.StartReadOp();
#endif
// Advance to the first live entry, or to the end if there are none.
while (IsOnNonLiveEntry()) {
@@ -945,9 +892,11 @@ PLDHashTable::Iterator::Iterator(const PLDHashTable* aTable)
PLDHashTable::Iterator::~Iterator()
{
#ifdef DEBUG
if (mTable) {
DECREMENT_RECURSION_LEVEL(mTable);
mTable->mChecker.EndReadOp();
}
#endif
}
bool
@@ -983,9 +932,12 @@ PLDHashTable::Iterator::Next()
}
PLDHashTable::RemovingIterator::RemovingIterator(RemovingIterator&& aOther)
: Iterator(mozilla::Move(aOther.mTable))
: Iterator(mozilla::Move(aOther))
, mHaveRemoved(aOther.mHaveRemoved)
{
// Do nothing with mChecker here. We treat RemovingIterator like Iterator --
// i.e. as a read operation -- until the very end. Then, if any elements have
// been removed, we temporarily treat it as a write operation.
}
PLDHashTable::RemovingIterator::RemovingIterator(PLDHashTable* aTable)
@@ -997,6 +949,10 @@ PLDHashTable::RemovingIterator::RemovingIterator(PLDHashTable* aTable)
PLDHashTable::RemovingIterator::~RemovingIterator()
{
if (mHaveRemoved) {
#ifdef DEBUG
AutoIteratorRemovalOp op(mTable->mChecker);
#endif
// Why is this cast needed? In Iterator, |mTable| is const. In
// RemovingIterator it should be non-const, but it inherits from Iterator
// so that's not possible. But it's ok because RemovingIterator's
@@ -1008,8 +964,6 @@ PLDHashTable::RemovingIterator::~RemovingIterator()
void
PLDHashTable::RemovingIterator::Remove()
{
METER(mStats.mRemoveEnums++);
// This cast is needed for the same reason as the one in the destructor.
const_cast<PLDHashTable*>(mTable)->RawRemove(Get());
mHaveRemoved = true;
@@ -1019,7 +973,7 @@ PLDHashTable::RemovingIterator::Remove()
MOZ_ALWAYS_INLINE void
PLDHashTable::MarkImmutable()
{
mRecursionLevel = IMMUTABLE_RECURSION_LEVEL;
mChecker.SetNonWritable();
}
void
@@ -1029,114 +983,3 @@ PL_DHashMarkTableImmutable(PLDHashTable* aTable)
}
#endif
#ifdef PL_DHASHMETER
#include <math.h>
void
PLDHashTable::DumpMeter(PLDHashEnumerator aDump, FILE* aFp)
{
PLDHashNumber hash1, hash2, maxChainHash1, maxChainHash2;
double sqsum, mean, variance, sigma;
PLDHashEntryHdr* entry;
char* entryAddr = mEntryStore;
int sizeLog2 = PL_DHASH_BITS - mHashShift;
uint32_t capacity = Capacity();
uint32_t sizeMask = (1u << sizeLog2) - 1;
uint32_t chainCount = 0, maxChainLen = 0;
hash2 = 0;
sqsum = 0;
MOZ_ASSERT_IF(capacity > 0, mEntryStore);
for (uint32_t i = 0; i < capacity; i++) {
entry = (PLDHashEntryHdr*)entryAddr;
entryAddr += mEntrySize;
if (!ENTRY_IS_LIVE(entry)) {
continue;
}
hash1 = HASH1(entry->mKeyHash & ~COLLISION_FLAG, mHashShift);
PLDHashNumber saveHash1 = hash1;
PLDHashEntryHdr* probe = ADDRESS_ENTRY(this, hash1);
uint32_t chainLen = 1;
if (probe == entry) {
/* Start of a (possibly unit-length) chain. */
chainCount++;
} else {
hash2 = HASH2(entry->mKeyHash & ~COLLISION_FLAG, sizeLog2, mHashShift);
do {
chainLen++;
hash1 -= hash2;
hash1 &= sizeMask;
probe = ADDRESS_ENTRY(this, hash1);
} while (probe != entry);
}
sqsum += chainLen * chainLen;
if (chainLen > maxChainLen) {
maxChainLen = chainLen;
maxChainHash1 = saveHash1;
maxChainHash2 = hash2;
}
}
if (mEntryCount && chainCount) {
mean = (double)mEntryCount / chainCount;
variance = chainCount * sqsum - mEntryCount * mEntryCount;
if (variance < 0 || chainCount == 1) {
variance = 0;
} else {
variance /= chainCount * (chainCount - 1);
}
sigma = sqrt(variance);
} else {
mean = sigma = 0;
}
fprintf(aFp, "Double hashing statistics:\n");
fprintf(aFp, " capacity (in entries): %u\n", Capacity());
fprintf(aFp, " number of entries: %u\n", mEntryCount);
fprintf(aFp, " number of removed entries: %u\n", mRemovedCount);
fprintf(aFp, " number of searches: %u\n", mStats.mSearches);
fprintf(aFp, " number of hits: %u\n", mStats.mHits);
fprintf(aFp, " number of misses: %u\n", mStats.mMisses);
fprintf(aFp, " mean steps per search: %g\n",
mStats.mSearches ? (double)mStats.mSteps / mStats.mSearches : 0.);
fprintf(aFp, " mean hash chain length: %g\n", mean);
fprintf(aFp, " standard deviation: %g\n", sigma);
fprintf(aFp, " maximum hash chain length: %u\n", maxChainLen);
fprintf(aFp, " number of searches: %u\n", mStats.mSearches);
fprintf(aFp, " adds that made a new entry: %u\n", mStats.mAddMisses);
fprintf(aFp, "adds that recycled removeds: %u\n", mStats.mAddOverRemoved);
fprintf(aFp, " adds that found an entry: %u\n", mStats.mAddHits);
fprintf(aFp, " add failures: %u\n", mStats.mAddFailures);
fprintf(aFp, " useful removes: %u\n", mStats.mRemoveHits);
fprintf(aFp, " useless removes: %u\n", mStats.mRemoveMisses);
fprintf(aFp, "removes that freed an entry: %u\n", mStats.mRemoveFrees);
fprintf(aFp, " removes while enumerating: %u\n", mStats.mRemoveEnums);
fprintf(aFp, " number of grows: %u\n", mStats.mGrows);
fprintf(aFp, " number of shrinks: %u\n", mStats.mShrinks);
fprintf(aFp, " number of compresses: %u\n", mStats.mCompresses);
fprintf(aFp, "number of enumerate shrinks: %u\n", mStats.mEnumShrinks);
if (aDump && maxChainLen && hash2) {
fputs("Maximum hash chain:\n", aFp);
hash1 = maxChainHash1;
hash2 = maxChainHash2;
entry = ADDRESS_ENTRY(this, hash1);
uint32_t i = 0;
do {
if (aDump(this, entry, i++, aFp) != PL_DHASH_NEXT) {
break;
}
hash1 -= hash2;
hash1 &= sizeMask;
entry = ADDRESS_ENTRY(this, hash1);
} while (!EntryIsFree(entry));
}
}
void
PL_DHashTableDumpMeter(PLDHashTable* aTable, PLDHashEnumerator aDump, FILE* aFp)
{
aTable->DumpMeter(aDump, aFp);
}
#endif /* PL_DHASHMETER */
+150 -44
View File
@@ -17,10 +17,6 @@
#include "mozilla/Types.h"
#include "nscore.h"
#ifdef PL_DHASHMETER
#include <stdio.h>
#endif
#if defined(__GNUC__) && defined(__i386__)
#define PL_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall))
#elif defined(XP_WIN)
@@ -144,6 +140,153 @@ typedef PLDHashOperator (*PLDHashEnumerator)(PLDHashTable* aTable,
typedef size_t (*PLDHashSizeOfEntryExcludingThisFun)(
PLDHashEntryHdr* aHdr, mozilla::MallocSizeOf aMallocSizeOf, void* aArg);
#ifdef DEBUG
// This class does three kinds of checking:
//
// - that calls to one of |mOps| or to an enumerator do not cause re-entry into
// the table in an unsafe way;
//
// - that multiple threads do not access the table in an unsafe way;
//
// - that a table marked as immutable is not modified.
//
// "Safe" here means that multiple concurrent read operations are ok, but a
// write operation (i.e. one that can cause the entry storage to be reallocated
// or destroyed) cannot safely run concurrently with another read or write
// operation. This meaning of "safe" is only partial; for example, it does not
// cover whether a single entry in the table is modified by two separate
// threads. (Doing such checking would be much harder.)
//
// It does this with two variables:
//
// - mState, which embodies a tri-stage tagged union with the following
// variants:
// - Idle
// - Read(n), where 'n' is the number of concurrent read operations
// - Write
//
// - mIsWritable, which indicates if the table is mutable.
//
class Checker
{
public:
MOZ_CONSTEXPR Checker() : mState(kIdle), mIsWritable(1) {}
Checker& operator=(Checker&& aOther) {
// Atomic<> doesn't have an |operator=(Atomic<>&&)|.
mState = uint32_t(aOther.mState);
mIsWritable = uint32_t(aOther.mIsWritable);
aOther.mState = kIdle;
return *this;
}
static bool IsIdle(uint32_t aState) { return aState == kIdle; }
static bool IsRead(uint32_t aState) { return kRead1 <= aState &&
aState <= kReadMax; }
static bool IsRead1(uint32_t aState) { return aState == kRead1; }
static bool IsWrite(uint32_t aState) { return aState == kWrite; }
bool IsIdle() const { return mState == kIdle; }
bool IsWritable() const { return !!mIsWritable; }
void SetNonWritable() { mIsWritable = 0; }
// NOTE: the obvious way to implement these functions is to (a) check
// |mState| is reasonable, and then (b) update |mState|. But the lack of
// atomicity in such an implementation can cause problems if we get unlucky
// thread interleaving between (a) and (b).
//
// So instead for |mState| we are careful to (a) first get |mState|'s old
// value and assign it a new value in single atomic operation, and only then
// (b) check the old value was reasonable. This ensures we don't have
// interleaving problems.
//
// For |mIsWritable| we don't need to be as careful because it can only in
// transition in one direction (from writable to non-writable).
void StartReadOp()
{
uint32_t oldState = mState++; // this is an atomic increment
MOZ_ASSERT(IsIdle(oldState) || IsRead(oldState));
MOZ_ASSERT(oldState < kReadMax); // check for overflow
}
void EndReadOp()
{
uint32_t oldState = mState--; // this is an atomic decrement
MOZ_ASSERT(IsRead(oldState));
}
void StartWriteOp()
{
MOZ_ASSERT(IsWritable());
uint32_t oldState = mState.exchange(kWrite);
MOZ_ASSERT(IsIdle(oldState));
}
void EndWriteOp()
{
// Check again that the table is writable, in case it was marked as
// non-writable just after the IsWritable() assertion in StartWriteOp()
// occurred.
MOZ_ASSERT(IsWritable());
uint32_t oldState = mState.exchange(kIdle);
MOZ_ASSERT(IsWrite(oldState));
}
void StartIteratorRemovalOp()
{
// When doing removals at the end of iteration, we go from Read1 state to
// Write and then back.
MOZ_ASSERT(IsWritable());
uint32_t oldState = mState.exchange(kWrite);
MOZ_ASSERT(IsRead1(oldState));
}
void EndIteratorRemovalOp()
{
// Check again that the table is writable, in case it was marked as
// non-writable just after the IsWritable() assertion in
// StartIteratorRemovalOp() occurred.
MOZ_ASSERT(IsWritable());
uint32_t oldState = mState.exchange(kRead1);
MOZ_ASSERT(IsWrite(oldState));
}
void StartDestructorOp()
{
// A destructor op is like a write, but the table doesn't need to be
// writable.
uint32_t oldState = mState.exchange(kWrite);
MOZ_ASSERT(IsIdle(oldState));
}
void EndDestructorOp()
{
uint32_t oldState = mState.exchange(kIdle);
MOZ_ASSERT(IsWrite(oldState));
}
private:
// Things of note about the representation of |mState|.
// - The values between kRead1..kReadMax represent valid Read(n) values.
// - kIdle and kRead1 are deliberately chosen so that incrementing the -
// former gives the latter.
// - 9999 concurrent readers should be enough for anybody.
static const uint32_t kIdle = 0;
static const uint32_t kRead1 = 1;
static const uint32_t kReadMax = 9999;
static const uint32_t kWrite = 10000;
mutable mozilla::Atomic<uint32_t> mState;
mutable mozilla::Atomic<uint32_t> mIsWritable;
};
#endif
/*
* A PLDHashTable may be allocated on the stack or within another structure or
* class. No entry storage is allocated until the first element is added. This
@@ -169,36 +312,9 @@ private:
uint32_t mRemovedCount; /* removed entry sentinels in table */
uint32_t mGeneration; /* entry storage generation number */
char* mEntryStore; /* entry storage; allocated lazily */
#ifdef PL_DHASHMETER
struct PLDHashStats
{
uint32_t mSearches; /* total number of table searches */
uint32_t mSteps; /* hash chain links traversed */
uint32_t mHits; /* searches that found key */
uint32_t mMisses; /* searches that didn't find key */
uint32_t mSearches; /* number of Search() calls */
uint32_t mAddMisses; /* adds that miss, and do work */
uint32_t mAddOverRemoved;/* adds that recycled a removed entry */
uint32_t mAddHits; /* adds that hit an existing entry */
uint32_t mAddFailures; /* out-of-memory during add growth */
uint32_t mRemoveHits; /* removes that hit, and do work */
uint32_t mRemoveMisses; /* useless removes that miss */
uint32_t mRemoveFrees; /* removes that freed entry directly */
uint32_t mRemoveEnums; /* removes done by Enumerate */
uint32_t mGrows; /* table expansions */
uint32_t mShrinks; /* table contractions */
uint32_t mCompresses; /* table compressions */
uint32_t mEnumShrinks; /* contractions after Enumerate */
} mStats;
#endif
#ifdef DEBUG
// We use an atomic counter here so that the various ++/-- operations can't
// get corrupted when a table is shared between threads. The associated
// assertions should in no way be taken to mean that thread safety is being
// validated! Proper synchronization and thread safety assertions must be
// employed by any consumers.
mutable mozilla::Atomic<uint32_t> mRecursionLevel;
mutable Checker mChecker;
#endif
public:
@@ -222,7 +338,7 @@ public:
// to the destructor, which the move assignment operator does.
, mEntryStore(nullptr)
#ifdef DEBUG
, mRecursionLevel(0)
, mChecker()
#endif
{
*this = mozilla::Move(aOther);
@@ -288,10 +404,6 @@ public:
void ClearEntryStub(PLDHashEntryHdr* aEntry);
#ifdef PL_DHASHMETER
void DumpMeter(PLDHashEnumerator aDump, FILE* aFp);
#endif
// This is an iterator for PLDHashtable. It is not safe to modify the
// table while it is being iterated over; on debug builds, attempting to do
// so will result in an assertion failure.
@@ -560,8 +672,7 @@ PL_DHashTableRemove(PLDHashTable* aTable, const void* aKey);
* NB: this is a "raw" or low-level routine, intended to be used only where
* the inefficiency of a full PL_DHashTableRemove (which rehashes in order
* to find the entry given its key) is not tolerable. This function does not
* shrink the table if it is underloaded. It does not update mStats #ifdef
* PL_DHASHMETER, either.
* shrink the table if it is underloaded.
*/
void PL_DHashTableRawRemove(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
@@ -606,9 +717,4 @@ size_t PL_DHashTableSizeOfIncludingThis(
void PL_DHashMarkTableImmutable(PLDHashTable* aTable);
#endif
#ifdef PL_DHASHMETER
void PL_DHashTableDumpMeter(PLDHashTable* aTable,
PLDHashEnumerator aDump, FILE* aFp);
#endif
#endif /* pldhash_h___ */
@@ -4,15 +4,13 @@
* 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/. */
#include <stdio.h>
#include "pldhash.h"
#include "gtest/gtest.h"
// This test mostly focuses on edge cases. But more coverage of normal
// operations wouldn't be a bad thing.
namespace TestPLDHash {
static bool test_pldhash_Init_capacity_ok()
TEST(PLDHashTableTest, InitCapacityOk)
{
// Try the largest allowed capacity. With PL_DHASH_MAX_CAPACITY==1<<26, this
// would allocate (if we added an element) 0.5GB of entry store on 32-bit
@@ -29,11 +27,9 @@ static bool test_pldhash_Init_capacity_ok()
//
PLDHashTable t(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub),
PL_DHASH_MAX_INITIAL_LENGTH);
return true;
}
static bool test_pldhash_lazy_storage()
TEST(PLDHashTableTest, LazyStorage)
{
PLDHashTable t(PL_DHashGetStubOps(), sizeof(PLDHashEntryStub));
@@ -41,48 +37,28 @@ static bool test_pldhash_lazy_storage()
// operations work appropriately when the table is empty and the storage
// hasn't yet been allocated.
if (t.Capacity() != 0) {
return false;
}
ASSERT_EQ(t.Capacity(), 0u);
ASSERT_EQ(t.EntrySize(), sizeof(PLDHashEntryStub));
ASSERT_EQ(t.EntryCount(), 0u);
ASSERT_EQ(t.Generation(), 0u);
if (t.EntrySize() != sizeof(PLDHashEntryStub)) {
return false;
}
if (t.EntryCount() != 0) {
return false;
}
if (t.Generation() != 0) {
return false;
}
if (PL_DHashTableSearch(&t, (const void*)1)) {
return false; // search succeeded?
}
ASSERT_TRUE(!PL_DHashTableSearch(&t, (const void*)1));
// No result to check here, but call it to make sure it doesn't crash.
PL_DHashTableRemove(&t, (const void*)2);
// Using a null |enumerator| should be fine because it shouldn't be called
// for an empty table.
PLDHashEnumerator enumerator = nullptr;
if (PL_DHashTableEnumerate(&t, enumerator, nullptr) != 0) {
return false; // enumeration count is non-zero?
for (auto iter = t.Iter(); !iter.Done(); iter.Next()) {
ASSERT_TRUE(false); // shouldn't hit this on an empty table
}
for (auto iter = t.Iter(); !iter.Done(); iter.Get()) {
return false; // shouldn't hit this on an empty table
for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
ASSERT_TRUE(false); // shouldn't hit this on an empty table
}
// Using a null |mallocSizeOf| should be fine because it shouldn't be called
// for an empty table.
mozilla::MallocSizeOf mallocSizeOf = nullptr;
if (PL_DHashTableSizeOfExcludingThis(&t, nullptr, mallocSizeOf) != 0) {
return false; // size is non-zero?
}
return true;
ASSERT_EQ(PL_DHashTableSizeOfExcludingThis(&t, nullptr, mallocSizeOf), 0u);
}
// A trivial hash function is good enough here. It's also super-fast for
@@ -109,7 +85,7 @@ static const PLDHashTableOps trivialOps = {
TrivialInitEntry
};
static bool test_pldhash_move_semantics()
TEST(PLDHashTableTest, MoveSemantics)
{
PLDHashTable t1(&trivialOps, sizeof(PLDHashEntryStub));
PL_DHashTableAdd(&t1, (const void*)88);
@@ -138,54 +114,38 @@ static bool test_pldhash_move_semantics()
PLDHashTable t9(&trivialOps, sizeof(PLDHashEntryStub));
PL_DHashTableAdd(&t9, (const void*)88);
PLDHashTable t10(mozilla::Move(t9)); // new table constructed with inited
return true;
}
static bool test_pldhash_Clear()
TEST(PLDHashTableTest, Clear)
{
PLDHashTable t1(&trivialOps, sizeof(PLDHashEntryStub));
t1.Clear();
if (t1.EntryCount() != 0) {
return false;
}
ASSERT_EQ(t1.EntryCount(), 0u);
t1.ClearAndPrepareForLength(100);
if (t1.EntryCount() != 0) {
return false;
}
ASSERT_EQ(t1.EntryCount(), 0u);
PL_DHashTableAdd(&t1, (const void*)77);
PL_DHashTableAdd(&t1, (const void*)88);
PL_DHashTableAdd(&t1, (const void*)99);
if (t1.EntryCount() != 3) {
return false;
}
ASSERT_EQ(t1.EntryCount(), 3u);
t1.Clear();
if (t1.EntryCount() != 0) {
return false;
}
ASSERT_EQ(t1.EntryCount(), 0u);
PL_DHashTableAdd(&t1, (const void*)55);
PL_DHashTableAdd(&t1, (const void*)66);
PL_DHashTableAdd(&t1, (const void*)77);
PL_DHashTableAdd(&t1, (const void*)88);
PL_DHashTableAdd(&t1, (const void*)99);
if (t1.EntryCount() != 5) {
return false;
}
ASSERT_EQ(t1.EntryCount(), 5u);
t1.ClearAndPrepareForLength(8192);
if (t1.EntryCount() != 0) {
return false;
}
return true;
ASSERT_EQ(t1.EntryCount(), 0u);
}
static bool test_pldhash_Iterator()
TEST(PLDHashTableIterator, Iterator)
{
PLDHashTable t(&trivialOps, sizeof(PLDHashEntryStub));
@@ -200,7 +160,7 @@ static bool test_pldhash_Iterator()
// Iterate through the empty table.
for (PLDHashTable::Iterator iter(&t); !iter.Done(); iter.Next()) {
(void) iter.Get();
return false; // shouldn't hit this
ASSERT_TRUE(false); // shouldn't hit this
}
// Add three entries.
@@ -224,14 +184,10 @@ static bool test_pldhash_Iterator()
}
n++;
}
if (!saw77 || !saw88 || !saw99 || n != 3) {
return false;
}
return true;
ASSERT_TRUE(saw77 && saw88 && saw99 && n == 3);
}
static bool test_pldhash_RemovingIterator()
TEST(PLDHashTableTest, RemovingIterator)
{
PLDHashTable t(&trivialOps, sizeof(PLDHashEntryStub));
@@ -239,8 +195,8 @@ static bool test_pldhash_RemovingIterator()
// elision, compilers might optimize away move constructor calls for normal
// iterator use.
{
PLDHashTable::Iterator iter1(&t);
PLDHashTable::Iterator iter2(mozilla::Move(iter1));
PLDHashTable::RemovingIterator iter1(&t);
PLDHashTable::RemovingIterator iter2(mozilla::Move(iter1));
}
// First, we insert 64 items, which results in a capacity of 128, and a load
@@ -248,18 +204,16 @@ static bool test_pldhash_RemovingIterator()
for (intptr_t i = 0; i < 64; i++) {
PL_DHashTableAdd(&t, (const void*)i);
}
if (t.EntryCount() != 64 || t.Capacity() != 128) {
return false;
}
ASSERT_EQ(t.EntryCount(), 64u);
ASSERT_EQ(t.Capacity(), 128u);
// The first removing iterator does no removing; capacity and entry count are
// unchanged.
for (PLDHashTable::RemovingIterator iter(&t); !iter.Done(); iter.Next()) {
(void) iter.Get();
}
if (t.EntryCount() != 64 || t.Capacity() != 128) {
return false;
}
ASSERT_EQ(t.EntryCount(), 64u);
ASSERT_EQ(t.Capacity(), 128u);
// The second removing iterator removes 16 items. This reduces the load
// factor to 37.5% (48 / 128), which isn't low enough to shrink the table.
@@ -269,9 +223,8 @@ static bool test_pldhash_RemovingIterator()
iter.Remove();
}
}
if (t.EntryCount() != 48 || t.Capacity() != 128) {
return false;
}
ASSERT_EQ(t.EntryCount(), 48u);
ASSERT_EQ(t.Capacity(), 128u);
// The third removing iterator removes another 16 items. This reduces
// the load factor to 25% (32 / 128), so the table is shrunk.
@@ -281,25 +234,22 @@ static bool test_pldhash_RemovingIterator()
iter.Remove();
}
}
if (t.EntryCount() != 32 || t.Capacity() != 64) {
return false;
}
ASSERT_EQ(t.EntryCount(), 32u);
ASSERT_EQ(t.Capacity(), 64u);
// The fourth removing iterator removes all remaining items. This reduces
// the capacity to the minimum.
for (auto iter = t.RemovingIter(); !iter.Done(); iter.Next()) {
iter.Remove();
}
if (t.EntryCount() != 0 || t.Capacity() != PL_DHASH_MIN_CAPACITY) {
return false;
}
return true;
ASSERT_EQ(t.EntryCount(), 0u);
ASSERT_EQ(t.Capacity(), unsigned(PL_DHASH_MIN_CAPACITY));
}
// See bug 931062, we skip this test on Android due to OOM.
// See bug 931062, we skip this test on Android due to OOM. Also, it's slow,
// and so should always be last.
#ifndef MOZ_WIDGET_ANDROID
static bool test_pldhash_grow_to_max_capacity()
TEST(PLDHashTableTest, GrowToMaxCapacity)
{
// This is infallible.
PLDHashTable* t =
@@ -318,49 +268,10 @@ static bool test_pldhash_grow_to_max_capacity()
// MaxLoadOnGrowthFailure()).
if (numInserted != PL_DHASH_MAX_CAPACITY - (PL_DHASH_MAX_CAPACITY >> 5)) {
delete t;
return false;
ASSERT_TRUE(false);
}
delete t;
return true;
}
#endif
//----
typedef bool (*TestFunc)();
#define DECL_TEST(name) { #name, name }
static const struct Test {
const char* name;
TestFunc func;
} tests[] = {
DECL_TEST(test_pldhash_Init_capacity_ok),
DECL_TEST(test_pldhash_lazy_storage),
DECL_TEST(test_pldhash_move_semantics),
DECL_TEST(test_pldhash_Clear),
DECL_TEST(test_pldhash_Iterator),
DECL_TEST(test_pldhash_RemovingIterator),
// See bug 931062, we skip this test on Android due to OOM. Also, it's slow,
// and so should always be last.
#ifndef MOZ_WIDGET_ANDROID
DECL_TEST(test_pldhash_grow_to_max_capacity),
#endif
{ nullptr, nullptr }
};
} // namespace TestPLDHash
using namespace TestPLDHash;
int main(int argc, char *argv[])
{
bool success = true;
for (const Test* t = tests; t->name != nullptr; ++t) {
bool test_result = t->func();
printf("%35s : %s\n", t->name, test_result ? "SUCCESS" : "FAILURE");
if (!test_result)
success = false;
}
return success ? 0 : -1;
}
+1
View File
@@ -11,6 +11,7 @@ UNIFIED_SOURCES += [
'TestEncoding.cpp',
'TestExpirationTracker.cpp',
'TestPipes.cpp',
'TestPLDHash.cpp',
'TestPriorityQueue.cpp',
'TestSnappyStreams.cpp',
'TestStorageStream.cpp',
-1
View File
@@ -63,7 +63,6 @@ GeckoCppUnitTests([
'TestObserverArray',
'TestObserverService',
'TestPipe',
'TestPLDHash',
'TestStringAPI',
'TestTArray',
'TestTextFormatter',