зеркало из https://github.com/mozilla/gecko-dev.git
422 строки
9.5 KiB
C++
422 строки
9.5 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/. */
|
|
|
|
/*
|
|
* internal interface representing CSS style rules that contain other
|
|
* rules, such as @media rules
|
|
*/
|
|
|
|
#include "mozilla/css/GroupRule.h"
|
|
|
|
#include "mozilla/dom/CSSRuleList.h"
|
|
|
|
using namespace mozilla::dom;
|
|
|
|
namespace mozilla {
|
|
namespace css {
|
|
|
|
#define CALL_INNER(inner_, call_) \
|
|
((inner_).is<GeckoGroupRuleRules>() \
|
|
? (inner_).as<GeckoGroupRuleRules>().call_ \
|
|
: (inner_).as<ServoGroupRuleRules>().call_)
|
|
|
|
// -------------------------------
|
|
// Style Rule List for group rules
|
|
//
|
|
|
|
class GroupRuleRuleList final : public dom::CSSRuleList
|
|
{
|
|
public:
|
|
explicit GroupRuleRuleList(GroupRule *aGroupRule);
|
|
|
|
virtual CSSStyleSheet* GetParentObject() override;
|
|
|
|
virtual Rule*
|
|
IndexedGetter(uint32_t aIndex, bool& aFound) override;
|
|
virtual uint32_t
|
|
Length() 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;
|
|
}
|
|
StyleSheet* sheet = mGroupRule->GetStyleSheet();
|
|
return sheet ? sheet->AsGecko() : nullptr;
|
|
}
|
|
|
|
uint32_t
|
|
GroupRuleRuleList::Length()
|
|
{
|
|
if (!mGroupRule) {
|
|
return 0;
|
|
}
|
|
|
|
return AssertedCast<uint32_t>(mGroupRule->StyleRuleCount());
|
|
}
|
|
|
|
Rule*
|
|
GroupRuleRuleList::IndexedGetter(uint32_t aIndex, bool& aFound)
|
|
{
|
|
aFound = false;
|
|
|
|
if (mGroupRule) {
|
|
RefPtr<Rule> rule = mGroupRule->GetStyleRuleAt(aIndex);
|
|
if (rule) {
|
|
aFound = true;
|
|
return rule;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// -------------------------------
|
|
// GeckoGroupRuleRules
|
|
//
|
|
|
|
GeckoGroupRuleRules::GeckoGroupRuleRules()
|
|
{
|
|
}
|
|
|
|
GeckoGroupRuleRules::GeckoGroupRuleRules(GeckoGroupRuleRules&& aOther)
|
|
: mRules(Move(aOther.mRules))
|
|
, mRuleCollection(Move(aOther.mRuleCollection))
|
|
{
|
|
}
|
|
|
|
GeckoGroupRuleRules::GeckoGroupRuleRules(const GeckoGroupRuleRules& aCopy)
|
|
{
|
|
for (const Rule* rule : aCopy.mRules) {
|
|
RefPtr<Rule> clone = rule->Clone();
|
|
mRules.AppendObject(clone);
|
|
}
|
|
}
|
|
|
|
GeckoGroupRuleRules::~GeckoGroupRuleRules()
|
|
{
|
|
for (Rule* rule : mRules) {
|
|
rule->SetParentRule(nullptr);
|
|
}
|
|
if (mRuleCollection) {
|
|
mRuleCollection->DropReference();
|
|
}
|
|
}
|
|
|
|
void
|
|
GeckoGroupRuleRules::Clear()
|
|
{
|
|
mRules.Clear();
|
|
if (mRuleCollection) {
|
|
mRuleCollection->DropReference();
|
|
mRuleCollection = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
GeckoGroupRuleRules::Traverse(nsCycleCollectionTraversalCallback& cb)
|
|
{
|
|
IncrementalClearCOMRuleArray& rules = mRules;
|
|
for (int32_t i = 0, count = rules.Count(); i < count; ++i) {
|
|
if (!rules[i]->IsCCLeaf()) {
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
|
|
cb.NoteXPCOMChild(rules[i]);
|
|
}
|
|
}
|
|
ImplCycleCollectionTraverse(cb, mRuleCollection, "mRuleCollection");
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
GeckoGroupRuleRules::List(FILE* out, int32_t aIndent) const
|
|
{
|
|
for (const Rule* rule : mRules) {
|
|
rule->List(out, aIndent + 1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
nsresult
|
|
GeckoGroupRuleRules::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;
|
|
}
|
|
|
|
CSSRuleList*
|
|
GeckoGroupRuleRules::CssRules(GroupRule* aParentRule)
|
|
{
|
|
if (!mRuleCollection) {
|
|
mRuleCollection = new GroupRuleRuleList(aParentRule);
|
|
}
|
|
return mRuleCollection;
|
|
}
|
|
|
|
size_t
|
|
GeckoGroupRuleRules::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
size_t n = mRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
|
for (const Rule* rule : mRules) {
|
|
n += rule->SizeOfIncludingThis(aMallocSizeOf);
|
|
}
|
|
|
|
// Measurement of the following members may be added later if DMD finds it is
|
|
// worthwhile:
|
|
// - mRuleCollection
|
|
return n;
|
|
}
|
|
|
|
// -------------------------------
|
|
// ServoGroupRuleRules
|
|
//
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
ServoGroupRuleRules::List(FILE* out, int32_t aIndent) const
|
|
{
|
|
// TODO list something reasonable?
|
|
}
|
|
#endif
|
|
|
|
size_t
|
|
ServoGroupRuleRules::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
// TODO how to implement?
|
|
return 0;
|
|
}
|
|
|
|
// -------------------------------
|
|
// GroupRule
|
|
//
|
|
|
|
GroupRule::GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber)
|
|
: Rule(aLineNumber, aColumnNumber)
|
|
, mInner(GeckoGroupRuleRules())
|
|
{
|
|
}
|
|
|
|
GroupRule::GroupRule(already_AddRefed<ServoCssRules> aRules,
|
|
uint32_t aLineNumber, uint32_t aColumnNumber)
|
|
: Rule(aLineNumber, aColumnNumber)
|
|
, mInner(ServoGroupRuleRules(Move(aRules)))
|
|
{
|
|
mInner.as<ServoGroupRuleRules>().SetParentRule(this);
|
|
}
|
|
|
|
GroupRule::GroupRule(const GroupRule& aCopy)
|
|
: Rule(aCopy)
|
|
, mInner(aCopy.mInner)
|
|
{
|
|
CALL_INNER(mInner, SetParentRule(this));
|
|
}
|
|
|
|
GroupRule::~GroupRule()
|
|
{
|
|
MOZ_ASSERT(!mSheet, "SetStyleSheet should have been called");
|
|
}
|
|
|
|
NS_IMPL_ADDREF_INHERITED(GroupRule, Rule)
|
|
NS_IMPL_RELEASE_INHERITED(GroupRule, Rule)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GroupRule)
|
|
NS_INTERFACE_MAP_END_INHERITING(Rule)
|
|
|
|
bool
|
|
GroupRule::IsCCLeaf() const
|
|
{
|
|
// Let's not worry for now about sorting out whether we're a leaf or not.
|
|
return false;
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(GroupRule)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(GroupRule, Rule)
|
|
CALL_INNER(tmp->mInner, SetParentRule(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()) {
|
|
CALL_INNER(tmp->mInner, SetStyleSheet(nullptr));
|
|
}
|
|
CALL_INNER(tmp->mInner, Clear());
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GroupRule, Rule)
|
|
CALL_INNER(tmp->mInner, Traverse(cb));
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
/* virtual */ void
|
|
GroupRule::SetStyleSheet(StyleSheet* 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()) {
|
|
CALL_INNER(mInner, SetStyleSheet(aSheet));
|
|
Rule::SetStyleSheet(aSheet);
|
|
}
|
|
}
|
|
|
|
void
|
|
GroupRule::AppendStyleRule(Rule* aRule)
|
|
{
|
|
GeckoRules().AppendObject(aRule);
|
|
StyleSheet* sheet = GetStyleSheet();
|
|
aRule->SetStyleSheet(sheet);
|
|
aRule->SetParentRule(this);
|
|
if (sheet) {
|
|
sheet->AsGecko()->SetModifiedByChildRule();
|
|
}
|
|
}
|
|
|
|
bool
|
|
GroupRule::EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const
|
|
{
|
|
for (const Rule* rule : GeckoRules()) {
|
|
if (!aFunc(const_cast<Rule*>(rule), aData)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
nsresult
|
|
GroupRule::InsertStyleRuleAt(uint32_t aIndex, Rule* aRule)
|
|
{
|
|
aRule->SetStyleSheet(GetStyleSheet());
|
|
aRule->SetParentRule(this);
|
|
if (!GeckoRules().InsertObjectAt(aRule, aIndex)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
GroupRule::AppendRulesToCssText(nsAString& aCssText) const
|
|
{
|
|
aCssText.AppendLiteral(" {\n");
|
|
for (const Rule* rule : GeckoRules()) {
|
|
nsAutoString cssText;
|
|
rule->GetCssText(cssText);
|
|
aCssText.AppendLiteral(" ");
|
|
aCssText.Append(cssText);
|
|
aCssText.Append('\n');
|
|
}
|
|
aCssText.Append('}');
|
|
}
|
|
|
|
// nsIDOMCSSMediaRule or nsIDOMCSSMozDocumentRule methods
|
|
nsresult
|
|
GroupRule::GetCssRules(nsIDOMCSSRuleList* *aRuleList)
|
|
{
|
|
NS_ADDREF(*aRuleList = CssRules());
|
|
return NS_OK;
|
|
}
|
|
|
|
CSSRuleList*
|
|
GroupRule::CssRules()
|
|
{
|
|
return CALL_INNER(mInner, CssRules(this));
|
|
}
|
|
|
|
nsresult
|
|
GroupRule::InsertRule(const nsAString & aRule, uint32_t aIndex, uint32_t* _retval)
|
|
{
|
|
ErrorResult rv;
|
|
*_retval = InsertRule(aRule, aIndex, rv);
|
|
return rv.StealNSResult();
|
|
}
|
|
|
|
uint32_t
|
|
GroupRule::InsertRule(const nsAString& aRule, uint32_t aIndex, ErrorResult& aRv)
|
|
{
|
|
StyleSheet* sheet = GetStyleSheet();
|
|
if (NS_WARN_IF(!sheet)) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return 0;
|
|
}
|
|
|
|
uint32_t count = StyleRuleCount();
|
|
if (aIndex > count) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
|
return 0;
|
|
}
|
|
|
|
NS_ASSERTION(count <= INT32_MAX, "Too many style rules!");
|
|
|
|
nsresult rv = sheet->InsertRuleIntoGroup(aRule, this, aIndex);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
return 0;
|
|
}
|
|
return aIndex;
|
|
}
|
|
|
|
nsresult
|
|
GroupRule::DeleteRule(uint32_t aIndex)
|
|
{
|
|
ErrorResult rv;
|
|
DeleteRule(aIndex, rv);
|
|
return rv.StealNSResult();
|
|
}
|
|
|
|
void
|
|
GroupRule::DeleteRule(uint32_t aIndex, ErrorResult& aRv)
|
|
{
|
|
StyleSheet* sheet = GetStyleSheet();
|
|
if (NS_WARN_IF(!sheet)) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
uint32_t count = StyleRuleCount();
|
|
if (aIndex >= count) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
|
return;
|
|
}
|
|
|
|
NS_ASSERTION(count <= INT32_MAX, "Too many style rules!");
|
|
|
|
nsresult rv = sheet->DeleteRuleFromGroup(this, aIndex);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
}
|
|
}
|
|
|
|
#undef CALL_INNER
|
|
|
|
} // namespace css
|
|
} // namespace mozilla
|