Reference-count CSS compressed data blocks and make them immutable when their reference count is above 1. (Bug 522595) r=bzbarsky

This commit is contained in:
L. David Baron 2009-12-11 08:13:19 -08:00
parent ab7ecc4372
commit 9c2df8e0c0
4 changed files with 88 additions and 51 deletions

View File

@ -410,7 +410,7 @@ nsCSSCompressedDataBlock::StorageFor(nsCSSProperty aProperty) const
return nsnull;
}
nsCSSCompressedDataBlock*
already_AddRefed<nsCSSCompressedDataBlock>
nsCSSCompressedDataBlock::Clone() const
{
const char *cursor = Block(), *cursor_end = BlockEnd();
@ -489,6 +489,8 @@ nsCSSCompressedDataBlock::Clone() const
result->mBlockEnd = result_cursor;
result->mStyleBits = mStyleBits;
NS_ASSERTION(result->DataSize() == DataSize(), "wrong size");
result->AddRef();
return result;
}
@ -544,13 +546,15 @@ nsCSSCompressedDataBlock::Destroy()
delete this;
}
/* static */ nsCSSCompressedDataBlock*
/* static */ already_AddRefed<nsCSSCompressedDataBlock>
nsCSSCompressedDataBlock::CreateEmptyBlock()
{
nsCSSCompressedDataBlock *result = new(0) nsCSSCompressedDataBlock();
if (!result)
return nsnull;
result->mBlockEnd = result->Block();
result->AddRef();
return result;
}
@ -584,10 +588,22 @@ nsCSSExpandedDataBlock::kOffsetTable[eCSSProperty_COUNT_no_shorthands] = {
};
void
nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock,
nsCSSExpandedDataBlock::DoExpand(nsRefPtr<nsCSSCompressedDataBlock> *aBlock,
PRBool aImportant)
{
NS_PRECONDITION(aBlock, "unexpected null block");
NS_PRECONDITION(*aBlock, "unexpected null block");
if (!(*aBlock)->IsMutable()) {
// FIXME (maybe): We really don't need to clone the block
// itself, just all the data inside it.
*aBlock = (*aBlock)->Clone();
if (!aBlock) {
// Not much we can do; just lose the properties.
NS_WARNING("out of memory");
return;
}
NS_ABORT_IF_FALSE((*aBlock)->IsMutable(), "we just cloned it");
}
/*
* Save needless copying and allocation by copying the memory
@ -595,8 +611,8 @@ nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock,
* then, to avoid destructors, deleting the compressed block by
* calling |delete| instead of using its |Destroy| method.
*/
const char* cursor = aBlock->Block();
const char* cursor_end = aBlock->BlockEnd();
const char* cursor = (*aBlock)->Block();
const char* cursor_end = (*aBlock)->BlockEnd();
while (cursor < cursor_end) {
nsCSSProperty iProp = PropertyAtCursor(cursor);
NS_ASSERTION(0 <= iProp && iProp < eCSSProperty_COUNT_no_shorthands,
@ -663,21 +679,21 @@ nsCSSExpandedDataBlock::DoExpand(nsCSSCompressedDataBlock *aBlock,
}
NS_ASSERTION(cursor == cursor_end, "inconsistent data");
delete aBlock;
NS_ASSERTION((*aBlock)->mRefCnt == 1, "unexpected reference count");
delete aBlock->forget().get();
}
void
nsCSSExpandedDataBlock::Expand(nsCSSCompressedDataBlock **aNormalBlock,
nsCSSCompressedDataBlock **aImportantBlock)
nsCSSExpandedDataBlock::Expand(
nsRefPtr<nsCSSCompressedDataBlock> *aNormalBlock,
nsRefPtr<nsCSSCompressedDataBlock> *aImportantBlock)
{
NS_PRECONDITION(*aNormalBlock, "unexpected null block");
AssertInitialState();
DoExpand(*aNormalBlock, PR_FALSE);
*aNormalBlock = nsnull;
DoExpand(aNormalBlock, PR_FALSE);
if (*aImportantBlock) {
DoExpand(*aImportantBlock, PR_TRUE);
*aImportantBlock = nsnull;
DoExpand(aImportantBlock, PR_TRUE);
}
}
@ -749,7 +765,7 @@ void
nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock,
nsCSSCompressedDataBlock **aImportantBlock)
{
nsCSSCompressedDataBlock *result_normal, *result_important;
nsRefPtr<nsCSSCompressedDataBlock> result_normal, result_important;
char *cursor_normal, *cursor_important;
ComputeSizeResult size = ComputeSize();
@ -765,7 +781,6 @@ nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock,
if (size.important != 0) {
result_important = new(size.important) nsCSSCompressedDataBlock();
if (!result_important) {
delete result_normal;
*aNormalBlock = nsnull;
*aImportantBlock = nsnull;
return;
@ -861,8 +876,8 @@ nsCSSExpandedDataBlock::Compress(nsCSSCompressedDataBlock **aNormalBlock,
ClearSets();
AssertInitialState();
*aNormalBlock = result_normal;
*aImportantBlock = result_important;
result_normal.forget(aNormalBlock);
result_important.forget(aImportantBlock);
}
void

View File

@ -45,6 +45,7 @@
#include "nsCSSStruct.h"
#include "nsCSSProps.h"
#include "nsCSSPropertySet.h"
#include "nsAutoPtr.h"
struct nsRuleData;
@ -56,6 +57,11 @@ class nsCSSDeclaration;
* property-value data for a CSS declaration block (which we misname a
* |nsCSSDeclaration|). Mutation is accomplished through
* |nsCSSExpandedDataBlock| or in some cases via direct slot access.
*
* Mutation is forbidden when the reference count is greater than one,
* since once a style rule has used a compressed data block, mutation of
* that block is forbidden, and any declarations that want to mutate it
* need to clone it first.
*/
class nsCSSCompressedDataBlock {
public:
@ -119,21 +125,36 @@ public:
/**
* Clone this block, or return null on out-of-memory.
*/
nsCSSCompressedDataBlock* Clone() const;
/**
* Delete all the data stored in this block, and the block itself.
*/
void Destroy();
already_AddRefed<nsCSSCompressedDataBlock> Clone() const;
/**
* Create a new nsCSSCompressedDataBlock holding no declarations.
*/
static nsCSSCompressedDataBlock* CreateEmptyBlock();
static already_AddRefed<nsCSSCompressedDataBlock> CreateEmptyBlock();
void AddRef() {
NS_ASSERTION(mRefCnt == 0 || mRefCnt == 1,
"unexpected reference count");
++mRefCnt;
}
void Release() {
NS_ASSERTION(mRefCnt == 1 || mRefCnt == 2,
"unexpected reference count");
if (--mRefCnt == 0) {
Destroy();
}
}
PRBool IsMutable() const {
NS_ASSERTION(mRefCnt == 1 || mRefCnt == 2,
"unexpected reference count");
return mRefCnt < 2;
}
private:
PRInt32 mStyleBits; // the structs for which we have data, according to
// |nsCachedStyleData::GetBitForSID|.
nsAutoRefCnt mRefCnt;
enum { block_chars = 4 }; // put 4 chars in the definition of the class
// to ensure size not inflated by alignment
@ -150,6 +171,11 @@ private:
// |Expand|) can delete compressed data blocks.
~nsCSSCompressedDataBlock() { }
/**
* Delete all the data stored in this block, and the block itself.
*/
void Destroy();
char* mBlockEnd; // the byte after the last valid byte
char mBlock_[block_chars]; // must be the last member!
@ -162,6 +188,7 @@ private:
// Direct slot access to our values. See StorageFor above. Can
// return null. Must not be called for shorthand properties.
void* SlotForValue(nsCSSProperty aProperty) {
NS_ABORT_IF_FALSE(IsMutable(), "must be mutable");
return const_cast<void*>(StorageFor(aProperty));
}
};
@ -199,13 +226,13 @@ public:
* expanded block. The state of this expanded block must be clear
* beforehand.
*
* The compressed block passed in IS DESTROYED by this method and
* The compressed block passed in IS RELEASED by this method and
* set to null, and thus cannot be used again. (This is necessary
* because ownership of sub-objects is transferred to the expanded
* block.)
* block in many cases.)
*/
void Expand(nsCSSCompressedDataBlock **aNormalBlock,
nsCSSCompressedDataBlock **aImportantBlock);
void Expand(nsRefPtr<nsCSSCompressedDataBlock> *aNormalBlock,
nsRefPtr<nsCSSCompressedDataBlock> *aImportantBlock);
/**
* Allocate a new compressed block and transfer all of the state
@ -242,7 +269,8 @@ private:
};
ComputeSizeResult ComputeSize();
void DoExpand(nsCSSCompressedDataBlock *aBlock, PRBool aImportant);
void DoExpand(nsRefPtr<nsCSSCompressedDataBlock> *aBlock,
PRBool aImportant);
#ifdef DEBUG
void DoAssertInitialState();

View File

@ -62,8 +62,6 @@
#include "nsCOMPtr.h"
nsCSSDeclaration::nsCSSDeclaration()
: mData(nsnull),
mImportantData(nsnull)
{
// check that we can fit all the CSS properties into a PRUint8
// for the mOrder array - if not, might need to use PRUint16!
@ -74,22 +72,17 @@ nsCSSDeclaration::nsCSSDeclaration()
nsCSSDeclaration::nsCSSDeclaration(const nsCSSDeclaration& aCopy)
: mOrder(aCopy.mOrder),
mData(aCopy.mData ? aCopy.mData->Clone() : nsnull),
mImportantData(aCopy.mImportantData ? aCopy.mImportantData->Clone()
: nsnull)
mData(aCopy.mData ? aCopy.mData->Clone()
: already_AddRefed<nsCSSCompressedDataBlock>(nsnull)),
mImportantData(aCopy.mImportantData
? aCopy.mImportantData->Clone()
: already_AddRefed<nsCSSCompressedDataBlock>(nsnull))
{
MOZ_COUNT_CTOR(nsCSSDeclaration);
}
nsCSSDeclaration::~nsCSSDeclaration(void)
{
if (mData) {
mData->Destroy();
}
if (mImportantData) {
mImportantData->Destroy();
}
MOZ_COUNT_DTOR(nsCSSDeclaration);
}
@ -108,7 +101,7 @@ nsresult
nsCSSDeclaration::RemoveProperty(nsCSSProperty aProperty)
{
nsCSSExpandedDataBlock data;
data.Expand(&mData, &mImportantData);
ExpandTo(&data);
NS_ASSERTION(!mData && !mImportantData, "Expand didn't null things out");
if (nsCSSProps::IsShorthand(aProperty)) {
@ -121,7 +114,7 @@ nsCSSDeclaration::RemoveProperty(nsCSSProperty aProperty)
mOrder.RemoveElement(aProperty);
}
data.Compress(&mData, &mImportantData);
CompressFrom(&data);
return NS_OK;
}

View File

@ -112,7 +112,8 @@ public:
void CompressFrom(nsCSSExpandedDataBlock *aExpandedData) {
NS_ASSERTION(!mData, "oops");
NS_ASSERTION(!mImportantData, "oops");
aExpandedData->Compress(&mData, &mImportantData);
aExpandedData->Compress(getter_AddRefs(mData),
getter_AddRefs(mImportantData));
aExpandedData->AssertInitialState();
}
@ -157,12 +158,8 @@ public:
* new data by a call to |CompressFrom|.
*/
void ClearData() {
mData->Destroy();
mData = nsnull;
if (mImportantData) {
mImportantData->Destroy();
mImportantData = nsnull;
}
mImportantData = nsnull;
mOrder.Clear();
}
@ -237,8 +234,12 @@ private:
private:
nsAutoTArray<PRUint8, 8> mOrder;
nsAutoRefCnt mRefCnt;
nsCSSCompressedDataBlock *mData; // never null, except while expanded
nsCSSCompressedDataBlock *mImportantData; // may be null
// never null, except while expanded
nsRefPtr<nsCSSCompressedDataBlock> mData;
// may be null
nsRefPtr<nsCSSCompressedDataBlock> mImportantData;
};
#endif /* nsCSSDeclaration_h___ */