/* -*- 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() \ ? (inner_).as().call_ \ : (inner_).as().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(mGroupRule->StyleRuleCount()); } Rule* GroupRuleRuleList::IndexedGetter(uint32_t aIndex, bool& aFound) { aFound = false; if (mGroupRule) { RefPtr 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 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 // ServoGroupRuleRules::~ServoGroupRuleRules() { if (mRuleList) { mRuleList->DropReference(); } } #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 aRules, uint32_t aLineNumber, uint32_t aColumnNumber) : Rule(aLineNumber, aColumnNumber) , mInner(ServoGroupRuleRules(Move(aRules))) { mInner.as().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(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->RuleChanged(this); } } bool GroupRule::EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const { for (const Rule* rule : GeckoRules()) { if (!aFunc(const_cast(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('}'); } CSSRuleList* GroupRule::CssRules() { return CALL_INNER(mInner, CssRules(this)); } 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; } 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