gecko-dev/layout/style/nsCSSCounterStyleRule.cpp

463 строки
13 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* a Gecko @counter-style rule */
#include "nsCSSCounterStyleRule.h"
#include "mozAutoDocUpdate.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/dom/CSSCounterStyleRuleBinding.h"
#include "nsCSSParser.h"
#include "nsStyleUtil.h"
using namespace mozilla;
using namespace mozilla::dom;
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
{
RefPtr<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_INHERITED(nsCSSCounterStyleRule, mozilla::css::Rule)
NS_IMPL_RELEASE_INHERITED(nsCSSCounterStyleRule, mozilla::css::Rule)
// QueryInterface implementation for nsCSSCounterStyleRule
// If this ever gets its own cycle-collection bits, reevaluate our IsCCLeaf
// implementation.
NS_INTERFACE_MAP_BEGIN(nsCSSCounterStyleRule)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSCounterStyleRule)
NS_INTERFACE_MAP_END_INHERITING(mozilla::css::Rule)
bool
nsCSSCounterStyleRule::IsCCLeaf() const
{
return Rule::IsCCLeaf();
}
#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(" ");
nsDependentAtomString name(mName);
fprintf_stderr(out, "%s@counter-style %s (rev.%u) {\n",
baseInd.get(), NS_ConvertUTF16toUTF8(name).get(),
mGeneration);
// TODO
fprintf_stderr(out, "%s}\n", baseInd.get());
}
#endif
/* virtual */ int32_t
nsCSSCounterStyleRule::GetType() const
{
return Rule::COUNTER_STYLE_RULE;
}
uint16_t
nsCSSCounterStyleRule::Type() const
{
return nsIDOMCSSRule::COUNTER_STYLE_RULE;
}
void
nsCSSCounterStyleRule::GetCssTextImpl(nsAString& aCssText) const
{
aCssText.AssignLiteral(u"@counter-style ");
nsDependentAtomString name(mName);
nsStyleUtil::AppendEscapedCSSIdent(name, aCssText);
aCssText.AppendLiteral(u" {\n");
for (nsCSSCounterDesc id = nsCSSCounterDesc(0);
id < eCSSCounterDesc_COUNT;
id = nsCSSCounterDesc(id + 1)) {
if (mValues[id].GetUnit() != eCSSUnit_Null) {
nsAutoString tmp;
// This is annoying. We want to be a const method, but kGetters stores
// XPCOM method pointers, which aren't const methods. The thing is,
// none of those mutate "this". So it's OK to cast away const here.
(const_cast<nsCSSCounterStyleRule*>(this)->*kGetters[id])(tmp);
aCssText.AppendLiteral(u" ");
AppendASCIItoUTF16(nsCSSProps::GetStringValue(id), aCssText);
aCssText.AppendLiteral(u": ");
aCssText.Append(tmp);
aCssText.AppendLiteral(u";\n");
}
}
aCssText.AppendLiteral(u"}");
}
// nsIDOMCSSCounterStyleRule methods
NS_IMETHODIMP
nsCSSCounterStyleRule::GetName(nsAString& aName)
{
aName.Truncate();
nsDependentAtomString name(mName);
nsStyleUtil::AppendEscapedCSSIdent(name, aName);
return NS_OK;
}
NS_IMETHODIMP
nsCSSCounterStyleRule::SetName(const nsAString& aName)
{
nsCSSParser parser;
if (RefPtr<nsAtom> name = parser.ParseCounterStyleName(aName, nullptr)) {
nsIDocument* doc = GetDocument();
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
mName = name;
if (StyleSheet* sheet = GetStyleSheet()) {
if (sheet->IsGecko()) {
sheet->AsGecko()->SetModifiedByChildRule();
}
if (doc) {
doc->StyleRuleChanged(sheet, 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);
MOZ_ASSERT(system.GetUnit() == eCSSUnit_Pair,
"Invalid system value");
return system.GetPairValue().mYValue;
}
void
nsCSSCounterStyleRule::SetDesc(nsCSSCounterDesc aDescID, const nsCSSValue& aValue)
{
MOZ_ASSERT(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++;
if (StyleSheet* sheet = GetStyleSheet()) {
if (sheet->IsGecko()) {
sheet->AsGecko()->SetModifiedByChildRule();
}
if (doc) {
doc->StyleRuleChanged(sheet, 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);
}
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);
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);
aSymbols.Append(' ');
item->mYValue.AppendToString(eCSSProperty_UNKNOWN, aSymbols);
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(u"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(u"bullets");
break;
case NS_STYLE_COUNTER_SPEAKAS_NUMBERS:
aSpeakAs.AssignLiteral(u"numbers");
break;
case NS_STYLE_COUNTER_SPEAKAS_WORDS:
aSpeakAs.AssignLiteral(u"words");
break;
case NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT:
aSpeakAs.AssignLiteral(u"spell-out");
break;
default:
NS_NOTREACHED("Unknown speech synthesis");
}
break;
case eCSSUnit_Auto:
case eCSSUnit_AtomIdent:
aSpeakAs.Truncate();
value.AppendToString(eCSSProperty_UNKNOWN, aSpeakAs);
break;
case eCSSUnit_Null:
aSpeakAs.Truncate();
break;
default:
NS_NOTREACHED("Unknown speech synthesis");
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);
}
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;
nsIURI* baseURL = nullptr;
nsIPrincipal* principal = nullptr;
if (StyleSheet* sheet = GetStyleSheet()) {
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);
}
/* virtual */ JSObject*
nsCSSCounterStyleRule::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return CSSCounterStyleRuleBinding::Wrap(aCx, this, aGivenProto);
}