gecko/layout/style/nsCSSRules.cpp

3507 lines
86 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2012-05-21 04:12:37 -07: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/. */
/* rules in a CSS stylesheet other than style rules (e.g., @import rules) */
#include "mozilla/Attributes.h"
#include "nsCSSRules.h"
#include "nsCSSValue.h"
#include "mozilla/CSSStyleSheet.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/ImportRule.h"
#include "mozilla/css/NameSpaceRule.h"
#include "nsString.h"
#include "nsIAtom.h"
#include "nsCSSProps.h"
#include "nsCOMPtr.h"
#include "nsIDOMCSSStyleSheet.h"
#include "nsIMediaList.h"
#include "mozilla/dom/CSSRuleList.h"
#include "nsIDocument.h"
#include "nsPresContext.h"
#include "nsContentUtils.h"
#include "nsError.h"
#include "nsStyleUtil.h"
#include "mozilla/css/Declaration.h"
#include "nsCSSParser.h"
#include "nsDOMClassInfoID.h"
#include "mozilla/dom/CSSStyleDeclarationBinding.h"
#include "StyleRule.h"
#include "nsFont.h"
#include "nsIURI.h"
#include "mozAutoDocUpdate.h"
using namespace mozilla;
#define IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_) \
/* virtual */ nsIDOMCSSRule* class_::GetDOMRule() \
{ return this; } \
/* virtual */ nsIDOMCSSRule* class_::GetExistingDOMRule() \
{ return this; }
#define IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(class_, super_) \
/* virtual */ void class_::MapRuleInfoInto(nsRuleData* aRuleData) \
{ NS_ABORT_IF_FALSE(false, "should not be called"); }
#define IMPL_STYLE_RULE_INHERIT(class_, super_) \
IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_) \
IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(class_, super_)
// base class for all rule types in a CSS style sheet
namespace mozilla {
namespace css {
CSSStyleSheet*
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 09:40:24 -07:00
Rule::GetStyleSheet() const
{
if (!(mSheet & 0x1)) {
return reinterpret_cast<CSSStyleSheet*>(mSheet);
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 09:40:24 -07:00
}
return nullptr;
}
nsHTMLCSSStyleSheet*
Rule::GetHTMLCSSStyleSheet() const
{
if (mSheet & 0x1) {
return reinterpret_cast<nsHTMLCSSStyleSheet*>(mSheet & ~uintptr_t(0x1));
}
return nullptr;
}
/* virtual */ void
Rule::SetStyleSheet(CSSStyleSheet* aSheet)
{
// We don't reference count this up reference. The style sheet
// will tell us when it's going away or when we're detached from
// it.
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 09:40:24 -07:00
mSheet = reinterpret_cast<uintptr_t>(aSheet);
}
void
Rule::SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aSheet)
{
// We don't reference count this up reference. The style sheet
// will tell us when it's going away or when we're detached from
// it.
mSheet = reinterpret_cast<uintptr_t>(aSheet);
mSheet |= 0x1;
}
nsresult
Rule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
if (mParentRule) {
NS_IF_ADDREF(*aParentRule = mParentRule->GetDOMRule());
} else {
*aParentRule = nullptr;
}
return NS_OK;
}
nsresult
Rule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
NS_ENSURE_ARG_POINTER(aSheet);
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 09:40:24 -07:00
NS_IF_ADDREF(*aSheet = GetStyleSheet());
return NS_OK;
}
css::Rule*
Rule::GetCSSRule()
{
return this;
}
size_t
Rule::SizeOfCOMArrayElementIncludingThis(css::Rule* aElement,
MallocSizeOf aMallocSizeOf,
void* aData)
{
return aElement->SizeOfIncludingThis(aMallocSizeOf);
}
// -------------------------------
// Style Rule List for group rules
//
class GroupRuleRuleList MOZ_FINAL : public dom::CSSRuleList
{
public:
explicit GroupRuleRuleList(GroupRule *aGroupRule);
virtual CSSStyleSheet* GetParentObject() MOZ_OVERRIDE;
virtual nsIDOMCSSRule*
IndexedGetter(uint32_t aIndex, bool& aFound) MOZ_OVERRIDE;
virtual uint32_t
Length() MOZ_OVERRIDE;
void DropReference() { mGroupRule = nullptr; }
private:
~GroupRuleRuleList();
private:
GroupRule* mGroupRule;
};
GroupRuleRuleList::GroupRuleRuleList(GroupRule *aGroupRule)
{
// Not reference counted to avoid circular references.
// The rule will tell us when its going away.
mGroupRule = aGroupRule;
}
GroupRuleRuleList::~GroupRuleRuleList()
{
}
CSSStyleSheet*
GroupRuleRuleList::GetParentObject()
{
if (!mGroupRule) {
return nullptr;
}
return mGroupRule->GetStyleSheet();
}
uint32_t
GroupRuleRuleList::Length()
{
if (!mGroupRule) {
return 0;
}
return AssertedCast<uint32_t>(mGroupRule->StyleRuleCount());
}
nsIDOMCSSRule*
GroupRuleRuleList::IndexedGetter(uint32_t aIndex, bool& aFound)
{
aFound = false;
if (mGroupRule) {
nsRefPtr<Rule> rule = mGroupRule->GetStyleRuleAt(aIndex);
if (rule) {
aFound = true;
return rule->GetDOMRule();
}
}
return nullptr;
}
} // namespace css
} // namespace mozilla
// -------------------------------------------
// CharsetRule
//
// Must be outside namespace
DOMCI_DATA(CSSCharsetRule, css::CharsetRule)
namespace mozilla {
namespace css {
CharsetRule::CharsetRule(const nsAString& aEncoding,
uint32_t aLineNumber, uint32_t aColumnNumber)
: Rule(aLineNumber, aColumnNumber),
mEncoding(aEncoding)
{
}
CharsetRule::CharsetRule(const CharsetRule& aCopy)
: Rule(aCopy),
mEncoding(aCopy.mEncoding)
{
}
NS_IMPL_ADDREF(CharsetRule)
NS_IMPL_RELEASE(CharsetRule)
// QueryInterface implementation for CharsetRule
NS_INTERFACE_MAP_BEGIN(CharsetRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSCharsetRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSCharsetRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT(CharsetRule, Rule)
#ifdef DEBUG
/* virtual */ void
CharsetRule::List(FILE* out, int32_t aIndent) const
{
// Indent
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
fputs("@charset \"", out);
fputs(NS_LossyConvertUTF16toASCII(mEncoding).get(), out);
fputs("\"\n", out);
}
#endif
/* virtual */ int32_t
CharsetRule::GetType() const
{
return Rule::CHARSET_RULE;
}
/* virtual */ already_AddRefed<Rule>
CharsetRule::Clone() const
{
nsRefPtr<Rule> clone = new CharsetRule(*this);
return clone.forget();
}
NS_IMETHODIMP
CharsetRule::GetEncoding(nsAString& aEncoding)
{
aEncoding = mEncoding;
return NS_OK;
}
NS_IMETHODIMP
CharsetRule::SetEncoding(const nsAString& aEncoding)
{
mEncoding = aEncoding;
return NS_OK;
}
NS_IMETHODIMP
CharsetRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::CHARSET_RULE;
return NS_OK;
}
NS_IMETHODIMP
CharsetRule::GetCssText(nsAString& aCssText)
{
aCssText.AssignLiteral("@charset \"");
aCssText.Append(mEncoding);
aCssText.AppendLiteral("\";");
return NS_OK;
}
NS_IMETHODIMP
CharsetRule::SetCssText(const nsAString& aCssText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
CharsetRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return Rule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
CharsetRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return Rule::GetParentRule(aParentRule);
}
css::Rule*
CharsetRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
/* virtual */ size_t
CharsetRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mEncoding
}
// -------------------------------------------
// ImportRule
//
ImportRule::ImportRule(nsMediaList* aMedia, const nsString& aURLSpec,
uint32_t aLineNumber, uint32_t aColumnNumber)
: Rule(aLineNumber, aColumnNumber)
, mURLSpec(aURLSpec)
, mMedia(aMedia)
{
// XXXbz This is really silly.... the mMedia here will be replaced
// with itself if we manage to load a sheet. Which should really
// never fail nowadays, in sane cases.
}
ImportRule::ImportRule(const ImportRule& aCopy)
: Rule(aCopy),
mURLSpec(aCopy.mURLSpec)
{
// Whether or not an @import rule has a null sheet is a permanent
// property of that @import rule, since it is null only if the target
// sheet failed security checks.
if (aCopy.mChildSheet) {
nsRefPtr<CSSStyleSheet> sheet =
aCopy.mChildSheet->Clone(nullptr, this, nullptr, nullptr);
SetSheet(sheet);
// SetSheet sets mMedia appropriately
}
}
ImportRule::~ImportRule()
{
if (mChildSheet) {
mChildSheet->SetOwnerRule(nullptr);
}
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(ImportRule)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ImportRule)
NS_IMPL_CYCLE_COLLECTION(ImportRule, mMedia, mChildSheet)
// QueryInterface implementation for ImportRule
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImportRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSImportRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSImportRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT(ImportRule, Rule)
#ifdef DEBUG
/* virtual */ void
ImportRule::List(FILE* out, int32_t aIndent) const
{
// Indent
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
fputs("@import \"", out);
fputs(NS_LossyConvertUTF16toASCII(mURLSpec).get(), out);
fputs("\" ", out);
nsAutoString mediaText;
mMedia->GetText(mediaText);
fputs(NS_LossyConvertUTF16toASCII(mediaText).get(), out);
fputs("\n", out);
}
#endif
/* virtual */ int32_t
ImportRule::GetType() const
{
return Rule::IMPORT_RULE;
}
/* virtual */ already_AddRefed<Rule>
ImportRule::Clone() const
{
nsRefPtr<Rule> clone = new ImportRule(*this);
return clone.forget();
}
void
ImportRule::SetSheet(CSSStyleSheet* aSheet)
{
NS_PRECONDITION(aSheet, "null arg");
// set the new sheet
mChildSheet = aSheet;
aSheet->SetOwnerRule(this);
// set our medialist to be the same as the sheet's medialist
mMedia = mChildSheet->Media();
}
NS_IMETHODIMP
ImportRule::GetType(uint16_t* aType)
{
NS_ENSURE_ARG_POINTER(aType);
*aType = nsIDOMCSSRule::IMPORT_RULE;
return NS_OK;
}
NS_IMETHODIMP
ImportRule::GetCssText(nsAString& aCssText)
{
aCssText.AssignLiteral("@import url(");
nsStyleUtil::AppendEscapedCSSString(mURLSpec, aCssText);
aCssText.Append(')');
if (mMedia) {
nsAutoString mediaText;
mMedia->GetText(mediaText);
if (!mediaText.IsEmpty()) {
aCssText.Append(' ');
aCssText.Append(mediaText);
}
}
aCssText.Append(';');
return NS_OK;
}
NS_IMETHODIMP
ImportRule::SetCssText(const nsAString& aCssText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ImportRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return Rule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
ImportRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return Rule::GetParentRule(aParentRule);
}
css::Rule*
ImportRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
NS_IMETHODIMP
ImportRule::GetHref(nsAString & aHref)
{
aHref = mURLSpec;
return NS_OK;
}
NS_IMETHODIMP
ImportRule::GetMedia(nsIDOMMediaList * *aMedia)
{
NS_ENSURE_ARG_POINTER(aMedia);
NS_IF_ADDREF(*aMedia = mMedia);
return NS_OK;
}
NS_IMETHODIMP
ImportRule::GetStyleSheet(nsIDOMCSSStyleSheet * *aStyleSheet)
{
NS_ENSURE_ARG_POINTER(aStyleSheet);
NS_IF_ADDREF(*aStyleSheet = mChildSheet);
return NS_OK;
}
/* virtual */ size_t
ImportRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mURLSpec
//
// The following members are not measured:
// - mMedia, because it is measured via CSSStyleSheet::mMedia
// - mChildSheet, because it is measured via CSSStyleSheetInner::mSheets
}
} // namespace css
} // namespace mozilla
// must be outside the namespace
DOMCI_DATA(CSSImportRule, css::ImportRule)
namespace mozilla {
namespace css {
GroupRule::GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber)
: Rule(aLineNumber, aColumnNumber)
{
}
static bool
SetParentRuleReference(Rule* aRule, void* aParentRule)
{
GroupRule* parentRule = static_cast<GroupRule*>(aParentRule);
aRule->SetParentRule(parentRule);
return true;
}
GroupRule::GroupRule(const GroupRule& aCopy)
: Rule(aCopy)
{
const_cast<GroupRule&>(aCopy).mRules.EnumerateForwards(GroupRule::CloneRuleInto, &mRules);
mRules.EnumerateForwards(SetParentRuleReference, this);
}
GroupRule::~GroupRule()
{
NS_ABORT_IF_FALSE(!mSheet, "SetStyleSheet should have been called");
mRules.EnumerateForwards(SetParentRuleReference, nullptr);
if (mRuleCollection) {
mRuleCollection->DropReference();
}
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(GroupRule)
NS_IMPL_CYCLE_COLLECTING_RELEASE(GroupRule)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GroupRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(GroupRule, Rule)
static bool
SetStyleSheetReference(Rule* aRule, void* aSheet)
{
CSSStyleSheet* sheet = (CSSStyleSheet*)aSheet;
aRule->SetStyleSheet(sheet);
return true;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(GroupRule)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(GroupRule)
tmp->mRules.EnumerateForwards(SetParentRuleReference, nullptr);
// If tmp does not have a stylesheet, neither do its descendants. In that
// case, don't try to null out their stylesheet, to avoid O(N^2) behavior in
// depth of group rule nesting. But if tmp _does_ have a stylesheet (which
// can happen if it gets unlinked earlier than its owning stylesheet), then we
// need to null out the stylesheet pointer on descendants now, before we clear
// tmp->mRules.
if (tmp->GetStyleSheet()) {
tmp->mRules.EnumerateForwards(SetStyleSheetReference, nullptr);
}
tmp->mRules.Clear();
if (tmp->mRuleCollection) {
tmp->mRuleCollection->DropReference();
tmp->mRuleCollection = nullptr;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(GroupRule)
const nsCOMArray<Rule>& rules = tmp->mRules;
for (int32_t i = 0, count = rules.Count(); i < count; ++i) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
cb.NoteXPCOMChild(rules[i]->GetExistingDOMRule());
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleCollection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
/* virtual */ void
GroupRule::SetStyleSheet(CSSStyleSheet* aSheet)
{
// Don't set the sheet on the kids if it's already the same as the sheet we
// already have. This is needed to avoid O(N^2) behavior in group nesting
// depth when seting the sheet to null during unlink, if we happen to unlin in
// order from most nested rule up to least nested rule.
if (aSheet != GetStyleSheet()) {
mRules.EnumerateForwards(SetStyleSheetReference, aSheet);
Rule::SetStyleSheet(aSheet);
}
}
#ifdef DEBUG
/* virtual */ void
GroupRule::List(FILE* out, int32_t aIndent) const
{
fputs(" {\n", out);
for (int32_t index = 0, count = mRules.Count(); index < count; ++index) {
mRules.ObjectAt(index)->List(out, aIndent + 1);
}
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
fputs("}\n", out);
}
#endif
void
GroupRule::AppendStyleRule(Rule* aRule)
{
mRules.AppendObject(aRule);
CSSStyleSheet* sheet = GetStyleSheet();
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 09:40:24 -07:00
aRule->SetStyleSheet(sheet);
aRule->SetParentRule(this);
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 09:40:24 -07:00
if (sheet) {
sheet->SetModifiedByChildRule();
}
}
Rule*
GroupRule::GetStyleRuleAt(int32_t aIndex) const
{
return mRules.SafeObjectAt(aIndex);
}
bool
GroupRule::EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const
{
return
const_cast<GroupRule*>(this)->mRules.EnumerateForwards(aFunc, aData);
}
/*
* The next two methods (DeleteStyleRuleAt and InsertStyleRuleAt)
* should never be called unless you have first called WillDirty() on
* the parents stylesheet. After they are called, DidDirty() needs to
* be called on the sheet
*/
nsresult
GroupRule::DeleteStyleRuleAt(uint32_t aIndex)
{
Rule* rule = mRules.SafeObjectAt(aIndex);
if (rule) {
rule->SetStyleSheet(nullptr);
rule->SetParentRule(nullptr);
}
return mRules.RemoveObjectAt(aIndex) ? NS_OK : NS_ERROR_ILLEGAL_VALUE;
}
nsresult
GroupRule::InsertStyleRuleAt(uint32_t aIndex, Rule* aRule)
{
aRule->SetStyleSheet(GetStyleSheet());
aRule->SetParentRule(this);
if (! mRules.InsertObjectAt(aRule, aIndex)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
GroupRule::ReplaceStyleRule(Rule* aOld, Rule* aNew)
{
int32_t index = mRules.IndexOf(aOld);
NS_ENSURE_TRUE(index != -1, NS_ERROR_UNEXPECTED);
mRules.ReplaceObjectAt(aNew, index);
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 09:40:24 -07:00
aNew->SetStyleSheet(GetStyleSheet());
aNew->SetParentRule(this);
aOld->SetStyleSheet(nullptr);
aOld->SetParentRule(nullptr);
return NS_OK;
}
void
GroupRule::AppendRulesToCssText(nsAString& aCssText)
{
aCssText.AppendLiteral(" {\n");
// get all the rules
for (int32_t index = 0, count = mRules.Count(); index < count; ++index) {
Rule* rule = mRules.ObjectAt(index);
nsIDOMCSSRule* domRule = rule->GetDOMRule();
if (domRule) {
nsAutoString cssText;
domRule->GetCssText(cssText);
aCssText.AppendLiteral(" ");
aCssText.Append(cssText);
aCssText.Append('\n');
}
}
aCssText.Append('}');
}
// nsIDOMCSSMediaRule or nsIDOMCSSMozDocumentRule methods
nsresult
GroupRule::GetCssRules(nsIDOMCSSRuleList* *aRuleList)
{
if (!mRuleCollection) {
mRuleCollection = new css::GroupRuleRuleList(this);
}
NS_ADDREF(*aRuleList = mRuleCollection);
return NS_OK;
}
nsresult
GroupRule::InsertRule(const nsAString & aRule, uint32_t aIndex, uint32_t* _retval)
{
CSSStyleSheet* sheet = GetStyleSheet();
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 09:40:24 -07:00
NS_ENSURE_TRUE(sheet, NS_ERROR_FAILURE);
if (aIndex > uint32_t(mRules.Count()))
return NS_ERROR_DOM_INDEX_SIZE_ERR;
NS_ASSERTION(uint32_t(mRules.Count()) <= INT32_MAX,
"Too many style rules!");
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 09:40:24 -07:00
return sheet->InsertRuleIntoGroup(aRule, this, aIndex, _retval);
}
nsresult
GroupRule::DeleteRule(uint32_t aIndex)
{
CSSStyleSheet* sheet = GetStyleSheet();
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 09:40:24 -07:00
NS_ENSURE_TRUE(sheet, NS_ERROR_FAILURE);
if (aIndex >= uint32_t(mRules.Count()))
return NS_ERROR_DOM_INDEX_SIZE_ERR;
NS_ASSERTION(uint32_t(mRules.Count()) <= INT32_MAX,
"Too many style rules!");
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 09:40:24 -07:00
return sheet->DeleteRuleFromGroup(this, aIndex);
}
/* virtual */ size_t
GroupRule::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
return mRules.SizeOfExcludingThis(Rule::SizeOfCOMArrayElementIncludingThis,
aMallocSizeOf);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mRuleCollection
}
// -------------------------------------------
// nsICSSMediaRule
//
MediaRule::MediaRule(uint32_t aLineNumber, uint32_t aColumnNumber)
: GroupRule(aLineNumber, aColumnNumber)
{
}
MediaRule::MediaRule(const MediaRule& aCopy)
: GroupRule(aCopy)
{
if (aCopy.mMedia) {
mMedia = aCopy.mMedia->Clone();
// XXXldb This doesn't really make sense.
mMedia->SetStyleSheet(aCopy.GetStyleSheet());
}
}
MediaRule::~MediaRule()
{
if (mMedia) {
mMedia->SetStyleSheet(nullptr);
}
}
NS_IMPL_ADDREF_INHERITED(MediaRule, GroupRule)
NS_IMPL_RELEASE_INHERITED(MediaRule, GroupRule)
// QueryInterface implementation for MediaRule
NS_INTERFACE_MAP_BEGIN(MediaRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSMediaRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSMediaRule)
NS_INTERFACE_MAP_END_INHERITING(GroupRule)
/* virtual */ void
MediaRule::SetStyleSheet(CSSStyleSheet* aSheet)
{
if (mMedia) {
// Set to null so it knows it's leaving one sheet and joining another.
mMedia->SetStyleSheet(nullptr);
mMedia->SetStyleSheet(aSheet);
}
GroupRule::SetStyleSheet(aSheet);
}
#ifdef DEBUG
/* virtual */ void
MediaRule::List(FILE* out, int32_t aIndent) const
{
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
nsAutoString buffer;
fputs("@media ", out);
if (mMedia) {
nsAutoString mediaText;
mMedia->GetText(mediaText);
fputs(NS_LossyConvertUTF16toASCII(mediaText).get(), out);
}
GroupRule::List(out, aIndent);
}
#endif
/* virtual */ int32_t
MediaRule::GetType() const
{
return Rule::MEDIA_RULE;
}
/* virtual */ already_AddRefed<Rule>
MediaRule::Clone() const
{
nsRefPtr<Rule> clone = new MediaRule(*this);
return clone.forget();
}
nsresult
MediaRule::SetMedia(nsMediaList* aMedia)
{
mMedia = aMedia;
if (aMedia)
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 09:40:24 -07:00
mMedia->SetStyleSheet(GetStyleSheet());
return NS_OK;
}
// nsIDOMCSSRule methods
NS_IMETHODIMP
MediaRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::MEDIA_RULE;
return NS_OK;
}
NS_IMETHODIMP
MediaRule::GetCssText(nsAString& aCssText)
{
aCssText.AssignLiteral("@media ");
AppendConditionText(aCssText);
GroupRule::AppendRulesToCssText(aCssText);
return NS_OK;
}
NS_IMETHODIMP
MediaRule::SetCssText(const nsAString& aCssText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
MediaRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return GroupRule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
MediaRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return GroupRule::GetParentRule(aParentRule);
}
css::Rule*
MediaRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
// nsIDOMCSSGroupingRule methods
NS_IMETHODIMP
MediaRule::GetCssRules(nsIDOMCSSRuleList* *aRuleList)
{
return GroupRule::GetCssRules(aRuleList);
}
NS_IMETHODIMP
MediaRule::InsertRule(const nsAString & aRule, uint32_t aIndex, uint32_t* _retval)
{
return GroupRule::InsertRule(aRule, aIndex, _retval);
}
NS_IMETHODIMP
MediaRule::DeleteRule(uint32_t aIndex)
{
return GroupRule::DeleteRule(aIndex);
}
// nsIDOMCSSConditionRule methods
NS_IMETHODIMP
MediaRule::GetConditionText(nsAString& aConditionText)
{
aConditionText.Truncate(0);
AppendConditionText(aConditionText);
return NS_OK;
}
NS_IMETHODIMP
MediaRule::SetConditionText(const nsAString& aConditionText)
{
if (!mMedia) {
nsRefPtr<nsMediaList> media = new nsMediaList();
media->SetStyleSheet(GetStyleSheet());
nsresult rv = media->SetMediaText(aConditionText);
if (NS_SUCCEEDED(rv)) {
mMedia = media;
}
return rv;
}
return mMedia->SetMediaText(aConditionText);
}
// nsIDOMCSSMediaRule methods
NS_IMETHODIMP
MediaRule::GetMedia(nsIDOMMediaList* *aMedia)
{
NS_ENSURE_ARG_POINTER(aMedia);
NS_IF_ADDREF(*aMedia = mMedia);
return NS_OK;
}
// GroupRule interface
/* virtual */ bool
MediaRule::UseForPresentation(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey& aKey)
{
if (mMedia) {
return mMedia->Matches(aPresContext, &aKey);
}
return true;
}
/* virtual */ size_t
MediaRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
n += GroupRule::SizeOfExcludingThis(aMallocSizeOf);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mMedia
return n;
}
void
MediaRule::AppendConditionText(nsAString& aOutput)
{
if (mMedia) {
nsAutoString mediaText;
mMedia->GetText(mediaText);
aOutput.Append(mediaText);
}
}
} // namespace css
} // namespace mozilla
// Must be outside namespace
DOMCI_DATA(CSSMediaRule, css::MediaRule)
namespace mozilla {
namespace css {
DocumentRule::DocumentRule(uint32_t aLineNumber, uint32_t aColumnNumber)
: GroupRule(aLineNumber, aColumnNumber)
{
}
DocumentRule::DocumentRule(const DocumentRule& aCopy)
: GroupRule(aCopy)
, mURLs(new URL(*aCopy.mURLs))
{
}
DocumentRule::~DocumentRule()
{
}
NS_IMPL_ADDREF_INHERITED(DocumentRule, GroupRule)
NS_IMPL_RELEASE_INHERITED(DocumentRule, GroupRule)
// QueryInterface implementation for DocumentRule
NS_INTERFACE_MAP_BEGIN(DocumentRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSMozDocumentRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSMozDocumentRule)
NS_INTERFACE_MAP_END_INHERITING(GroupRule)
#ifdef DEBUG
/* virtual */ void
DocumentRule::List(FILE* out, int32_t aIndent) const
{
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
nsAutoCString str;
str.AssignLiteral("@-moz-document ");
for (URL *url = mURLs; url; url = url->next) {
switch (url->func) {
case eURL:
str.AppendLiteral("url(\"");
break;
case eURLPrefix:
str.AppendLiteral("url-prefix(\"");
break;
case eDomain:
str.AppendLiteral("domain(\"");
break;
case eRegExp:
str.AppendLiteral("regexp(\"");
break;
}
nsAutoCString escapedURL(url->url);
escapedURL.ReplaceSubstring("\"", "\\\""); // escape quotes
str.Append(escapedURL);
str.AppendLiteral("\"), ");
}
str.Cut(str.Length() - 2, 1); // remove last ,
fputs(str.get(), out);
GroupRule::List(out, aIndent);
}
#endif
/* virtual */ int32_t
DocumentRule::GetType() const
{
return Rule::DOCUMENT_RULE;
}
/* virtual */ already_AddRefed<Rule>
DocumentRule::Clone() const
{
nsRefPtr<Rule> clone = new DocumentRule(*this);
return clone.forget();
}
// nsIDOMCSSRule methods
NS_IMETHODIMP
DocumentRule::GetType(uint16_t* aType)
{
// XXX What should really happen here?
*aType = nsIDOMCSSRule::UNKNOWN_RULE;
return NS_OK;
}
NS_IMETHODIMP
DocumentRule::GetCssText(nsAString& aCssText)
{
aCssText.AssignLiteral("@-moz-document ");
AppendConditionText(aCssText);
GroupRule::AppendRulesToCssText(aCssText);
return NS_OK;
}
NS_IMETHODIMP
DocumentRule::SetCssText(const nsAString& aCssText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
DocumentRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return GroupRule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
DocumentRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return GroupRule::GetParentRule(aParentRule);
}
css::Rule*
DocumentRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
// nsIDOMCSSGroupingRule methods
NS_IMETHODIMP
DocumentRule::GetCssRules(nsIDOMCSSRuleList* *aRuleList)
{
return GroupRule::GetCssRules(aRuleList);
}
NS_IMETHODIMP
DocumentRule::InsertRule(const nsAString & aRule, uint32_t aIndex, uint32_t* _retval)
{
return GroupRule::InsertRule(aRule, aIndex, _retval);
}
NS_IMETHODIMP
DocumentRule::DeleteRule(uint32_t aIndex)
{
return GroupRule::DeleteRule(aIndex);
}
// nsIDOMCSSConditionRule methods
NS_IMETHODIMP
DocumentRule::GetConditionText(nsAString& aConditionText)
{
aConditionText.Truncate(0);
AppendConditionText(aConditionText);
return NS_OK;
}
NS_IMETHODIMP
DocumentRule::SetConditionText(const nsAString& aConditionText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
// GroupRule interface
/* virtual */ bool
DocumentRule::UseForPresentation(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey& aKey)
{
nsIDocument *doc = aPresContext->Document();
nsIURI *docURI = doc->GetDocumentURI();
nsAutoCString docURISpec;
if (docURI)
docURI->GetSpec(docURISpec);
for (URL *url = mURLs; url; url = url->next) {
switch (url->func) {
case eURL: {
if (docURISpec == url->url)
return true;
} break;
case eURLPrefix: {
if (StringBeginsWith(docURISpec, url->url))
return true;
} break;
case eDomain: {
nsAutoCString host;
if (docURI)
docURI->GetHost(host);
int32_t lenDiff = host.Length() - url->url.Length();
if (lenDiff == 0) {
if (host == url->url)
return true;
} else {
if (StringEndsWith(host, url->url) &&
host.CharAt(lenDiff - 1) == '.')
return true;
}
} break;
case eRegExp: {
NS_ConvertUTF8toUTF16 spec(docURISpec);
NS_ConvertUTF8toUTF16 regex(url->url);
if (nsContentUtils::IsPatternMatching(spec, regex, doc)) {
return true;
}
} break;
}
}
return false;
}
DocumentRule::URL::~URL()
{
NS_CSS_DELETE_LIST_MEMBER(DocumentRule::URL, this, next);
}
/* virtual */ size_t
DocumentRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
n += GroupRule::SizeOfExcludingThis(aMallocSizeOf);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mURLs
return n;
}
void
DocumentRule::AppendConditionText(nsAString& aCssText)
{
for (URL *url = mURLs; url; url = url->next) {
switch (url->func) {
case eURL:
aCssText.AppendLiteral("url(");
break;
case eURLPrefix:
aCssText.AppendLiteral("url-prefix(");
break;
case eDomain:
aCssText.AppendLiteral("domain(");
break;
case eRegExp:
aCssText.AppendLiteral("regexp(");
break;
}
nsStyleUtil::AppendEscapedCSSString(NS_ConvertUTF8toUTF16(url->url),
aCssText);
aCssText.AppendLiteral("), ");
}
aCssText.Truncate(aCssText.Length() - 2); // remove last ", "
}
} // namespace css
} // namespace mozilla
// Must be outside namespace
DOMCI_DATA(CSSMozDocumentRule, css::DocumentRule)
// -------------------------------------------
// NameSpaceRule
//
namespace mozilla {
namespace css {
NameSpaceRule::NameSpaceRule(nsIAtom* aPrefix, const nsString& aURLSpec,
uint32_t aLineNumber, uint32_t aColumnNumber)
: Rule(aLineNumber, aColumnNumber),
mPrefix(aPrefix),
mURLSpec(aURLSpec)
{
}
NameSpaceRule::NameSpaceRule(const NameSpaceRule& aCopy)
: Rule(aCopy),
mPrefix(aCopy.mPrefix),
mURLSpec(aCopy.mURLSpec)
{
}
NameSpaceRule::~NameSpaceRule()
{
}
NS_IMPL_ADDREF(NameSpaceRule)
NS_IMPL_RELEASE(NameSpaceRule)
// QueryInterface implementation for NameSpaceRule
NS_INTERFACE_MAP_BEGIN(NameSpaceRule)
if (aIID.Equals(NS_GET_IID(css::NameSpaceRule))) {
*aInstancePtr = this;
NS_ADDREF_THIS();
return NS_OK;
}
else
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSNameSpaceRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT(NameSpaceRule, Rule)
#ifdef DEBUG
/* virtual */ void
NameSpaceRule::List(FILE* out, int32_t aIndent) const
{
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
nsAutoString buffer;
fputs("@namespace ", out);
if (mPrefix) {
mPrefix->ToString(buffer);
fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out);
fputs(" ", out);
}
fputs("url(", out);
fputs(NS_LossyConvertUTF16toASCII(mURLSpec).get(), out);
fputs(")\n", out);
}
#endif
/* virtual */ int32_t
NameSpaceRule::GetType() const
{
return Rule::NAMESPACE_RULE;
}
/* virtual */ already_AddRefed<Rule>
NameSpaceRule::Clone() const
{
nsRefPtr<Rule> clone = new NameSpaceRule(*this);
return clone.forget();
}
NS_IMETHODIMP
NameSpaceRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::NAMESPACE_RULE;
return NS_OK;
}
NS_IMETHODIMP
NameSpaceRule::GetCssText(nsAString& aCssText)
{
aCssText.AssignLiteral("@namespace ");
if (mPrefix) {
aCssText.Append(nsDependentAtomString(mPrefix) + NS_LITERAL_STRING(" "));
}
aCssText.AppendLiteral("url(");
nsStyleUtil::AppendEscapedCSSString(mURLSpec, aCssText);
aCssText.AppendLiteral(");");
return NS_OK;
}
NS_IMETHODIMP
NameSpaceRule::SetCssText(const nsAString& aCssText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
NameSpaceRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return Rule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
NameSpaceRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return Rule::GetParentRule(aParentRule);
}
css::Rule*
NameSpaceRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
/* virtual */ size_t
NameSpaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mPrefix
// - mURLSpec
}
} // namespace css
} // namespace mozilla
// Must be outside namespace
DOMCI_DATA(CSSNameSpaceRule, css::NameSpaceRule)
// -------------------------------------------
// nsCSSFontFaceStyleDecl and related routines
//
// Mapping from nsCSSFontDesc codes to CSSFontFaceDescriptors fields.
nsCSSValue CSSFontFaceDescriptors::* const
CSSFontFaceDescriptors::Fields[] = {
#define CSS_FONT_DESC(name_, method_) &CSSFontFaceDescriptors::m##method_,
#include "nsCSSFontDescList.h"
#undef CSS_FONT_DESC
};
const nsCSSValue&
CSSFontFaceDescriptors::Get(nsCSSFontDesc aFontDescID) const
{
MOZ_ASSERT(aFontDescID > eCSSFontDesc_UNKNOWN &&
aFontDescID < eCSSFontDesc_COUNT);
return this->*CSSFontFaceDescriptors::Fields[aFontDescID];
}
nsCSSValue&
CSSFontFaceDescriptors::Get(nsCSSFontDesc aFontDescID)
{
MOZ_ASSERT(aFontDescID > eCSSFontDesc_UNKNOWN &&
aFontDescID < eCSSFontDesc_COUNT);
return this->*CSSFontFaceDescriptors::Fields[aFontDescID];
}
// QueryInterface implementation for nsCSSFontFaceStyleDecl
NS_INTERFACE_MAP_BEGIN(nsCSSFontFaceStyleDecl)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleDeclaration)
NS_INTERFACE_MAP_ENTRY(nsICSSDeclaration)
NS_INTERFACE_MAP_ENTRY(nsISupports)
// We forward the cycle collection interfaces to ContainingRule(), which is
// never null (in fact, we're part of that object!)
if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
return ContainingRule()->QueryInterface(aIID, aInstancePtr);
}
else
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF_USING_AGGREGATOR(nsCSSFontFaceStyleDecl, ContainingRule())
NS_IMPL_RELEASE_USING_AGGREGATOR(nsCSSFontFaceStyleDecl, ContainingRule())
// helper for string GetPropertyValue and RemovePropertyValue
nsresult
nsCSSFontFaceStyleDecl::GetPropertyValue(nsCSSFontDesc aFontDescID,
nsAString & aResult) const
{
NS_ENSURE_ARG_RANGE(aFontDescID, eCSSFontDesc_UNKNOWN,
eCSSFontDesc_COUNT - 1);
aResult.Truncate();
if (aFontDescID == eCSSFontDesc_UNKNOWN)
return NS_OK;
const nsCSSValue& val = mDescriptors.Get(aFontDescID);
if (val.GetUnit() == eCSSUnit_Null) {
// Avoid having to check no-value in the Family and Src cases below.
return NS_OK;
}
switch (aFontDescID) {
case eCSSFontDesc_Family: {
// we don't use nsCSSValue::AppendToString here because it doesn't
// canonicalize the way we want, and anyway it's overkill when
// we know we have eCSSUnit_String
NS_ASSERTION(val.GetUnit() == eCSSUnit_String, "unexpected unit");
nsDependentString family(val.GetStringBufferValue());
nsStyleUtil::AppendEscapedCSSString(family, aResult);
return NS_OK;
}
case eCSSFontDesc_Style:
val.AppendToString(eCSSProperty_font_style, aResult,
nsCSSValue::eNormalized);
return NS_OK;
case eCSSFontDesc_Weight:
val.AppendToString(eCSSProperty_font_weight, aResult,
nsCSSValue::eNormalized);
return NS_OK;
case eCSSFontDesc_Stretch:
val.AppendToString(eCSSProperty_font_stretch, aResult,
nsCSSValue::eNormalized);
return NS_OK;
case eCSSFontDesc_FontFeatureSettings:
nsStyleUtil::AppendFontFeatureSettings(val, aResult);
return NS_OK;
case eCSSFontDesc_FontLanguageOverride:
val.AppendToString(eCSSProperty_font_language_override, aResult,
nsCSSValue::eNormalized);
return NS_OK;
case eCSSFontDesc_Src:
nsStyleUtil::AppendSerializedFontSrc(val, aResult);
return NS_OK;
case eCSSFontDesc_UnicodeRange:
nsStyleUtil::AppendUnicodeRange(val, aResult);
return NS_OK;
case eCSSFontDesc_UNKNOWN:
case eCSSFontDesc_COUNT:
;
}
NS_NOTREACHED("nsCSSFontFaceStyleDecl::GetPropertyValue: "
"out-of-range value got to the switch");
return NS_ERROR_INVALID_ARG;
}
// attribute DOMString cssText;
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::GetCssText(nsAString & aCssText)
{
nsAutoString descStr;
aCssText.Truncate();
for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
id < eCSSFontDesc_COUNT;
id = nsCSSFontDesc(id + 1)) {
if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null &&
NS_SUCCEEDED(GetPropertyValue(id, descStr))) {
NS_ASSERTION(descStr.Length() > 0,
"GetCssText: non-null unit, empty property value");
aCssText.AppendLiteral(" ");
aCssText.AppendASCII(nsCSSProps::GetStringValue(id).get());
aCssText.AppendLiteral(": ");
aCssText.Append(descStr);
aCssText.AppendLiteral(";\n");
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::SetCssText(const nsAString & aCssText)
{
return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
}
// DOMString getPropertyValue (in DOMString propertyName);
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::GetPropertyValue(const nsAString & propertyName,
nsAString & aResult)
{
return GetPropertyValue(nsCSSProps::LookupFontDesc(propertyName), aResult);
}
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::GetAuthoredPropertyValue(const nsAString& propertyName,
nsAString& aResult)
{
// We don't return any authored property values different from
// GetPropertyValue, currently.
return GetPropertyValue(nsCSSProps::LookupFontDesc(propertyName), aResult);
}
// nsIDOMCSSValue getPropertyCSSValue (in DOMString propertyName);
already_AddRefed<dom::CSSValue>
nsCSSFontFaceStyleDecl::GetPropertyCSSValue(const nsAString & propertyName,
ErrorResult& aRv)
{
// ??? nsDOMCSSDeclaration returns null/NS_OK, but that seems wrong.
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
}
// DOMString removeProperty (in DOMString propertyName) raises (DOMException);
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::RemoveProperty(const nsAString & propertyName,
nsAString & aResult)
{
nsCSSFontDesc descID = nsCSSProps::LookupFontDesc(propertyName);
NS_ASSERTION(descID >= eCSSFontDesc_UNKNOWN &&
descID < eCSSFontDesc_COUNT,
"LookupFontDesc returned value out of range");
if (descID == eCSSFontDesc_UNKNOWN) {
aResult.Truncate();
} else {
nsresult rv = GetPropertyValue(descID, aResult);
NS_ENSURE_SUCCESS(rv, rv);
mDescriptors.Get(descID).Reset();
}
return NS_OK;
}
// DOMString getPropertyPriority (in DOMString propertyName);
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::GetPropertyPriority(const nsAString & propertyName,
nsAString & aResult)
{
// font descriptors do not have priorities at present
aResult.Truncate();
return NS_OK;
}
// void setProperty (in DOMString propertyName, in DOMString value,
// in DOMString priority) raises (DOMException);
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::SetProperty(const nsAString & propertyName,
const nsAString & value,
const nsAString & priority)
{
return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
}
// readonly attribute unsigned long length;
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::GetLength(uint32_t *aLength)
{
uint32_t len = 0;
for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
id < eCSSFontDesc_COUNT;
id = nsCSSFontDesc(id + 1))
if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null)
len++;
*aLength = len;
return NS_OK;
}
// DOMString item (in unsigned long index);
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::Item(uint32_t aIndex, nsAString& aReturn)
{
bool found;
IndexedGetter(aIndex, found, aReturn);
if (!found) {
aReturn.Truncate();
}
return NS_OK;
}
void
nsCSSFontFaceStyleDecl::IndexedGetter(uint32_t index, bool& aFound, nsAString & aResult)
{
int32_t nset = -1;
for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
id < eCSSFontDesc_COUNT;
id = nsCSSFontDesc(id + 1)) {
if (mDescriptors.Get(id).GetUnit() != eCSSUnit_Null) {
nset++;
if (nset == int32_t(index)) {
aFound = true;
aResult.AssignASCII(nsCSSProps::GetStringValue(id).get());
return;
}
}
}
aFound = false;
}
// readonly attribute nsIDOMCSSRule parentRule;
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::GetParentRule(nsIDOMCSSRule** aParentRule)
{
NS_IF_ADDREF(*aParentRule = ContainingRule()->GetDOMRule());
return NS_OK;
}
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::GetPropertyValue(const nsCSSProperty aPropID,
nsAString& aValue)
{
return
GetPropertyValue(NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aPropID)),
aValue);
}
NS_IMETHODIMP
nsCSSFontFaceStyleDecl::SetPropertyValue(const nsCSSProperty aPropID,
const nsAString& aValue)
{
return SetProperty(NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aPropID)),
aValue, EmptyString());
}
nsINode*
nsCSSFontFaceStyleDecl::GetParentObject()
{
return ContainingRule()->GetDocument();
}
JSObject*
nsCSSFontFaceStyleDecl::WrapObject(JSContext *cx)
{
return mozilla::dom::CSSStyleDeclarationBinding::Wrap(cx, this);
}
// -------------------------------------------
// nsCSSFontFaceRule
//
/* virtual */ already_AddRefed<css::Rule>
nsCSSFontFaceRule::Clone() const
{
nsRefPtr<css::Rule> clone = new nsCSSFontFaceRule(*this);
return clone.forget();
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSFontFaceRule)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSFontFaceRule)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSFontFaceRule)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsCSSFontFaceRule)
// 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.
tmp->mDecl.TraceWrapper(aCallbacks, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSFontFaceRule)
// 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.
tmp->mDecl.ReleaseWrapper(static_cast<nsISupports*>(p));
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSFontFaceRule)
// Just NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS here: that will call
// into our Trace hook, where we do the right thing with declarations
// already.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
DOMCI_DATA(CSSFontFaceRule, nsCSSFontFaceRule)
// QueryInterface implementation for nsCSSFontFaceRule
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSFontFaceRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSFontFaceRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSFontFaceRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT(nsCSSFontFaceRule, Rule)
#ifdef DEBUG
void
nsCSSFontFaceRule::List(FILE* out, int32_t aIndent) const
{
nsCString baseInd, descInd;
for (int32_t indent = aIndent; --indent >= 0; ) {
baseInd.AppendLiteral(" ");
descInd.AppendLiteral(" ");
}
descInd.AppendLiteral(" ");
nsString descStr;
fprintf(out, "%s@font-face {\n", baseInd.get());
for (nsCSSFontDesc id = nsCSSFontDesc(eCSSFontDesc_UNKNOWN + 1);
id < eCSSFontDesc_COUNT;
id = nsCSSFontDesc(id + 1))
if (mDecl.mDescriptors.Get(id).GetUnit() != eCSSUnit_Null) {
if (NS_FAILED(mDecl.GetPropertyValue(id, descStr)))
descStr.AssignLiteral("#<serialization error>");
else if (descStr.Length() == 0)
descStr.AssignLiteral("#<serialization missing>");
fprintf(out, "%s%s: %s\n",
descInd.get(), nsCSSProps::GetStringValue(id).get(),
NS_ConvertUTF16toUTF8(descStr).get());
}
fprintf(out, "%s}\n", baseInd.get());
}
#endif
/* virtual */ int32_t
nsCSSFontFaceRule::GetType() const
{
return Rule::FONT_FACE_RULE;
}
NS_IMETHODIMP
nsCSSFontFaceRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::FONT_FACE_RULE;
return NS_OK;
}
NS_IMETHODIMP
nsCSSFontFaceRule::GetCssText(nsAString& aCssText)
{
nsAutoString propText;
mDecl.GetCssText(propText);
aCssText.AssignLiteral("@font-face {\n");
aCssText.Append(propText);
aCssText.Append('}');
return NS_OK;
}
NS_IMETHODIMP
nsCSSFontFaceRule::SetCssText(const nsAString& aCssText)
{
return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
}
NS_IMETHODIMP
nsCSSFontFaceRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return Rule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
nsCSSFontFaceRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return Rule::GetParentRule(aParentRule);
}
css::Rule*
nsCSSFontFaceRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
NS_IMETHODIMP
nsCSSFontFaceRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
{
NS_IF_ADDREF(*aStyle = &mDecl);
return NS_OK;
}
// Arguably these should forward to nsCSSFontFaceStyleDecl methods.
void
nsCSSFontFaceRule::SetDesc(nsCSSFontDesc aDescID, nsCSSValue const & aValue)
{
NS_PRECONDITION(aDescID > eCSSFontDesc_UNKNOWN &&
aDescID < eCSSFontDesc_COUNT,
"aDescID out of range in nsCSSFontFaceRule::SetDesc");
// FIXME: handle dynamic changes
mDecl.mDescriptors.Get(aDescID) = aValue;
}
void
nsCSSFontFaceRule::GetDesc(nsCSSFontDesc aDescID, nsCSSValue & aValue)
{
NS_PRECONDITION(aDescID > eCSSFontDesc_UNKNOWN &&
aDescID < eCSSFontDesc_COUNT,
"aDescID out of range in nsCSSFontFaceRule::GetDesc");
aValue = mDecl.mDescriptors.Get(aDescID);
}
/* virtual */ size_t
nsCSSFontFaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mDecl
}
// -----------------------------------
// nsCSSFontFeatureValuesRule
//
/* virtual */ already_AddRefed<css::Rule>
nsCSSFontFeatureValuesRule::Clone() const
{
nsRefPtr<css::Rule> clone = new nsCSSFontFeatureValuesRule(*this);
return clone.forget();
}
NS_IMPL_ADDREF(nsCSSFontFeatureValuesRule)
NS_IMPL_RELEASE(nsCSSFontFeatureValuesRule)
DOMCI_DATA(CSSFontFeatureValuesRule, nsCSSFontFeatureValuesRule)
// QueryInterface implementation for nsCSSFontFeatureValuesRule
NS_INTERFACE_MAP_BEGIN(nsCSSFontFeatureValuesRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSFontFeatureValuesRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT(nsCSSFontFeatureValuesRule, Rule)
static void
FeatureValuesToString(
const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aFeatureValues,
nsAString& aOutStr)
{
uint32_t i, n;
// append values
n = aFeatureValues.Length();
for (i = 0; i < n; i++) {
const gfxFontFeatureValueSet::FeatureValues& fv = aFeatureValues[i];
// @alternate
aOutStr.AppendLiteral(" @");
nsAutoString functAlt;
nsStyleUtil::GetFunctionalAlternatesName(fv.alternate, functAlt);
aOutStr.Append(functAlt);
aOutStr.AppendLiteral(" {");
// for each ident-values tuple
uint32_t j, numValues = fv.valuelist.Length();
for (j = 0; j < numValues; j++) {
aOutStr.Append(' ');
const gfxFontFeatureValueSet::ValueList& vlist = fv.valuelist[j];
nsStyleUtil::AppendEscapedCSSIdent(vlist.name, aOutStr);
aOutStr.Append(':');
uint32_t k, numSelectors = vlist.featureSelectors.Length();
for (k = 0; k < numSelectors; k++) {
aOutStr.Append(' ');
aOutStr.AppendInt(vlist.featureSelectors[k]);
}
aOutStr.Append(';');
}
aOutStr.AppendLiteral(" }\n");
}
}
static void
FontFeatureValuesRuleToString(
const mozilla::FontFamilyList& aFamilyList,
const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aFeatureValues,
nsAString& aOutStr)
{
aOutStr.AssignLiteral("@font-feature-values ");
nsAutoString familyListStr, valueTextStr;
nsStyleUtil::AppendEscapedCSSFontFamilyList(aFamilyList, familyListStr);
aOutStr.Append(familyListStr);
aOutStr.AppendLiteral(" {\n");
FeatureValuesToString(aFeatureValues, valueTextStr);
aOutStr.Append(valueTextStr);
aOutStr.Append('}');
}
#ifdef DEBUG
void
nsCSSFontFeatureValuesRule::List(FILE* out, int32_t aIndent) const
{
nsAutoString text;
FontFeatureValuesRuleToString(mFamilyList, mFeatureValues, text);
NS_ConvertUTF16toUTF8 utf8(text);
// replace newlines with newlines plus indent spaces
char* indent = new char[(aIndent + 1) * 2];
int32_t i;
for (i = 1; i < (aIndent + 1) * 2 - 1; i++) {
indent[i] = 0x20;
}
indent[0] = 0xa;
indent[aIndent * 2 + 1] = 0;
utf8.ReplaceSubstring("\n", indent);
delete [] indent;
for (i = aIndent; --i >= 0; ) fputs(" ", out);
fprintf(out, "%s\n", utf8.get());
}
#endif
/* virtual */ int32_t
nsCSSFontFeatureValuesRule::GetType() const
{
return Rule::FONT_FEATURE_VALUES_RULE;
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::FONT_FEATURE_VALUES_RULE;
return NS_OK;
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::GetCssText(nsAString& aCssText)
{
FontFeatureValuesRuleToString(mFamilyList, mFeatureValues, aCssText);
return NS_OK;
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::SetCssText(const nsAString& aCssText)
{
// FIXME: implement???
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return Rule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return Rule::GetParentRule(aParentRule);
}
css::Rule*
nsCSSFontFeatureValuesRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::GetFontFamily(nsAString& aFamilyListStr)
{
nsStyleUtil::AppendEscapedCSSFontFamilyList(mFamilyList, aFamilyListStr);
return NS_OK;
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::SetFontFamily(const nsAString& aFontFamily)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::GetValueText(nsAString& aValueText)
{
FeatureValuesToString(mFeatureValues, aValueText);
return NS_OK;
}
NS_IMETHODIMP
nsCSSFontFeatureValuesRule::SetValueText(const nsAString& aValueText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
struct MakeFamilyArray {
explicit MakeFamilyArray(nsTArray<nsString>& aFamilyArray)
: familyArray(aFamilyArray), hasGeneric(false)
{}
static bool
AddFamily(const nsString& aFamily, bool aGeneric, void* aData)
{
MakeFamilyArray *familyArr = reinterpret_cast<MakeFamilyArray*> (aData);
if (!aGeneric && !aFamily.IsEmpty()) {
familyArr->familyArray.AppendElement(aFamily);
}
if (aGeneric) {
familyArr->hasGeneric = true;
}
return true;
}
nsTArray<nsString>& familyArray;
bool hasGeneric;
};
void
nsCSSFontFeatureValuesRule::SetFamilyList(
const mozilla::FontFamilyList& aFamilyList)
{
mFamilyList = aFamilyList;
}
void
nsCSSFontFeatureValuesRule::AddValueList(int32_t aVariantAlternate,
nsTArray<gfxFontFeatureValueSet::ValueList>& aValueList)
{
uint32_t i, len = mFeatureValues.Length();
bool foundAlternate = false;
// add to an existing list for a given property value
for (i = 0; i < len; i++) {
gfxFontFeatureValueSet::FeatureValues& f = mFeatureValues.ElementAt(i);
if (f.alternate == uint32_t(aVariantAlternate)) {
f.valuelist.AppendElements(aValueList);
foundAlternate = true;
break;
}
}
// create a new list for a given property value
if (!foundAlternate) {
gfxFontFeatureValueSet::FeatureValues &f = *mFeatureValues.AppendElement();
f.alternate = aVariantAlternate;
f.valuelist.AppendElements(aValueList);
}
}
size_t
nsCSSFontFeatureValuesRule::SizeOfIncludingThis(
MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this);
}
// -------------------------------------------
// nsCSSKeyframeStyleDeclaration
//
nsCSSKeyframeStyleDeclaration::nsCSSKeyframeStyleDeclaration(nsCSSKeyframeRule *aRule)
: mRule(aRule)
{
}
nsCSSKeyframeStyleDeclaration::~nsCSSKeyframeStyleDeclaration()
{
NS_ASSERTION(!mRule, "DropReference not called.");
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSKeyframeStyleDeclaration)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSKeyframeStyleDeclaration)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsCSSKeyframeStyleDeclaration)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSKeyframeStyleDeclaration)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
css::Declaration*
nsCSSKeyframeStyleDeclaration::GetCSSDeclaration(bool aAllocate)
{
if (mRule) {
return mRule->Declaration();
} else {
return nullptr;
}
}
void
nsCSSKeyframeStyleDeclaration::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv)
{
GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv);
}
NS_IMETHODIMP
nsCSSKeyframeStyleDeclaration::GetParentRule(nsIDOMCSSRule **aParent)
{
NS_ENSURE_ARG_POINTER(aParent);
NS_IF_ADDREF(*aParent = mRule);
return NS_OK;
}
nsresult
nsCSSKeyframeStyleDeclaration::SetCSSDeclaration(css::Declaration* aDecl)
{
NS_ABORT_IF_FALSE(aDecl, "must be non-null");
mRule->ChangeDeclaration(aDecl);
return NS_OK;
}
nsIDocument*
nsCSSKeyframeStyleDeclaration::DocToUpdate()
{
return nullptr;
}
nsINode*
nsCSSKeyframeStyleDeclaration::GetParentObject()
{
return mRule ? mRule->GetDocument() : nullptr;
}
// -------------------------------------------
// nsCSSKeyframeRule
//
nsCSSKeyframeRule::nsCSSKeyframeRule(const nsCSSKeyframeRule& aCopy)
// copy everything except our reference count and mDOMDeclaration
: Rule(aCopy)
, mKeys(aCopy.mKeys)
, mDeclaration(new css::Declaration(*aCopy.mDeclaration))
{
}
nsCSSKeyframeRule::~nsCSSKeyframeRule()
{
if (mDOMDeclaration) {
mDOMDeclaration->DropReference();
}
}
/* virtual */ already_AddRefed<css::Rule>
nsCSSKeyframeRule::Clone() const
{
nsRefPtr<css::Rule> clone = new nsCSSKeyframeRule(*this);
return clone.forget();
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSKeyframeRule)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSKeyframeRule)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSKeyframeRule)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSKeyframeRule)
if (tmp->mDOMDeclaration) {
tmp->mDOMDeclaration->DropReference();
tmp->mDOMDeclaration = nullptr;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSKeyframeRule)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMDeclaration)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
DOMCI_DATA(MozCSSKeyframeRule, nsCSSKeyframeRule)
// QueryInterface implementation for nsCSSKeyframeRule
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSKeyframeRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMMozCSSKeyframeRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCSSKeyframeRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(nsCSSKeyframeRule, Rule)
/* virtual */ void
nsCSSKeyframeRule::MapRuleInfoInto(nsRuleData* aRuleData)
{
// We need to implement MapRuleInfoInto because the animation manager
// constructs a rule node pointing to us in order to compute the
// styles it needs to animate.
// The spec says that !important declarations should just be ignored
NS_ASSERTION(!mDeclaration->HasImportantData(),
"Keyframe rules has !important data");
mDeclaration->MapNormalRuleInfoInto(aRuleData);
}
#ifdef DEBUG
void
nsCSSKeyframeRule::List(FILE* out, int32_t aIndent) const
{
for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out);
nsAutoString tmp;
DoGetKeyText(tmp);
fputs(NS_ConvertUTF16toUTF8(tmp).get(), out);
fputs(" ", out);
mDeclaration->List(out, aIndent);
fputs("\n", out);
}
#endif
/* virtual */ int32_t
nsCSSKeyframeRule::GetType() const
{
return Rule::KEYFRAME_RULE;
}
NS_IMETHODIMP
nsCSSKeyframeRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::KEYFRAME_RULE;
return NS_OK;
}
NS_IMETHODIMP
nsCSSKeyframeRule::GetCssText(nsAString& aCssText)
{
DoGetKeyText(aCssText);
aCssText.AppendLiteral(" { ");
nsAutoString tmp;
mDeclaration->ToString(tmp);
aCssText.Append(tmp);
aCssText.AppendLiteral(" }");
return NS_OK;
}
NS_IMETHODIMP
nsCSSKeyframeRule::SetCssText(const nsAString& aCssText)
{
// FIXME: implement???
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCSSKeyframeRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return Rule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
nsCSSKeyframeRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return Rule::GetParentRule(aParentRule);
}
css::Rule*
nsCSSKeyframeRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
NS_IMETHODIMP
nsCSSKeyframeRule::GetKeyText(nsAString& aKeyText)
{
DoGetKeyText(aKeyText);
return NS_OK;
}
void
nsCSSKeyframeRule::DoGetKeyText(nsAString& aKeyText) const
{
aKeyText.Truncate();
uint32_t i = 0, i_end = mKeys.Length();
NS_ABORT_IF_FALSE(i_end != 0, "must have some keys");
for (;;) {
aKeyText.AppendFloat(mKeys[i] * 100.0f);
aKeyText.Append(char16_t('%'));
if (++i == i_end) {
break;
}
aKeyText.AppendLiteral(", ");
}
}
NS_IMETHODIMP
nsCSSKeyframeRule::SetKeyText(const nsAString& aKeyText)
{
nsCSSParser parser;
InfallibleTArray<float> newSelectors;
// FIXME: pass filename and line number
if (!parser.ParseKeyframeSelectorString(aKeyText, nullptr, 0, newSelectors)) {
// for now, we don't do anything if the parse fails
return NS_OK;
}
nsIDocument* doc = GetDocument();
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
newSelectors.SwapElements(mKeys);
CSSStyleSheet* sheet = GetStyleSheet();
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 09:40:24 -07:00
if (sheet) {
sheet->SetModifiedByChildRule();
if (doc) {
doc->StyleRuleChanged(sheet, this, this);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCSSKeyframeRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
{
if (!mDOMDeclaration) {
mDOMDeclaration = new nsCSSKeyframeStyleDeclaration(this);
}
NS_ADDREF(*aStyle = mDOMDeclaration);
return NS_OK;
}
void
nsCSSKeyframeRule::ChangeDeclaration(css::Declaration* aDeclaration)
{
// Our caller already did a BeginUpdate/EndUpdate, but with
// UPDATE_CONTENT, and we need UPDATE_STYLE to trigger work in
// PresShell::EndUpdate.
nsIDocument* doc = GetDocument();
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
// Be careful to not assign to an nsAutoPtr if we would be assigning
// the thing it already holds.
if (aDeclaration != mDeclaration) {
mDeclaration = aDeclaration;
}
CSSStyleSheet* sheet = GetStyleSheet();
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 09:40:24 -07:00
if (sheet) {
sheet->SetModifiedByChildRule();
if (doc) {
doc->StyleRuleChanged(sheet, this, this);
}
}
}
/* virtual */ size_t
nsCSSKeyframeRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mKeys
// - mDeclaration
// - mDOMDeclaration
}
// -------------------------------------------
// nsCSSKeyframesRule
//
nsCSSKeyframesRule::nsCSSKeyframesRule(const nsCSSKeyframesRule& aCopy)
// copy everything except our reference count. GroupRule's copy
// constructor also doesn't copy the lazily-constructed
// mRuleCollection.
: GroupRule(aCopy),
mName(aCopy.mName)
{
}
nsCSSKeyframesRule::~nsCSSKeyframesRule()
{
}
/* virtual */ already_AddRefed<css::Rule>
nsCSSKeyframesRule::Clone() const
{
nsRefPtr<css::Rule> clone = new nsCSSKeyframesRule(*this);
return clone.forget();
}
NS_IMPL_ADDREF_INHERITED(nsCSSKeyframesRule, css::GroupRule)
NS_IMPL_RELEASE_INHERITED(nsCSSKeyframesRule, css::GroupRule)
DOMCI_DATA(MozCSSKeyframesRule, nsCSSKeyframesRule)
// QueryInterface implementation for nsCSSKeyframesRule
NS_INTERFACE_MAP_BEGIN(nsCSSKeyframesRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMMozCSSKeyframesRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCSSKeyframesRule)
NS_INTERFACE_MAP_END_INHERITING(GroupRule)
#ifdef DEBUG
void
nsCSSKeyframesRule::List(FILE* out, int32_t aIndent) const
{
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
fprintf(out, "@keyframes %s", NS_ConvertUTF16toUTF8(mName).get());
GroupRule::List(out, aIndent);
}
#endif
/* virtual */ int32_t
nsCSSKeyframesRule::GetType() const
{
return Rule::KEYFRAMES_RULE;
}
NS_IMETHODIMP
nsCSSKeyframesRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::KEYFRAMES_RULE;
return NS_OK;
}
NS_IMETHODIMP
nsCSSKeyframesRule::GetCssText(nsAString& aCssText)
{
aCssText.AssignLiteral("@keyframes ");
aCssText.Append(mName);
aCssText.AppendLiteral(" {\n");
nsAutoString tmp;
for (uint32_t i = 0, i_end = mRules.Count(); i != i_end; ++i) {
static_cast<nsCSSKeyframeRule*>(mRules[i])->GetCssText(tmp);
aCssText.Append(tmp);
aCssText.Append('\n');
}
aCssText.Append('}');
return NS_OK;
}
NS_IMETHODIMP
nsCSSKeyframesRule::SetCssText(const nsAString& aCssText)
{
// FIXME: implement???
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCSSKeyframesRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return GroupRule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
nsCSSKeyframesRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return GroupRule::GetParentRule(aParentRule);
}
css::Rule*
nsCSSKeyframesRule::GetCSSRule()
{
return GroupRule::GetCSSRule();
}
NS_IMETHODIMP
nsCSSKeyframesRule::GetName(nsAString& aName)
{
aName = mName;
return NS_OK;
}
NS_IMETHODIMP
nsCSSKeyframesRule::SetName(const nsAString& aName)
{
if (mName == aName) {
return NS_OK;
}
nsIDocument* doc = GetDocument();
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
mName = aName;
CSSStyleSheet* sheet = GetStyleSheet();
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 09:40:24 -07:00
if (sheet) {
sheet->SetModifiedByChildRule();
if (doc) {
doc->StyleRuleChanged(sheet, this, this);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCSSKeyframesRule::GetCssRules(nsIDOMCSSRuleList* *aRuleList)
{
return GroupRule::GetCssRules(aRuleList);
}
NS_IMETHODIMP
nsCSSKeyframesRule::AppendRule(const nsAString& aRule)
{
// The spec is confusing, and I think we should just append the rule,
// which also turns out to match WebKit:
// http://lists.w3.org/Archives/Public/www-style/2011Apr/0034.html
nsCSSParser parser;
// FIXME: pass filename and line number
nsRefPtr<nsCSSKeyframeRule> rule =
parser.ParseKeyframeRule(aRule, nullptr, 0);
if (rule) {
nsIDocument* doc = GetDocument();
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
AppendStyleRule(rule);
CSSStyleSheet* sheet = GetStyleSheet();
if (sheet) {
sheet->SetModifiedByChildRule();
if (doc) {
doc->StyleRuleChanged(sheet, this, this);
}
}
}
return NS_OK;
}
static const uint32_t RULE_NOT_FOUND = uint32_t(-1);
uint32_t
nsCSSKeyframesRule::FindRuleIndexForKey(const nsAString& aKey)
{
nsCSSParser parser;
InfallibleTArray<float> keys;
// FIXME: pass filename and line number
if (parser.ParseKeyframeSelectorString(aKey, nullptr, 0, keys)) {
// The spec isn't clear, but we'll match on the key list, which
// mostly matches what WebKit does, except we'll do last-match
// instead of first-match, and handling parsing differences better.
// http://lists.w3.org/Archives/Public/www-style/2011Apr/0036.html
// http://lists.w3.org/Archives/Public/www-style/2011Apr/0037.html
for (uint32_t i = mRules.Count(); i-- != 0; ) {
if (static_cast<nsCSSKeyframeRule*>(mRules[i])->GetKeys() == keys) {
return i;
}
}
}
return RULE_NOT_FOUND;
}
NS_IMETHODIMP
nsCSSKeyframesRule::DeleteRule(const nsAString& aKey)
{
uint32_t index = FindRuleIndexForKey(aKey);
if (index != RULE_NOT_FOUND) {
nsIDocument* doc = GetDocument();
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
mRules.RemoveObjectAt(index);
CSSStyleSheet* sheet = GetStyleSheet();
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 09:40:24 -07:00
if (sheet) {
sheet->SetModifiedByChildRule();
if (doc) {
doc->StyleRuleChanged(sheet, this, this);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCSSKeyframesRule::FindRule(const nsAString& aKey,
nsIDOMMozCSSKeyframeRule** aResult)
{
uint32_t index = FindRuleIndexForKey(aKey);
if (index == RULE_NOT_FOUND) {
*aResult = nullptr;
} else {
NS_ADDREF(*aResult = static_cast<nsCSSKeyframeRule*>(mRules[index]));
}
return NS_OK;
}
// GroupRule interface
/* virtual */ bool
nsCSSKeyframesRule::UseForPresentation(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey& aKey)
{
NS_ABORT_IF_FALSE(false, "should not be called");
return false;
}
/* virtual */ size_t
nsCSSKeyframesRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
n += GroupRule::SizeOfExcludingThis(aMallocSizeOf);
// Measurement of the following members may be added later if DMD finds it is
// worthwhile:
// - mName
return n;
}
// -------------------------------------------
// nsCSSPageStyleDeclaration
//
nsCSSPageStyleDeclaration::nsCSSPageStyleDeclaration(nsCSSPageRule* aRule)
: mRule(aRule)
{
}
nsCSSPageStyleDeclaration::~nsCSSPageStyleDeclaration()
{
NS_ASSERTION(!mRule, "DropReference not called.");
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSPageStyleDeclaration)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSPageStyleDeclaration)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsCSSPageStyleDeclaration)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSPageStyleDeclaration)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
css::Declaration*
nsCSSPageStyleDeclaration::GetCSSDeclaration(bool aAllocate)
{
if (mRule) {
return mRule->Declaration();
} else {
return nullptr;
}
}
void
nsCSSPageStyleDeclaration::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv)
{
GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv);
}
NS_IMETHODIMP
nsCSSPageStyleDeclaration::GetParentRule(nsIDOMCSSRule** aParent)
{
NS_ENSURE_ARG_POINTER(aParent);
NS_IF_ADDREF(*aParent = mRule);
return NS_OK;
}
nsresult
nsCSSPageStyleDeclaration::SetCSSDeclaration(css::Declaration* aDecl)
{
NS_ABORT_IF_FALSE(aDecl, "must be non-null");
mRule->ChangeDeclaration(aDecl);
return NS_OK;
}
nsIDocument*
nsCSSPageStyleDeclaration::DocToUpdate()
{
return nullptr;
}
nsINode*
nsCSSPageStyleDeclaration::GetParentObject()
{
return mRule ? mRule->GetDocument() : nullptr;
}
// -------------------------------------------
// nsCSSPageRule
//
nsCSSPageRule::nsCSSPageRule(const nsCSSPageRule& aCopy)
// copy everything except our reference count and mDOMDeclaration
: Rule(aCopy)
, mDeclaration(new css::Declaration(*aCopy.mDeclaration))
{
}
nsCSSPageRule::~nsCSSPageRule()
{
if (mDOMDeclaration) {
mDOMDeclaration->DropReference();
}
}
/* virtual */ already_AddRefed<css::Rule>
nsCSSPageRule::Clone() const
{
nsRefPtr<css::Rule> clone = new nsCSSPageRule(*this);
return clone.forget();
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSPageRule)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSPageRule)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSPageRule)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSPageRule)
if (tmp->mDOMDeclaration) {
tmp->mDOMDeclaration->DropReference();
tmp->mDOMDeclaration = nullptr;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSPageRule)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMDeclaration)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
DOMCI_DATA(CSSPageRule, nsCSSPageRule)
// QueryInterface implementation for nsCSSPageRule
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSPageRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSPageRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSPageRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(nsCSSPageRule, Rule)
#ifdef DEBUG
void
nsCSSPageRule::List(FILE* out, int32_t aIndent) const
{
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
fputs("@page ", out);
mDeclaration->List(out, aIndent);
fputs("\n", out);
}
#endif
/* virtual */ int32_t
nsCSSPageRule::GetType() const
{
return Rule::PAGE_RULE;
}
NS_IMETHODIMP
nsCSSPageRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::PAGE_RULE;
return NS_OK;
}
NS_IMETHODIMP
nsCSSPageRule::GetCssText(nsAString& aCssText)
{
aCssText.AppendLiteral("@page { ");
nsAutoString tmp;
mDeclaration->ToString(tmp);
aCssText.Append(tmp);
aCssText.AppendLiteral(" }");
return NS_OK;
}
NS_IMETHODIMP
nsCSSPageRule::SetCssText(const nsAString& aCssText)
{
// FIXME: implement???
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCSSPageRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return Rule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
nsCSSPageRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return Rule::GetParentRule(aParentRule);
}
css::Rule*
nsCSSPageRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
css::ImportantRule*
nsCSSPageRule::GetImportantRule()
{
if (!mDeclaration->HasImportantData()) {
return nullptr;
}
if (!mImportantRule) {
mImportantRule = new css::ImportantRule(mDeclaration);
}
return mImportantRule;
}
/* virtual */ void
nsCSSPageRule::MapRuleInfoInto(nsRuleData* aRuleData)
{
mDeclaration->MapNormalRuleInfoInto(aRuleData);
}
NS_IMETHODIMP
nsCSSPageRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
{
if (!mDOMDeclaration) {
mDOMDeclaration = new nsCSSPageStyleDeclaration(this);
}
NS_ADDREF(*aStyle = mDOMDeclaration);
return NS_OK;
}
void
nsCSSPageRule::ChangeDeclaration(css::Declaration* aDeclaration)
{
mImportantRule = nullptr;
// Be careful to not assign to an nsAutoPtr if we would be assigning
// the thing it already holds.
if (aDeclaration != mDeclaration) {
mDeclaration = aDeclaration;
}
CSSStyleSheet* sheet = GetStyleSheet();
if (sheet) {
sheet->SetModifiedByChildRule();
}
}
/* virtual */ size_t
nsCSSPageRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this);
}
namespace mozilla {
CSSSupportsRule::CSSSupportsRule(bool aConditionMet,
const nsString& aCondition,
uint32_t aLineNumber, uint32_t aColumnNumber)
: css::GroupRule(aLineNumber, aColumnNumber)
, mUseGroup(aConditionMet)
, mCondition(aCondition)
{
}
CSSSupportsRule::~CSSSupportsRule()
{
}
CSSSupportsRule::CSSSupportsRule(const CSSSupportsRule& aCopy)
: css::GroupRule(aCopy),
mUseGroup(aCopy.mUseGroup),
mCondition(aCopy.mCondition)
{
}
#ifdef DEBUG
/* virtual */ void
CSSSupportsRule::List(FILE* out, int32_t aIndent) const
{
for (int32_t indent = aIndent; --indent >= 0; ) fputs(" ", out);
fputs("@supports ", out);
fputs(NS_ConvertUTF16toUTF8(mCondition).get(), out);
css::GroupRule::List(out, aIndent);
}
#endif
/* virtual */ int32_t
CSSSupportsRule::GetType() const
{
return Rule::SUPPORTS_RULE;
}
/* virtual */ already_AddRefed<mozilla::css::Rule>
CSSSupportsRule::Clone() const
{
nsRefPtr<css::Rule> clone = new CSSSupportsRule(*this);
return clone.forget();
}
/* virtual */ bool
CSSSupportsRule::UseForPresentation(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey& aKey)
{
return mUseGroup;
}
NS_IMPL_ADDREF_INHERITED(CSSSupportsRule, css::GroupRule)
NS_IMPL_RELEASE_INHERITED(CSSSupportsRule, css::GroupRule)
// QueryInterface implementation for CSSSupportsRule
NS_INTERFACE_MAP_BEGIN(CSSSupportsRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSSupportsRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSSupportsRule)
NS_INTERFACE_MAP_END_INHERITING(GroupRule)
// nsIDOMCSSRule methods
NS_IMETHODIMP
CSSSupportsRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::SUPPORTS_RULE;
return NS_OK;
}
NS_IMETHODIMP
CSSSupportsRule::GetCssText(nsAString& aCssText)
{
aCssText.AssignLiteral("@supports ");
aCssText.Append(mCondition);
css::GroupRule::AppendRulesToCssText(aCssText);
return NS_OK;
}
NS_IMETHODIMP
CSSSupportsRule::SetCssText(const nsAString& aCssText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
CSSSupportsRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return css::GroupRule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
CSSSupportsRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return css::GroupRule::GetParentRule(aParentRule);
}
css::Rule*
CSSSupportsRule::GetCSSRule()
{
return css::GroupRule::GetCSSRule();
}
// nsIDOMCSSGroupingRule methods
NS_IMETHODIMP
CSSSupportsRule::GetCssRules(nsIDOMCSSRuleList* *aRuleList)
{
return css::GroupRule::GetCssRules(aRuleList);
}
NS_IMETHODIMP
CSSSupportsRule::InsertRule(const nsAString & aRule, uint32_t aIndex, uint32_t* _retval)
{
return css::GroupRule::InsertRule(aRule, aIndex, _retval);
}
NS_IMETHODIMP
CSSSupportsRule::DeleteRule(uint32_t aIndex)
{
return css::GroupRule::DeleteRule(aIndex);
}
// nsIDOMCSSConditionRule methods
NS_IMETHODIMP
CSSSupportsRule::GetConditionText(nsAString& aConditionText)
{
aConditionText.Assign(mCondition);
return NS_OK;
}
NS_IMETHODIMP
CSSSupportsRule::SetConditionText(const nsAString& aConditionText)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
/* virtual */ size_t
CSSSupportsRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = aMallocSizeOf(this);
n += css::GroupRule::SizeOfExcludingThis(aMallocSizeOf);
n += mCondition.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
return n;
}
} // namespace mozilla
// Must be outside namespace
DOMCI_DATA(CSSSupportsRule, mozilla::CSSSupportsRule)
// -------------------------------------------
// nsCSSCounterStyleRule
//
nsCSSCounterStyleRule::nsCSSCounterStyleRule(const nsCSSCounterStyleRule& aCopy)
: Rule(aCopy)
, mName(aCopy.mName)
, mGeneration(aCopy.mGeneration)
{
for (size_t i = 0; i < ArrayLength(mValues); ++i) {
mValues[i] = aCopy.mValues[i];
}
}
nsCSSCounterStyleRule::~nsCSSCounterStyleRule()
{
}
/* virtual */ already_AddRefed<css::Rule>
nsCSSCounterStyleRule::Clone() const
{
nsRefPtr<css::Rule> clone = new nsCSSCounterStyleRule(*this);
return clone.forget();
}
nsCSSCounterStyleRule::Getter const
nsCSSCounterStyleRule::kGetters[] = {
#define CSS_COUNTER_DESC(name_, method_) &nsCSSCounterStyleRule::Get##method_,
#include "nsCSSCounterDescList.h"
#undef CSS_COUNTER_DESC
};
NS_IMPL_ADDREF(nsCSSCounterStyleRule)
NS_IMPL_RELEASE(nsCSSCounterStyleRule)
DOMCI_DATA(CSSCounterStyleRule, nsCSSCounterStyleRule)
// QueryInterface implementation for nsCSSCounterStyleRule
NS_INTERFACE_MAP_BEGIN(nsCSSCounterStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSCounterStyleRule)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSCounterStyleRule)
NS_INTERFACE_MAP_END
IMPL_STYLE_RULE_INHERIT(nsCSSCounterStyleRule, css::Rule)
#ifdef DEBUG
void
nsCSSCounterStyleRule::List(FILE* out, int32_t aIndent) const
{
nsCString baseInd, descInd;
for (int32_t indent = aIndent; --indent >= 0; ) {
baseInd.AppendLiteral(" ");
}
descInd = baseInd;
descInd.AppendLiteral(" ");
fprintf(out, "%s@counter-style %s (rev.%u) {\n",
baseInd.get(), NS_ConvertUTF16toUTF8(mName).get(), mGeneration);
// TODO
fprintf(out, "%s}\n", baseInd.get());
}
#endif
/* virtual */ int32_t
nsCSSCounterStyleRule::GetType() const
{
return Rule::COUNTER_STYLE_RULE;
}
// nsIDOMCSSRule methods
NS_IMETHODIMP
nsCSSCounterStyleRule::GetType(uint16_t* aType)
{
*aType = nsIDOMCSSRule::COUNTER_STYLE_RULE;
return NS_OK;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::GetCssText(nsAString& aCssText)
{
aCssText.AssignLiteral(MOZ_UTF16("@counter-style "));
nsStyleUtil::AppendEscapedCSSIdent(mName, aCssText);
aCssText.AppendLiteral(MOZ_UTF16(" {\n"));
for (nsCSSCounterDesc id = nsCSSCounterDesc(0);
id < eCSSCounterDesc_COUNT;
id = nsCSSCounterDesc(id + 1)) {
if (mValues[id].GetUnit() != eCSSUnit_Null) {
nsAutoString tmp;
(this->*kGetters[id])(tmp);
aCssText.AppendLiteral(MOZ_UTF16(" "));
AppendASCIItoUTF16(nsCSSProps::GetStringValue(id), aCssText);
aCssText.AppendLiteral(MOZ_UTF16(": "));
aCssText.Append(tmp);
aCssText.AppendLiteral(MOZ_UTF16(";\n"));
}
}
aCssText.AppendLiteral(MOZ_UTF16("}"));
return NS_OK;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::SetCssText(const nsAString& aCssText)
{
// FIXME: implement???
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
{
return Rule::GetParentStyleSheet(aSheet);
}
NS_IMETHODIMP
nsCSSCounterStyleRule::GetParentRule(nsIDOMCSSRule** aParentRule)
{
return Rule::GetParentRule(aParentRule);
}
css::Rule*
nsCSSCounterStyleRule::GetCSSRule()
{
return Rule::GetCSSRule();
}
// nsIDOMCSSCounterStyleRule methods
NS_IMETHODIMP
nsCSSCounterStyleRule::GetName(nsAString& aName)
{
aName.Truncate();
nsStyleUtil::AppendEscapedCSSIdent(mName, aName);
return NS_OK;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::SetName(const nsAString& aName)
{
nsCSSParser parser;
nsAutoString name;
if (parser.ParseCounterStyleName(aName, nullptr, name)) {
nsIDocument* doc = GetDocument();
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
mName = name;
CSSStyleSheet* sheet = GetStyleSheet();
if (sheet) {
sheet->SetModifiedByChildRule();
if (doc) {
doc->StyleRuleChanged(sheet, this, this);
}
}
}
return NS_OK;
}
int32_t
nsCSSCounterStyleRule::GetSystem() const
{
const nsCSSValue& system = GetDesc(eCSSCounterDesc_System);
switch (system.GetUnit()) {
case eCSSUnit_Enumerated:
return system.GetIntValue();
case eCSSUnit_Pair:
return system.GetPairValue().mXValue.GetIntValue();
default:
return NS_STYLE_COUNTER_SYSTEM_SYMBOLIC;
}
}
const nsCSSValue&
nsCSSCounterStyleRule::GetSystemArgument() const
{
const nsCSSValue& system = GetDesc(eCSSCounterDesc_System);
NS_ABORT_IF_FALSE(system.GetUnit() == eCSSUnit_Pair,
"Invalid system value");
return system.GetPairValue().mYValue;
}
void
nsCSSCounterStyleRule::SetDesc(nsCSSCounterDesc aDescID, const nsCSSValue& aValue)
{
NS_ABORT_IF_FALSE(aDescID >= 0 && aDescID < eCSSCounterDesc_COUNT,
"descriptor ID out of range");
nsIDocument* doc = GetDocument();
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
mValues[aDescID] = aValue;
mGeneration++;
CSSStyleSheet* sheet = GetStyleSheet();
if (sheet) {
sheet->SetModifiedByChildRule();
if (doc) {
doc->StyleRuleChanged(sheet, this, this);
}
}
}
NS_IMETHODIMP
nsCSSCounterStyleRule::GetSystem(nsAString& aSystem)
{
const nsCSSValue& value = GetDesc(eCSSCounterDesc_System);
if (value.GetUnit() == eCSSUnit_Null) {
aSystem.Truncate();
return NS_OK;
}
aSystem = NS_ConvertASCIItoUTF16(nsCSSProps::ValueToKeyword(
GetSystem(), nsCSSProps::kCounterSystemKTable));
if (value.GetUnit() == eCSSUnit_Pair) {
aSystem.Append(' ');
GetSystemArgument().AppendToString(
eCSSProperty_UNKNOWN, aSystem, nsCSSValue::eNormalized);
}
return NS_OK;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::GetSymbols(nsAString& aSymbols)
{
const nsCSSValue& value = GetDesc(eCSSCounterDesc_Symbols);
aSymbols.Truncate();
if (value.GetUnit() == eCSSUnit_List) {
for (const nsCSSValueList* item = value.GetListValue();
item; item = item->mNext) {
item->mValue.AppendToString(eCSSProperty_UNKNOWN,
aSymbols,
nsCSSValue::eNormalized);
if (item->mNext) {
aSymbols.Append(' ');
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::GetAdditiveSymbols(nsAString& aSymbols)
{
const nsCSSValue& value = GetDesc(eCSSCounterDesc_AdditiveSymbols);
aSymbols.Truncate();
if (value.GetUnit() == eCSSUnit_PairList) {
for (const nsCSSValuePairList* item = value.GetPairListValue();
item; item = item->mNext) {
item->mXValue.AppendToString(eCSSProperty_UNKNOWN,
aSymbols, nsCSSValue::eNormalized);
aSymbols.Append(' ');
item->mYValue.AppendToString(eCSSProperty_UNKNOWN,
aSymbols, nsCSSValue::eNormalized);
if (item->mNext) {
aSymbols.AppendLiteral(", ");
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::GetRange(nsAString& aRange)
{
const nsCSSValue& value = GetDesc(eCSSCounterDesc_Range);
switch (value.GetUnit()) {
case eCSSUnit_Auto:
aRange.AssignLiteral(MOZ_UTF16("auto"));
break;
case eCSSUnit_PairList:
aRange.Truncate();
for (const nsCSSValuePairList* item = value.GetPairListValue();
item; item = item->mNext) {
const nsCSSValue& lower = item->mXValue;
const nsCSSValue& upper = item->mYValue;
if (lower.GetUnit() == eCSSUnit_Enumerated) {
NS_ASSERTION(lower.GetIntValue() ==
NS_STYLE_COUNTER_RANGE_INFINITE,
"Unrecognized keyword");
aRange.AppendLiteral("infinite");
} else {
aRange.AppendInt(lower.GetIntValue());
}
aRange.Append(' ');
if (upper.GetUnit() == eCSSUnit_Enumerated) {
NS_ASSERTION(upper.GetIntValue() ==
NS_STYLE_COUNTER_RANGE_INFINITE,
"Unrecognized keyword");
aRange.AppendLiteral("infinite");
} else {
aRange.AppendInt(upper.GetIntValue());
}
if (item->mNext) {
aRange.AppendLiteral(", ");
}
}
break;
default:
aRange.Truncate();
}
return NS_OK;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::GetSpeakAs(nsAString& aSpeakAs)
{
const nsCSSValue& value = GetDesc(eCSSCounterDesc_SpeakAs);
switch (value.GetUnit()) {
case eCSSUnit_Enumerated:
switch (value.GetIntValue()) {
case NS_STYLE_COUNTER_SPEAKAS_BULLETS:
aSpeakAs.AssignLiteral(MOZ_UTF16("bullets"));
break;
case NS_STYLE_COUNTER_SPEAKAS_NUMBERS:
aSpeakAs.AssignLiteral(MOZ_UTF16("numbers"));
break;
case NS_STYLE_COUNTER_SPEAKAS_WORDS:
aSpeakAs.AssignLiteral(MOZ_UTF16("words"));
break;
case NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT:
aSpeakAs.AssignLiteral(MOZ_UTF16("spell-out"));
break;
default:
NS_NOTREACHED("Unknown speech synthesis");
}
break;
case eCSSUnit_Auto:
case eCSSUnit_Ident:
aSpeakAs.Truncate();
value.AppendToString(eCSSProperty_UNKNOWN,
aSpeakAs, nsCSSValue::eNormalized);
break;
default:
aSpeakAs.Truncate();
}
return NS_OK;
}
nsresult
nsCSSCounterStyleRule::GetDescriptor(nsCSSCounterDesc aDescID,
nsAString& aValue)
{
NS_ASSERTION(aDescID == eCSSCounterDesc_Negative ||
aDescID == eCSSCounterDesc_Prefix ||
aDescID == eCSSCounterDesc_Suffix ||
aDescID == eCSSCounterDesc_Pad ||
aDescID == eCSSCounterDesc_Fallback,
"Unexpected descriptor");
const nsCSSValue& value = GetDesc(aDescID);
aValue.Truncate();
if (value.GetUnit() != eCSSUnit_Null) {
value.AppendToString(
eCSSProperty_UNKNOWN, aValue, nsCSSValue::eNormalized);
}
return NS_OK;
}
#define CSS_COUNTER_DESC_GETTER(name_) \
NS_IMETHODIMP \
nsCSSCounterStyleRule::Get##name_(nsAString& a##name_) \
{ \
return GetDescriptor(eCSSCounterDesc_##name_, a##name_);\
}
CSS_COUNTER_DESC_GETTER(Negative)
CSS_COUNTER_DESC_GETTER(Prefix)
CSS_COUNTER_DESC_GETTER(Suffix)
CSS_COUNTER_DESC_GETTER(Pad)
CSS_COUNTER_DESC_GETTER(Fallback)
#undef CSS_COUNTER_DESC_GETTER
/* static */ bool
nsCSSCounterStyleRule::CheckDescValue(int32_t aSystem,
nsCSSCounterDesc aDescID,
const nsCSSValue& aValue)
{
switch (aDescID) {
case eCSSCounterDesc_System:
if (aValue.GetUnit() != eCSSUnit_Pair) {
return aValue.GetIntValue() == aSystem;
} else {
return aValue.GetPairValue().mXValue.GetIntValue() == aSystem;
}
case eCSSCounterDesc_Symbols:
switch (aSystem) {
case NS_STYLE_COUNTER_SYSTEM_NUMERIC:
case NS_STYLE_COUNTER_SYSTEM_ALPHABETIC:
// for these two system, the list must contain at least 2 elements
return aValue.GetListValue()->mNext;
case NS_STYLE_COUNTER_SYSTEM_EXTENDS:
// for extends system, no symbols should be set
return false;
default:
return true;
}
case eCSSCounterDesc_AdditiveSymbols:
switch (aSystem) {
case NS_STYLE_COUNTER_SYSTEM_EXTENDS:
return false;
default:
return true;
}
default:
return true;
}
}
nsresult
nsCSSCounterStyleRule::SetDescriptor(nsCSSCounterDesc aDescID,
const nsAString& aValue)
{
nsCSSParser parser;
nsCSSValue value;
CSSStyleSheet* sheet = GetStyleSheet();
nsIURI* baseURL = nullptr;
nsIPrincipal* principal = nullptr;
if (sheet) {
baseURL = sheet->GetBaseURI();
principal = sheet->Principal();
}
if (parser.ParseCounterDescriptor(aDescID, aValue, nullptr,
baseURL, principal, value)) {
if (CheckDescValue(GetSystem(), aDescID, value)) {
SetDesc(aDescID, value);
}
}
return NS_OK;
}
#define CSS_COUNTER_DESC_SETTER(name_) \
NS_IMETHODIMP \
nsCSSCounterStyleRule::Set##name_(const nsAString& a##name_) \
{ \
return SetDescriptor(eCSSCounterDesc_##name_, a##name_); \
}
CSS_COUNTER_DESC_SETTER(System)
CSS_COUNTER_DESC_SETTER(Symbols)
CSS_COUNTER_DESC_SETTER(AdditiveSymbols)
CSS_COUNTER_DESC_SETTER(Negative)
CSS_COUNTER_DESC_SETTER(Prefix)
CSS_COUNTER_DESC_SETTER(Suffix)
CSS_COUNTER_DESC_SETTER(Range)
CSS_COUNTER_DESC_SETTER(Pad)
CSS_COUNTER_DESC_SETTER(Fallback)
CSS_COUNTER_DESC_SETTER(SpeakAs)
#undef CSS_COUNTER_DESC_SETTER
/* virtual */ size_t
nsCSSCounterStyleRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this);
}