gecko-dev/layout/style/GroupRule.cpp

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