gecko-dev/layout/style/StyleRule.cpp

1413 строки
40 KiB
C++
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2012-05-21 15:12:37 +04:00
/* 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/. */
/*
* representation of CSS style rules (selectors+declaration), CSS
* selectors, and DOM objects for style rules, selectors, and
* declarations
*/
#include "mozilla/css/StyleRule.h"
#include "mozilla/DeclarationBlockInlines.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/GroupRule.h"
#include "mozilla/css/Declaration.h"
#include "mozilla/dom/CSSStyleRuleBinding.h"
1998-11-26 04:34:53 +03:00
#include "nsIDocument.h"
1998-04-14 00:24:54 +04:00
#include "nsIAtom.h"
#include "nsString.h"
#include "nsStyleUtil.h"
#include "nsDOMCSSDeclaration.h"
#include "nsNameSpaceManager.h"
#include "nsXMLNameSpaceMap.h"
#include "nsCSSPseudoClasses.h"
#include "nsCSSAnonBoxes.h"
#include "nsTArray.h"
#include "nsContentUtils.h"
#include "nsError.h"
#include "mozAutoDocUpdate.h"
class nsIDOMCSSStyleDeclaration;
class nsIDOMCSSStyleSheet;
using namespace mozilla;
#define NS_IF_CLONE(member_) \
PR_BEGIN_MACRO \
if (member_) { \
result->member_ = member_->Clone(); \
if (!result->member_) { \
delete result; \
return nullptr; \
} \
} \
PR_END_MACRO
#define NS_IF_DELETE(ptr) \
PR_BEGIN_MACRO \
delete ptr; \
ptr = nullptr; \
PR_END_MACRO
1998-04-14 00:24:54 +04:00
/* ************************************************************************** */
1998-12-11 05:50:43 +03:00
nsAtomList::nsAtomList(nsIAtom* aAtom)
: mAtom(aAtom),
mNext(nullptr)
1998-04-14 00:24:54 +04:00
{
MOZ_COUNT_CTOR(nsAtomList);
1998-04-14 00:24:54 +04:00
}
1998-12-11 05:50:43 +03:00
nsAtomList::nsAtomList(const nsString& aAtomValue)
: mAtom(nullptr),
mNext(nullptr)
1998-12-11 05:50:43 +03:00
{
MOZ_COUNT_CTOR(nsAtomList);
mAtom = NS_Atomize(aAtomValue);
1998-12-11 05:50:43 +03:00
}
nsAtomList*
nsAtomList::Clone(bool aDeep) const
1998-12-11 05:50:43 +03:00
{
nsAtomList *result = new nsAtomList(mAtom);
if (!result)
return nullptr;
if (aDeep)
NS_CSS_CLONE_LIST_MEMBER(nsAtomList, this, mNext, result, (false));
return result;
1998-12-11 05:50:43 +03:00
}
size_t
nsAtomList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
const nsAtomList* a = this;
while (a) {
n += aMallocSizeOf(a);
// The following members aren't measured:
// - a->mAtom, because it may be shared
a = a->mNext;
}
return n;
}
1998-12-11 05:50:43 +03:00
nsAtomList::~nsAtomList(void)
{
MOZ_COUNT_DTOR(nsAtomList);
NS_CSS_DELETE_LIST_MEMBER(nsAtomList, this, mNext);
1998-12-11 05:50:43 +03:00
}
nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType)
: mType(aType),
mNext(nullptr)
{
NS_ASSERTION(!nsCSSPseudoClasses::HasStringArg(aType) &&
!nsCSSPseudoClasses::HasNthPairArg(aType),
"unexpected pseudo-class");
MOZ_COUNT_CTOR(nsPseudoClassList);
u.mMemory = nullptr;
}
nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
const char16_t* aString)
: mType(aType),
mNext(nullptr)
{
NS_ASSERTION(nsCSSPseudoClasses::HasStringArg(aType),
"unexpected pseudo-class");
NS_ASSERTION(aString, "string expected");
MOZ_COUNT_CTOR(nsPseudoClassList);
u.mString = NS_strdup(aString);
}
nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
const int32_t* aIntPair)
: mType(aType),
mNext(nullptr)
{
NS_ASSERTION(nsCSSPseudoClasses::HasNthPairArg(aType),
"unexpected pseudo-class");
NS_ASSERTION(aIntPair, "integer pair expected");
MOZ_COUNT_CTOR(nsPseudoClassList);
u.mNumbers =
static_cast<int32_t*>(nsMemory::Clone(aIntPair, sizeof(int32_t) * 2));
}
// adopts aSelectorList
nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
nsCSSSelectorList* aSelectorList)
: mType(aType),
mNext(nullptr)
{
NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType),
"unexpected pseudo-class");
NS_ASSERTION(aSelectorList, "selector list expected");
MOZ_COUNT_CTOR(nsPseudoClassList);
u.mSelectors = aSelectorList;
}
nsPseudoClassList*
nsPseudoClassList::Clone(bool aDeep) const
{
nsPseudoClassList *result;
if (!u.mMemory) {
result = new nsPseudoClassList(mType);
} else if (nsCSSPseudoClasses::HasStringArg(mType)) {
result = new nsPseudoClassList(mType, u.mString);
} else if (nsCSSPseudoClasses::HasNthPairArg(mType)) {
result = new nsPseudoClassList(mType, u.mNumbers);
} else {
NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(mType),
"unexpected pseudo-class");
// This constructor adopts its selector list argument.
result = new nsPseudoClassList(mType, u.mSelectors->Clone());
}
if (aDeep)
NS_CSS_CLONE_LIST_MEMBER(nsPseudoClassList, this, mNext, result,
(false));
return result;
}
size_t
nsPseudoClassList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
const nsPseudoClassList* p = this;
while (p) {
n += aMallocSizeOf(p);
if (!p->u.mMemory) {
// do nothing
} else if (nsCSSPseudoClasses::HasStringArg(p->mType)) {
n += aMallocSizeOf(p->u.mString);
} else if (nsCSSPseudoClasses::HasNthPairArg(p->mType)) {
n += aMallocSizeOf(p->u.mNumbers);
} else {
NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(p->mType),
"unexpected pseudo-class");
n += p->u.mSelectors->SizeOfIncludingThis(aMallocSizeOf);
}
p = p->mNext;
}
return n;
}
nsPseudoClassList::~nsPseudoClassList(void)
{
MOZ_COUNT_DTOR(nsPseudoClassList);
if (nsCSSPseudoClasses::HasSelectorListArg(mType)) {
delete u.mSelectors;
} else if (u.mMemory) {
free(u.mMemory);
}
NS_CSS_DELETE_LIST_MEMBER(nsPseudoClassList, this, mNext);
}
nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr)
: mValue(),
mNext(nullptr),
mLowercaseAttr(nullptr),
mCasedAttr(nullptr),
mNameSpace(aNameSpace),
1998-12-11 05:50:43 +03:00
mFunction(NS_ATTR_FUNC_SET),
// mValueCaseSensitivity doesn't matter; we have no value.
mValueCaseSensitivity(ValueCaseSensitivity::CaseSensitive)
1998-12-11 05:50:43 +03:00
{
MOZ_COUNT_CTOR(nsAttrSelector);
nsAutoString lowercase;
nsContentUtils::ASCIIToLower(aAttr, lowercase);
mCasedAttr = NS_Atomize(aAttr);
mLowercaseAttr = NS_Atomize(lowercase);
1998-12-11 05:50:43 +03:00
}
nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunction,
const nsString& aValue,
ValueCaseSensitivity aValueCaseSensitivity)
: mValue(aValue),
mNext(nullptr),
mLowercaseAttr(nullptr),
mCasedAttr(nullptr),
mNameSpace(aNameSpace),
1998-12-11 05:50:43 +03:00
mFunction(aFunction),
mValueCaseSensitivity(aValueCaseSensitivity)
1998-12-11 05:50:43 +03:00
{
MOZ_COUNT_CTOR(nsAttrSelector);
nsAutoString lowercase;
nsContentUtils::ASCIIToLower(aAttr, lowercase);
mCasedAttr = NS_Atomize(aAttr);
mLowercaseAttr = NS_Atomize(lowercase);
1998-12-11 05:50:43 +03:00
}
nsAttrSelector::nsAttrSelector(int32_t aNameSpace, nsIAtom* aLowercaseAttr,
nsIAtom* aCasedAttr, uint8_t aFunction,
const nsString& aValue,
ValueCaseSensitivity aValueCaseSensitivity)
: mValue(aValue),
mNext(nullptr),
mLowercaseAttr(aLowercaseAttr),
mCasedAttr(aCasedAttr),
mNameSpace(aNameSpace),
mFunction(aFunction),
mValueCaseSensitivity(aValueCaseSensitivity)
1998-12-11 05:50:43 +03:00
{
MOZ_COUNT_CTOR(nsAttrSelector);
}
nsAttrSelector*
nsAttrSelector::Clone(bool aDeep) const
{
nsAttrSelector *result =
new nsAttrSelector(mNameSpace, mLowercaseAttr, mCasedAttr,
mFunction, mValue, mValueCaseSensitivity);
if (aDeep)
NS_CSS_CLONE_LIST_MEMBER(nsAttrSelector, this, mNext, result, (false));
return result;
1998-12-11 05:50:43 +03:00
}
nsAttrSelector::~nsAttrSelector(void)
{
MOZ_COUNT_DTOR(nsAttrSelector);
NS_CSS_DELETE_LIST_MEMBER(nsAttrSelector, this, mNext);
1998-12-11 05:50:43 +03:00
}
size_t
nsAttrSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
const nsAttrSelector* p = this;
while (p) {
n += aMallocSizeOf(p);
n += p->mValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
p = p->mNext;
}
return n;
}
// -- nsCSSSelector -------------------------------
1998-12-11 05:50:43 +03:00
nsCSSSelector::nsCSSSelector(void)
: mLowercaseTag(nullptr),
mCasedTag(nullptr),
mIDList(nullptr),
mClassList(nullptr),
mPseudoClassList(nullptr),
mAttrList(nullptr),
mNegations(nullptr),
mNext(nullptr),
mNameSpace(kNameSpaceID_Unknown),
mOperator(0),
mPseudoType(CSSPseudoElementType::NotPseudo)
1998-04-14 00:24:54 +04:00
{
MOZ_COUNT_CTOR(nsCSSSelector);
1998-04-14 00:24:54 +04:00
}
nsCSSSelector*
nsCSSSelector::Clone(bool aDeepNext, bool aDeepNegations) const
1998-12-11 05:50:43 +03:00
{
nsCSSSelector *result = new nsCSSSelector();
if (!result)
return nullptr;
1998-04-14 00:24:54 +04:00
result->mNameSpace = mNameSpace;
result->mLowercaseTag = mLowercaseTag;
result->mCasedTag = mCasedTag;
result->mOperator = mOperator;
result->mPseudoType = mPseudoType;
NS_IF_CLONE(mIDList);
NS_IF_CLONE(mClassList);
NS_IF_CLONE(mPseudoClassList);
NS_IF_CLONE(mAttrList);
1998-12-11 05:50:43 +03:00
// No need to worry about multiple levels of recursion since an
// mNegations can't have an mNext.
NS_ASSERTION(!mNegations || !mNegations->mNext,
"mNegations can't have non-null mNext");
if (aDeepNegations) {
NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNegations, result,
(true, false));
}
1998-04-14 00:24:54 +04:00
if (aDeepNext) {
NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNext, result,
(false, true));
1998-04-14 00:24:54 +04:00
}
return result;
1998-04-14 00:24:54 +04:00
}
nsCSSSelector::~nsCSSSelector(void)
{
MOZ_COUNT_DTOR(nsCSSSelector);
Reset();
// No need to worry about multiple levels of recursion since an
// mNegations can't have an mNext.
NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNext);
}
1998-04-14 00:24:54 +04:00
1998-12-11 05:50:43 +03:00
void nsCSSSelector::Reset(void)
1998-04-14 00:24:54 +04:00
{
1998-12-11 05:50:43 +03:00
mNameSpace = kNameSpaceID_Unknown;
mLowercaseTag = nullptr;
mCasedTag = nullptr;
NS_IF_DELETE(mIDList);
1998-12-11 05:50:43 +03:00
NS_IF_DELETE(mClassList);
NS_IF_DELETE(mPseudoClassList);
NS_IF_DELETE(mAttrList);
// No need to worry about multiple levels of recursion since an
// mNegations can't have an mNext.
NS_ASSERTION(!mNegations || !mNegations->mNext,
"mNegations can't have non-null mNext");
NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNegations);
mOperator = char16_t(0);
1998-12-11 05:50:43 +03:00
}
void nsCSSSelector::SetNameSpace(int32_t aNameSpace)
1998-12-11 05:50:43 +03:00
{
mNameSpace = aNameSpace;
}
void nsCSSSelector::SetTag(const nsString& aTag)
1998-12-11 05:50:43 +03:00
{
if (aTag.IsEmpty()) {
mLowercaseTag = mCasedTag = nullptr;
return;
}
mCasedTag = NS_Atomize(aTag);
nsAutoString lowercase;
nsContentUtils::ASCIIToLower(aTag, lowercase);
mLowercaseTag = NS_Atomize(lowercase);
1998-12-11 05:50:43 +03:00
}
void nsCSSSelector::AddID(const nsString& aID)
1998-12-11 05:50:43 +03:00
{
if (!aID.IsEmpty()) {
nsAtomList** list = &mIDList;
while (nullptr != *list) {
list = &((*list)->mNext);
}
*list = new nsAtomList(aID);
1998-04-14 00:24:54 +04:00
}
1998-12-11 05:50:43 +03:00
}
void nsCSSSelector::AddClass(const nsString& aClass)
{
if (!aClass.IsEmpty()) {
1998-12-11 05:50:43 +03:00
nsAtomList** list = &mClassList;
while (nullptr != *list) {
1998-12-11 05:50:43 +03:00
list = &((*list)->mNext);
}
*list = new nsAtomList(aClass);
1998-04-14 00:24:54 +04:00
}
1998-12-11 05:50:43 +03:00
}
void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType)
{
AddPseudoClassInternal(new nsPseudoClassList(aType));
}
void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
const char16_t* aString)
1998-12-11 05:50:43 +03:00
{
AddPseudoClassInternal(new nsPseudoClassList(aType, aString));
}
void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
const int32_t* aIntPair)
{
AddPseudoClassInternal(new nsPseudoClassList(aType, aIntPair));
}
void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
nsCSSSelectorList* aSelectorList)
{
// Take ownership of nsCSSSelectorList instead of copying.
AddPseudoClassInternal(new nsPseudoClassList(aType, aSelectorList));
}
void nsCSSSelector::AddPseudoClassInternal(nsPseudoClassList *aPseudoClass)
{
nsPseudoClassList** list = &mPseudoClassList;
while (nullptr != *list) {
list = &((*list)->mNext);
1998-12-11 05:50:43 +03:00
}
*list = aPseudoClass;
1998-12-11 05:50:43 +03:00
}
void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr)
1998-12-11 05:50:43 +03:00
{
if (!aAttr.IsEmpty()) {
1998-12-11 05:50:43 +03:00
nsAttrSelector** list = &mAttrList;
while (nullptr != *list) {
1998-12-11 05:50:43 +03:00
list = &((*list)->mNext);
}
*list = new nsAttrSelector(aNameSpace, aAttr);
1998-12-11 05:50:43 +03:00
}
}
void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunc,
const nsString& aValue,
nsAttrSelector::ValueCaseSensitivity aCaseSensitivity)
1998-12-11 05:50:43 +03:00
{
if (!aAttr.IsEmpty()) {
1998-12-11 05:50:43 +03:00
nsAttrSelector** list = &mAttrList;
while (nullptr != *list) {
1998-12-11 05:50:43 +03:00
list = &((*list)->mNext);
}
*list = new nsAttrSelector(aNameSpace, aAttr, aFunc, aValue, aCaseSensitivity);
1998-12-11 05:50:43 +03:00
}
}
void nsCSSSelector::SetOperator(char16_t aOperator)
1998-12-11 05:50:43 +03:00
{
mOperator = aOperator;
}
int32_t nsCSSSelector::CalcWeightWithoutNegations() const
1998-12-11 05:50:43 +03:00
{
int32_t weight = 0;
1998-12-11 05:50:43 +03:00
#ifdef MOZ_XUL
MOZ_ASSERT(!(IsPseudoElement() &&
PseudoType() != CSSPseudoElementType::XULTree &&
mClassList),
"If non-XUL-tree pseudo-elements can have class selectors "
"after them, specificity calculation must be updated");
#else
MOZ_ASSERT(!(IsPseudoElement() && mClassList),
"If pseudo-elements can have class selectors "
"after them, specificity calculation must be updated");
#endif
MOZ_ASSERT(!(IsPseudoElement() && (mIDList || mAttrList)),
"If pseudo-elements can have id or attribute selectors "
"after them, specificity calculation must be updated");
if (nullptr != mCasedTag) {
1998-12-11 05:50:43 +03:00
weight += 0x000001;
1998-04-14 00:24:54 +04:00
}
nsAtomList* list = mIDList;
while (nullptr != list) {
1998-12-11 05:50:43 +03:00
weight += 0x010000;
list = list->mNext;
1998-12-11 05:50:43 +03:00
}
list = mClassList;
#ifdef MOZ_XUL
// XUL tree pseudo-elements abuse mClassList to store some private
// data; ignore that.
if (PseudoType() == CSSPseudoElementType::XULTree) {
list = nullptr;
}
#endif
while (nullptr != list) {
1998-12-11 05:50:43 +03:00
weight += 0x000100;
list = list->mNext;
}
// FIXME (bug 561154): This is incorrect for :-moz-any(), which isn't
// really a pseudo-class. In order to handle :-moz-any() correctly,
// we need to compute specificity after we match, based on which
// option we matched with (and thus also need to try the
// highest-specificity options first).
nsPseudoClassList *plist = mPseudoClassList;
while (nullptr != plist) {
1998-12-11 05:50:43 +03:00
weight += 0x000100;
plist = plist->mNext;
1998-12-11 05:50:43 +03:00
}
nsAttrSelector* attr = mAttrList;
while (nullptr != attr) {
1998-12-11 05:50:43 +03:00
weight += 0x000100;
attr = attr->mNext;
}
return weight;
}
int32_t nsCSSSelector::CalcWeight() const
{
// Loop over this selector and all its negations.
int32_t weight = 0;
for (const nsCSSSelector *n = this; n; n = n->mNegations) {
weight += n->CalcWeightWithoutNegations();
}
1998-12-11 05:50:43 +03:00
return weight;
1998-04-14 00:24:54 +04:00
}
//
// Builds the textual representation of a selector. Called by DOM 2 CSS
// StyleRule:selectorText
//
void
nsCSSSelector::ToString(nsAString& aString, CSSStyleSheet* aSheet,
bool aAppend) const
{
if (!aAppend)
aString.Truncate();
// selectors are linked from right-to-left, so the next selector in
// the linked list actually precedes this one in the resulting string
AutoTArray<const nsCSSSelector*, 8> stack;
for (const nsCSSSelector *s = this; s; s = s->mNext) {
stack.AppendElement(s);
}
while (!stack.IsEmpty()) {
uint32_t index = stack.Length() - 1;
const nsCSSSelector *s = stack.ElementAt(index);
stack.RemoveElementAt(index);
s->AppendToStringWithoutCombinators(aString, aSheet, false);
// Append the combinator, if needed.
if (!stack.IsEmpty()) {
const nsCSSSelector *next = stack.ElementAt(index - 1);
char16_t oper = s->mOperator;
if (next->IsPseudoElement()) {
NS_ASSERTION(oper == char16_t(':'),
"improperly chained pseudo element");
} else {
NS_ASSERTION(oper != char16_t(0),
"compound selector without combinator");
aString.Append(char16_t(' '));
if (oper != char16_t(' ')) {
aString.Append(oper);
aString.Append(char16_t(' '));
}
}
}
}
}
void
nsCSSSelector::AppendToStringWithoutCombinators(
nsAString& aString,
CSSStyleSheet* aSheet,
bool aUseStandardNamespacePrefixes) const
{
AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, false,
aUseStandardNamespacePrefixes);
for (const nsCSSSelector* negation = mNegations; negation;
negation = negation->mNegations) {
aString.AppendLiteral(":not(");
negation->AppendToStringWithoutCombinatorsOrNegations(
aString, aSheet, true, aUseStandardNamespacePrefixes);
aString.Append(char16_t(')'));
}
}
#ifdef DEBUG
nsCString
nsCSSSelector::RestrictedSelectorToString() const
{
MOZ_ASSERT(IsRestrictedSelector());
nsString result;
AppendToStringWithoutCombinators(result, nullptr, true);
return NS_ConvertUTF16toUTF8(result);
}
static bool
AppendStandardNamespacePrefixToString(nsAString& aString, int32_t aNameSpace)
{
if (aNameSpace == kNameSpaceID_Unknown) {
// Wildcard namespace; no prefix to write.
return false;
}
switch (aNameSpace) {
case kNameSpaceID_None:
break;
case kNameSpaceID_XML:
aString.AppendLiteral("xml");
break;
case kNameSpaceID_XHTML:
aString.AppendLiteral("html");
break;
case kNameSpaceID_XLink:
aString.AppendLiteral("xlink");
break;
case kNameSpaceID_XSLT:
aString.AppendLiteral("xsl");
break;
case kNameSpaceID_XBL:
aString.AppendLiteral("xbl");
break;
case kNameSpaceID_MathML:
aString.AppendLiteral("math");
break;
case kNameSpaceID_RDF:
aString.AppendLiteral("rdf");
break;
case kNameSpaceID_XUL:
aString.AppendLiteral("xul");
break;
case kNameSpaceID_SVG:
aString.AppendLiteral("svg");
break;
default:
aString.AppendLiteral("ns");
aString.AppendInt(aNameSpace);
break;
}
return true;
}
#endif
void
nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
(nsAString& aString, CSSStyleSheet* aSheet,
bool aIsNegated,
bool aUseStandardNamespacePrefixes) const
{
nsAutoString temp;
bool isPseudoElement = IsPseudoElement();
// For non-pseudo-element selectors or for lone pseudo-elements, deal with
// namespace prefixes.
bool wroteNamespace = false;
if (!isPseudoElement || !mNext) {
// append the namespace prefix if needed
nsXMLNameSpaceMap *sheetNS = aSheet ? aSheet->GetNameSpaceMap() : nullptr;
// sheetNS is non-null if and only if we had an @namespace rule. If it's
// null, that means that the only namespaces we could have are the
// wildcard namespace (which can be implicit in this case) and the "none"
// namespace, which then needs to be explicitly specified.
if (aUseStandardNamespacePrefixes) {
#ifdef DEBUG
// We have no sheet to look up prefix information from. This is
// only for debugging, so use some "standard" prefixes that
// are recognizable.
wroteNamespace =
AppendStandardNamespacePrefixToString(aString, mNameSpace);
if (wroteNamespace) {
aString.Append(char16_t('|'));
}
#endif
} else if (!sheetNS) {
NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown ||
mNameSpace == kNameSpaceID_None,
"How did we get this namespace?");
if (mNameSpace == kNameSpaceID_None) {
aString.Append(char16_t('|'));
wroteNamespace = true;
}
} else if (sheetNS->FindNameSpaceID(nullptr) == mNameSpace) {
// We have the default namespace (possibly including the wildcard
// namespace). Do nothing.
NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown ||
CanBeNamespaced(aIsNegated),
"How did we end up with this namespace?");
} else if (mNameSpace == kNameSpaceID_None) {
NS_ASSERTION(CanBeNamespaced(aIsNegated),
"How did we end up with this namespace?");
aString.Append(char16_t('|'));
wroteNamespace = true;
} else if (mNameSpace != kNameSpaceID_Unknown) {
NS_ASSERTION(CanBeNamespaced(aIsNegated),
"How did we end up with this namespace?");
nsIAtom *prefixAtom = sheetNS->FindPrefix(mNameSpace);
NS_ASSERTION(prefixAtom, "how'd we get a non-default namespace "
"without a prefix?");
nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(prefixAtom),
aString);
aString.Append(char16_t('|'));
wroteNamespace = true;
} else {
// A selector for an element in any namespace, while the default
// namespace is something else. :not() is special in that the default
// namespace is not implied for non-type selectors, so if this is a
// negated non-type selector we don't need to output an explicit wildcard
// namespace here, since those default to a wildcard namespace.
if (CanBeNamespaced(aIsNegated)) {
aString.AppendLiteral("*|");
wroteNamespace = true;
}
}
}
if (!mLowercaseTag) {
// Universal selector: avoid writing the universal selector when we
// can avoid it, especially since we're required to avoid it for the
// inside of :not()
if (wroteNamespace ||
(!mIDList && !mClassList && !mPseudoClassList && !mAttrList &&
(aIsNegated || !mNegations))) {
aString.Append(char16_t('*'));
}
} else {
// Append the tag name
nsAutoString tag;
(isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag);
if (isPseudoElement) {
if (!mNext) {
// Lone pseudo-element selector -- toss in a wildcard type selector
// XXXldb Why?
aString.Append(char16_t('*'));
}
// While our atoms use one colon, most pseudo-elements require two
// colons (those not in CSS level 2) and all pseudo-elements allow
// two colons. So serialize to the non-deprecated two colon syntax.
aString.Append(char16_t(':'));
// This should not be escaped since (a) the pseudo-element string
// has a ":" that can't be escaped and (b) all pseudo-elements at
// this point are known, and therefore we know they don't need
// escaping.
aString.Append(tag);
} else {
nsStyleUtil::AppendEscapedCSSIdent(tag, aString);
}
}
// Append the id, if there is one
if (mIDList) {
nsAtomList* list = mIDList;
while (list != nullptr) {
list->mAtom->ToString(temp);
aString.Append(char16_t('#'));
nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
list = list->mNext;
}
}
// Append each class in the linked list
if (mClassList) {
if (isPseudoElement) {
#ifdef MOZ_XUL
MOZ_ASSERT(nsCSSAnonBoxes::IsTreePseudoElement(mLowercaseTag),
"must be tree pseudo-element");
aString.Append(char16_t('('));
for (nsAtomList* list = mClassList; list; list = list->mNext) {
nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(list->mAtom), aString);
aString.Append(char16_t(','));
}
// replace the final comma with a close-paren
aString.Replace(aString.Length() - 1, 1, char16_t(')'));
#else
NS_ERROR("Can't happen");
#endif
} else {
nsAtomList* list = mClassList;
while (list != nullptr) {
list->mAtom->ToString(temp);
aString.Append(char16_t('.'));
nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
list = list->mNext;
}
}
}
// Append each attribute selector in the linked list
if (mAttrList) {
nsAttrSelector* list = mAttrList;
while (list != nullptr) {
aString.Append(char16_t('['));
// Append the namespace prefix
if (list->mNameSpace == kNameSpaceID_Unknown) {
aString.Append(char16_t('*'));
aString.Append(char16_t('|'));
} else if (list->mNameSpace != kNameSpaceID_None) {
if (aUseStandardNamespacePrefixes) {
#ifdef DEBUG
AppendStandardNamespacePrefixToString(aString, list->mNameSpace);
aString.Append(char16_t('|'));
#endif
} else if (aSheet) {
nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap();
nsIAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace);
// Default namespaces don't apply to attribute selectors, so
// we must have a useful prefix.
NS_ASSERTION(prefixAtom,
"How did we end up with a namespace if the prefix "
"is unknown?");
nsAutoString prefix;
prefixAtom->ToString(prefix);
nsStyleUtil::AppendEscapedCSSIdent(prefix, aString);
aString.Append(char16_t('|'));
}
}
// Append the attribute name
list->mCasedAttr->ToString(temp);
nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
if (list->mFunction != NS_ATTR_FUNC_SET) {
// Append the function
if (list->mFunction == NS_ATTR_FUNC_INCLUDES)
aString.Append(char16_t('~'));
else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH)
aString.Append(char16_t('|'));
else if (list->mFunction == NS_ATTR_FUNC_BEGINSMATCH)
aString.Append(char16_t('^'));
else if (list->mFunction == NS_ATTR_FUNC_ENDSMATCH)
aString.Append(char16_t('$'));
else if (list->mFunction == NS_ATTR_FUNC_CONTAINSMATCH)
aString.Append(char16_t('*'));
aString.Append(char16_t('='));
// Append the value
nsStyleUtil::AppendEscapedCSSString(list->mValue, aString);
if (list->mValueCaseSensitivity ==
nsAttrSelector::ValueCaseSensitivity::CaseInsensitive) {
aString.Append(NS_LITERAL_STRING(" i"));
}
}
aString.Append(char16_t(']'));
list = list->mNext;
}
}
// Append each pseudo-class in the linked list
for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) {
nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp);
// This should not be escaped since (a) the pseudo-class string
// has a ":" that can't be escaped and (b) all pseudo-classes at
// this point are known, and therefore we know they don't need
// escaping.
aString.Append(temp);
if (list->u.mMemory) {
aString.Append(char16_t('('));
if (nsCSSPseudoClasses::HasStringArg(list->mType)) {
nsStyleUtil::AppendEscapedCSSIdent(
nsDependentString(list->u.mString), aString);
} else if (nsCSSPseudoClasses::HasNthPairArg(list->mType)) {
int32_t a = list->u.mNumbers[0],
b = list->u.mNumbers[1];
temp.Truncate();
if (a != 0) {
if (a == -1) {
temp.Append(char16_t('-'));
} else if (a != 1) {
temp.AppendInt(a);
}
temp.Append(char16_t('n'));
}
if (b != 0 || a == 0) {
if (b >= 0 && a != 0) // check a != 0 for whether we printed above
temp.Append(char16_t('+'));
temp.AppendInt(b);
}
aString.Append(temp);
} else {
NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(list->mType),
"unexpected pseudo-class");
nsString tmp;
list->u.mSelectors->ToString(tmp, aSheet);
aString.Append(tmp);
}
aString.Append(char16_t(')'));
}
}
}
bool
nsCSSSelector::CanBeNamespaced(bool aIsNegated) const
{
return !aIsNegated ||
(!mIDList && !mClassList && !mPseudoClassList && !mAttrList);
}
size_t
nsCSSSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
const nsCSSSelector* s = this;
while (s) {
n += aMallocSizeOf(s);
#define MEASURE(x) n += x ? x->SizeOfIncludingThis(aMallocSizeOf) : 0;
MEASURE(s->mIDList);
MEASURE(s->mClassList);
MEASURE(s->mPseudoClassList);
MEASURE(s->mNegations);
MEASURE(s->mAttrList);
// The following members aren't measured:
// - s->mLowercaseTag, because it's an atom and therefore shared
// - s->mCasedTag, because it's an atom and therefore shared
s = s->mNext;
}
return n;
}
// -- nsCSSSelectorList -------------------------------
nsCSSSelectorList::nsCSSSelectorList(void)
: mSelectors(nullptr),
mWeight(0),
mNext(nullptr)
{
MOZ_COUNT_CTOR(nsCSSSelectorList);
}
nsCSSSelectorList::~nsCSSSelectorList()
{
MOZ_COUNT_DTOR(nsCSSSelectorList);
delete mSelectors;
NS_CSS_DELETE_LIST_MEMBER(nsCSSSelectorList, this, mNext);
}
nsCSSSelector*
nsCSSSelectorList::AddSelector(char16_t aOperator)
{
nsCSSSelector* newSel = new nsCSSSelector();
if (mSelectors) {
NS_ASSERTION(aOperator != char16_t(0), "chaining without combinator");
mSelectors->SetOperator(aOperator);
} else {
NS_ASSERTION(aOperator == char16_t(0), "combinator without chaining");
}
newSel->mNext = mSelectors;
mSelectors = newSel;
return newSel;
}
void
nsCSSSelectorList::RemoveRightmostSelector()
{
nsCSSSelector* current = mSelectors;
mSelectors = mSelectors->mNext;
MOZ_ASSERT(mSelectors,
"Rightmost selector has been removed, but now "
"mSelectors is null");
mSelectors->SetOperator(char16_t(0));
// Make sure that deleting current won't delete the whole list.
current->mNext = nullptr;
delete current;
}
void
nsCSSSelectorList::ToString(nsAString& aResult, CSSStyleSheet* aSheet)
{
aResult.Truncate();
nsCSSSelectorList *p = this;
for (;;) {
p->mSelectors->ToString(aResult, aSheet, true);
p = p->mNext;
if (!p)
break;
aResult.AppendLiteral(", ");
}
}
nsCSSSelectorList*
nsCSSSelectorList::Clone(bool aDeep) const
{
nsCSSSelectorList *result = new nsCSSSelectorList();
result->mWeight = mWeight;
NS_IF_CLONE(mSelectors);
if (aDeep) {
NS_CSS_CLONE_LIST_MEMBER(nsCSSSelectorList, this, mNext, result,
(false));
}
return result;
}
size_t
nsCSSSelectorList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
const nsCSSSelectorList* s = this;
while (s) {
n += aMallocSizeOf(s);
n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0;
s = s->mNext;
}
return n;
}
// --------------------------------------------------------
class DOMCSSDeclarationImpl : public nsDOMCSSDeclaration
{
protected:
// Needs to be protected so we can use NS_IMPL_ADDREF_USING_AGGREGATOR.
virtual ~DOMCSSDeclarationImpl(void);
// But we need to allow UniquePtr to delete us.
friend class mozilla::DefaultDelete<DOMCSSDeclarationImpl>;
public:
explicit DOMCSSDeclarationImpl(css::StyleRule *aRule);
NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override;
virtual DeclarationBlock* GetCSSDeclaration(Operation aOperation) override;
virtual nsresult SetCSSDeclaration(DeclarationBlock* aDecl) override;
virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) override;
URLExtraData* GetURLData() const final;
virtual nsIDocument* DocToUpdate() override;
// Override |AddRef| and |Release| for being owned by StyleRule. Also, we
// need to forward QI for cycle collection things to StyleRule.
NS_DECL_ISUPPORTS_INHERITED
virtual nsINode *GetParentObject() override
{
return mRule ? mRule->GetDocument() : nullptr;
}
protected:
// This reference is not reference-counted. The rule object owns us and we go
// away when it does.
css::StyleRule *mRule;
};
DOMCSSDeclarationImpl::DOMCSSDeclarationImpl(css::StyleRule *aRule)
: mRule(aRule)
{
}
DOMCSSDeclarationImpl::~DOMCSSDeclarationImpl(void)
{
}
NS_IMPL_ADDREF_USING_AGGREGATOR(DOMCSSDeclarationImpl, mRule)
NS_IMPL_RELEASE_USING_AGGREGATOR(DOMCSSDeclarationImpl, mRule)
NS_INTERFACE_MAP_BEGIN(DOMCSSDeclarationImpl)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
// We forward the cycle collection interfaces to mRule, which is
// never null.
if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
return mRule->QueryInterface(aIID, aInstancePtr);
}
else
NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration)
DeclarationBlock*
DOMCSSDeclarationImpl::GetCSSDeclaration(Operation aOperation)
{
if (aOperation != eOperation_Read) {
RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
if (sheet) {
sheet->WillDirty();
}
}
return mRule->GetDeclaration();
}
void
DOMCSSDeclarationImpl::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv)
{
GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv);
}
URLExtraData*
DOMCSSDeclarationImpl::GetURLData() const
{
MOZ_ASSERT_UNREACHABLE("GetURLData shouldn't be calling on a Gecko rule");
return GetURLDataForRule(mRule);
}
2003-06-24 10:20:18 +04:00
NS_IMETHODIMP
DOMCSSDeclarationImpl::GetParentRule(nsIDOMCSSRule **aParent)
{
NS_ENSURE_ARG_POINTER(aParent);
NS_IF_ADDREF(*aParent = mRule);
return NS_OK;
}
nsresult
DOMCSSDeclarationImpl::SetCSSDeclaration(DeclarationBlock* aDecl)
{
NS_PRECONDITION(mRule,
"can only be called when |GetCSSDeclaration| returned a declaration");
nsCOMPtr<nsIDocument> doc;
RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
if (sheet) {
doc = sheet->GetAssociatedDocument();
}
mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true);
mRule->SetDeclaration(aDecl->AsGecko());
if (sheet) {
sheet->DidDirty();
}
if (doc) {
doc->StyleRuleChanged(sheet, mRule);
}
return NS_OK;
}
nsIDocument*
DOMCSSDeclarationImpl::DocToUpdate()
{
return nullptr;
}
// -- StyleRule ------------------------------------
namespace mozilla {
namespace css {
uint16_t
StyleRule::Type() const
{
return nsIDOMCSSRule::STYLE_RULE;
}
NS_IMETHODIMP
StyleRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
{
NS_ADDREF(*aStyle = Style());
return NS_OK;
}
nsICSSDeclaration*
StyleRule::Style()
{
if (!mDOMDeclaration) {
mDOMDeclaration.reset(new DOMCSSDeclarationImpl(this));
}
return mDOMDeclaration.get();
}
NS_IMETHODIMP
StyleRule::GetCSSStyleRule(StyleRule **aResult)
{
*aResult = this;
NS_ADDREF(*aResult);
return NS_OK;
}
StyleRule::StyleRule(nsCSSSelectorList* aSelector,
Declaration* aDeclaration,
uint32_t aLineNumber,
uint32_t aColumnNumber)
: BindingStyleRule(aLineNumber, aColumnNumber),
mSelector(aSelector),
mDeclaration(aDeclaration)
1998-04-14 00:24:54 +04:00
{
NS_PRECONDITION(aDeclaration, "must have a declaration");
mDeclaration->SetOwningRule(this);
1998-04-14 00:24:54 +04:00
}
// for |Clone|
StyleRule::StyleRule(const StyleRule& aCopy)
: BindingStyleRule(aCopy),
mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr),
mDeclaration(new Declaration(*aCopy.mDeclaration))
{
mDeclaration->SetOwningRule(this);
// rest is constructed lazily on existing data
}
StyleRule::~StyleRule()
1998-04-14 00:24:54 +04:00
{
delete mSelector;
DropReferences();
}
void
StyleRule::DropReferences()
{
if (mDeclaration) {
mDeclaration->SetOwningRule(nullptr);
}
}
// QueryInterface implementation for StyleRule
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(StyleRule)
if (aIID.Equals(NS_GET_IID(mozilla::css::StyleRule))) {
*aInstancePtr = this;
NS_ADDREF_THIS();
return NS_OK;
}
else
NS_INTERFACE_MAP_ENTRY(nsICSSStyleRuleDOMWrapper)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleRule)
NS_INTERFACE_MAP_END_INHERITING(Rule)
NS_IMPL_ADDREF_INHERITED(StyleRule, Rule)
NS_IMPL_RELEASE_INHERITED(StyleRule, Rule)
NS_IMPL_CYCLE_COLLECTION_CLASS(StyleRule)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(StyleRule, Rule)
// Keep this in sync with IsCCLeaf.
// Trace the wrapper for our declaration. This just expands out
// NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
// directly because the wrapper is on the declaration, not on us.
if (tmp->mDOMDeclaration) {
tmp->mDOMDeclaration->TraceWrapper(aCallbacks, aClosure);
}
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(StyleRule, Rule)
// Unlink the wrapper for our declaraton. This just expands out
// NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
// directly because the wrapper is on the declaration, not on us.
if (tmp->mDOMDeclaration) {
tmp->mDOMDeclaration->ReleaseWrapper(static_cast<nsISupports*>(p));
}
tmp->DropReferences();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(StyleRule, Rule)
// Keep this in sync with IsCCLeaf.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
bool
StyleRule::IsCCLeaf() const
{
if (!Rule::IsCCLeaf()) {
return false;
}
return !mDOMDeclaration || !mDOMDeclaration->PreservingWrapper();
}
1998-04-14 00:24:54 +04:00
/* virtual */ int32_t
StyleRule::GetType() const
{
return Rule::STYLE_RULE;
}
/* virtual */ already_AddRefed<Rule>
StyleRule::Clone() const
{
Bug 1207245 - part 6 - rename nsRefPtr<T> to RefPtr<T>; r=ehsan; a=Tomcat The bulk of this commit was generated with a script, executed at the top level of a typical source code checkout. The only non-machine-generated part was modifying MFBT's moz.build to reflect the new naming. CLOSED TREE makes big refactorings like this a piece of cake. # The main substitution. find . -name '*.cpp' -o -name '*.cc' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ xargs perl -p -i -e ' s/nsRefPtr\.h/RefPtr\.h/g; # handle includes s/nsRefPtr ?</RefPtr</g; # handle declarations and variables ' # Handle a special friend declaration in gfx/layers/AtomicRefCountedWithFinalize.h. perl -p -i -e 's/::nsRefPtr;/::RefPtr;/' gfx/layers/AtomicRefCountedWithFinalize.h # Handle nsRefPtr.h itself, a couple places that define constructors # from nsRefPtr, and code generators specially. We do this here, rather # than indiscriminantly s/nsRefPtr/RefPtr/, because that would rename # things like nsRefPtrHashtable. perl -p -i -e 's/nsRefPtr/RefPtr/g' \ mfbt/nsRefPtr.h \ xpcom/glue/nsCOMPtr.h \ xpcom/base/OwningNonNull.h \ ipc/ipdl/ipdl/lower.py \ ipc/ipdl/ipdl/builtin.py \ dom/bindings/Codegen.py \ python/lldbutils/lldbutils/utils.py # In our indiscriminate substitution above, we renamed # nsRefPtrGetterAddRefs, the class behind getter_AddRefs. Fix that up. find . -name '*.cpp' -o -name '*.h' -o -name '*.idl' | \ xargs perl -p -i -e 's/nsRefPtrGetterAddRefs/RefPtrGetterAddRefs/g' if [ -d .git ]; then git mv mfbt/nsRefPtr.h mfbt/RefPtr.h else hg mv mfbt/nsRefPtr.h mfbt/RefPtr.h fi --HG-- rename : mfbt/nsRefPtr.h => mfbt/RefPtr.h
2015-10-18 08:24:48 +03:00
RefPtr<Rule> clone = new StyleRule(*this);
return clone.forget();
}
void
StyleRule::SetDeclaration(Declaration* aDecl)
{
if (aDecl == mDeclaration) {
return;
}
mDeclaration->SetOwningRule(nullptr);
mDeclaration = aDecl;
mDeclaration->SetOwningRule(this);
}
#ifdef DEBUG
/* virtual */ void
StyleRule::List(FILE* out, int32_t aIndent) const
1998-04-14 00:24:54 +04:00
{
nsAutoCString str;
1998-04-14 00:24:54 +04:00
// Indent
for (int32_t index = aIndent; --index >= 0; ) {
str.AppendLiteral(" ");
}
1998-04-14 00:24:54 +04:00
if (mSelector) {
nsAutoString buffer;
Bug 760331: Coalesce data for inline style across nodes. r=bz This patch enables sharing of an nsAttrValue's MiscContainer between nodes for style rules. MiscContainers of type eCSSStyleRule are now refcounted (with some clever struct packing to ensure that the amount of memory allocated for MiscContainer remains unchanged on 32 and 64 bit). This infrastructure can be used to share most MiscContainer types in the future if we find advantages to sharing other types than just eCSSStyleRuley. A cache mapping strings to MiscContainers has been added to nsHTMLCSSStyleSheet. MiscContainers can be shared between nsAttrValues when one nsAttrValue is SetTo another nsAttrValue or when there is a cache hit in this cache. This patch also adds the ability to tell a style rule that it belongs to an nsHTMLCSSStyleSheet, with appropriate accessor functions to separate that from the existing case of belonging to an nsCSSStyleSheet. The primary use case is to reduce memory use for pages that have lots of inline style attributes with the same value. This can happen easily with large pages that are automatically generated. An (admittedly pathological) testcase in Bug 686975 sees over 250 MB of memory savings with this change. Reusing the same MiscContainer for multiple nodes saves the overhead of maintaining separate copies of the string containing the serialized value of the style attribute and of creating separate style rules for each node. Eliminating duplicate style rules enables further savings in layout through style context sharing. The testcase sees the amount of memory used by style contexts go from over 250 MB to 10 KB. Because the cache is based on the text value of the style attribute, it will not handle attributes that have different text values but are parsed into identical style rules. We also do not attempt to share MiscContainers when the node's base URI differs from the document URI. The effect of these limitations is expected to be low.
2012-09-30 20:40:24 +04:00
mSelector->ToString(buffer, GetStyleSheet());
AppendUTF16toUTF8(buffer, str);
str.Append(' ');
}
1998-04-14 00:24:54 +04:00
if (nullptr != mDeclaration) {
nsAutoString buffer;
str.AppendLiteral("{ ");
mDeclaration->ToString(buffer);
AppendUTF16toUTF8(buffer, str);
str.Append('}');
CSSStyleSheet* sheet = GetStyleSheet();
if (sheet) {
nsIURI* uri = sheet->GetSheetURI();
if (uri) {
str.Append(" /* ");
str.Append(uri->GetSpecOrDefault());
str.Append(':');
str.AppendInt(mLineNumber);
str.Append(" */");
}
}
1998-04-14 00:24:54 +04:00
}
else {
str.AppendLiteral("{ null declaration }");
1998-04-14 00:24:54 +04:00
}
str.Append('\n');
fprintf_stderr(out, "%s", str.get());
1998-04-14 00:24:54 +04:00
}
#endif
void
StyleRule::GetCssTextImpl(nsAString& aCssText) const
1998-11-26 04:34:53 +03:00
{
if (mSelector) {
Bug 760331: Coalesce data for inline style across nodes. r=bz This patch enables sharing of an nsAttrValue's MiscContainer between nodes for style rules. MiscContainers of type eCSSStyleRule are now refcounted (with some clever struct packing to ensure that the amount of memory allocated for MiscContainer remains unchanged on 32 and 64 bit). This infrastructure can be used to share most MiscContainer types in the future if we find advantages to sharing other types than just eCSSStyleRuley. A cache mapping strings to MiscContainers has been added to nsHTMLCSSStyleSheet. MiscContainers can be shared between nsAttrValues when one nsAttrValue is SetTo another nsAttrValue or when there is a cache hit in this cache. This patch also adds the ability to tell a style rule that it belongs to an nsHTMLCSSStyleSheet, with appropriate accessor functions to separate that from the existing case of belonging to an nsCSSStyleSheet. The primary use case is to reduce memory use for pages that have lots of inline style attributes with the same value. This can happen easily with large pages that are automatically generated. An (admittedly pathological) testcase in Bug 686975 sees over 250 MB of memory savings with this change. Reusing the same MiscContainer for multiple nodes saves the overhead of maintaining separate copies of the string containing the serialized value of the style attribute and of creating separate style rules for each node. Eliminating duplicate style rules enables further savings in layout through style context sharing. The testcase sees the amount of memory used by style contexts go from over 250 MB to 10 KB. Because the cache is based on the text value of the style attribute, it will not handle attributes that have different text values but are parsed into identical style rules. We also do not attempt to share MiscContainers when the node's base URI differs from the document URI. The effect of these limitations is expected to be low.
2012-09-30 20:40:24 +04:00
mSelector->ToString(aCssText, GetStyleSheet());
aCssText.Append(char16_t(' '));
}
aCssText.Append(char16_t('{'));
aCssText.Append(char16_t(' '));
if (mDeclaration)
{
nsAutoString tempString;
mDeclaration->ToString( tempString );
aCssText.Append( tempString );
}
aCssText.Append(char16_t(' '));
aCssText.Append(char16_t('}'));
}
NS_IMETHODIMP
StyleRule::GetSelectorText(nsAString& aSelectorText)
{
if (mSelector)
Bug 760331: Coalesce data for inline style across nodes. r=bz This patch enables sharing of an nsAttrValue's MiscContainer between nodes for style rules. MiscContainers of type eCSSStyleRule are now refcounted (with some clever struct packing to ensure that the amount of memory allocated for MiscContainer remains unchanged on 32 and 64 bit). This infrastructure can be used to share most MiscContainer types in the future if we find advantages to sharing other types than just eCSSStyleRuley. A cache mapping strings to MiscContainers has been added to nsHTMLCSSStyleSheet. MiscContainers can be shared between nsAttrValues when one nsAttrValue is SetTo another nsAttrValue or when there is a cache hit in this cache. This patch also adds the ability to tell a style rule that it belongs to an nsHTMLCSSStyleSheet, with appropriate accessor functions to separate that from the existing case of belonging to an nsCSSStyleSheet. The primary use case is to reduce memory use for pages that have lots of inline style attributes with the same value. This can happen easily with large pages that are automatically generated. An (admittedly pathological) testcase in Bug 686975 sees over 250 MB of memory savings with this change. Reusing the same MiscContainer for multiple nodes saves the overhead of maintaining separate copies of the string containing the serialized value of the style attribute and of creating separate style rules for each node. Eliminating duplicate style rules enables further savings in layout through style context sharing. The testcase sees the amount of memory used by style contexts go from over 250 MB to 10 KB. Because the cache is based on the text value of the style attribute, it will not handle attributes that have different text values but are parsed into identical style rules. We also do not attempt to share MiscContainers when the node's base URI differs from the document URI. The effect of these limitations is expected to be low.
2012-09-30 20:40:24 +04:00
mSelector->ToString(aSelectorText, GetStyleSheet());
else
aSelectorText.Truncate();
return NS_OK;
}
NS_IMETHODIMP
StyleRule::SetSelectorText(const nsAString& aSelectorText)
{
// XXX TBI - get a parser and re-parse the selectors,
1998-12-11 05:50:43 +03:00
// XXX then need to re-compute the cascade
// XXX and dirty sheet
return NS_OK;
}
/* virtual */ size_t
StyleRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
n += mSelector ? mSelector->SizeOfIncludingThis(aMallocSizeOf) : 0;
n += mDeclaration ? mDeclaration->SizeOfIncludingThis(aMallocSizeOf) : 0;
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mDOMRule;
return n;
}
} // namespace css
} // namespace mozilla