Bug 1315601 part 9 - Split Gecko-specific GroupRule logic into a separate struct. r=heycam
MozReview-Commit-ID: 7CkGO2KgJN3
--- a/layout/style/GroupRule.cpp
+++ b/layout/style/GroupRule.cpp
@@ -13,16 +13,22 @@
#include "mozilla/dom/CSSRuleList.h"
using namespace mozilla::dom;
namespace mozilla {
namespace css {
+// TODO The other branch should be changed to Servo rule.
+#define CALL_INNER(inner_, call_) \
+ ((inner_).is<GeckoGroupRuleRules>() \
+ ? (inner_).as<GeckoGroupRuleRules>().call_ \
+ : (inner_).as<GeckoGroupRuleRules>().call_)
+
// -------------------------------
// Style Rule List for group rules
//
class GroupRuleRuleList final : public dom::CSSRuleList
{
public:
explicit GroupRuleRuleList(GroupRule *aGroupRule);
@@ -86,209 +92,242 @@ GroupRuleRuleList::IndexedGetter(uint32_
return rule;
}
}
return nullptr;
}
// -------------------------------
-// GroupRule
+// GeckoGroupRuleRules
//
-GroupRule::GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber)
- : Rule(aLineNumber, aColumnNumber)
+GeckoGroupRuleRules::GeckoGroupRuleRules()
{
}
-GroupRule::GroupRule(const GroupRule& aCopy)
- : Rule(aCopy)
+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);
- clone->SetParentRule(this);
}
}
-GroupRule::~GroupRule()
+GeckoGroupRuleRules::~GeckoGroupRuleRules()
{
- MOZ_ASSERT(!mSheet, "SetStyleSheet should have been called");
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;
+}
+
+// -------------------------------
+// GroupRule
+//
+
+GroupRule::GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber)
+ : Rule(aLineNumber, aColumnNumber)
+ , mInner(GeckoGroupRuleRules())
+{
+}
+
+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)
- for (Rule* rule : tmp->mRules) {
- rule->SetParentRule(nullptr);
- }
+ 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()) {
- for (Rule* rule : tmp->mRules) {
- rule->SetStyleSheet(nullptr);
- }
+ CALL_INNER(tmp->mInner, SetStyleSheet(nullptr));
}
- tmp->mRules.Clear();
- if (tmp->mRuleCollection) {
- tmp->mRuleCollection->DropReference();
- tmp->mRuleCollection = nullptr;
- }
+ CALL_INNER(tmp->mInner, Clear());
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GroupRule, Rule)
- const nsCOMArray<Rule>& rules = tmp->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]);
- }
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleCollection)
+ 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()) {
- for (Rule* rule : mRules) {
- rule->SetStyleSheet(aSheet);
- }
+ CALL_INNER(mInner, SetStyleSheet(aSheet));
Rule::SetStyleSheet(aSheet);
}
}
-#ifdef DEBUG
-/* virtual */ void
-GroupRule::List(FILE* out, int32_t aIndent) const
-{
- for (int32_t index = 0, count = mRules.Count(); index < count; ++index) {
- mRules.ObjectAt(index)->List(out, aIndent + 1);
- }
-}
-#endif
-
void
GroupRule::AppendStyleRule(Rule* aRule)
{
- mRules.AppendObject(aRule);
+ GeckoRules().AppendObject(aRule);
StyleSheet* sheet = GetStyleSheet();
aRule->SetStyleSheet(sheet);
aRule->SetParentRule(this);
if (sheet) {
sheet->AsGecko()->SetModifiedByChildRule();
}
}
-Rule*
-GroupRule::GetStyleRuleAt(int32_t aIndex) const
-{
- return mRules.SafeObjectAt(aIndex);
-}
-
bool
GroupRule::EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const
{
- for (const Rule* rule : mRules) {
+ for (const Rule* rule : GeckoRules()) {
if (!aFunc(const_cast<Rule*>(rule), aData)) {
return false;
}
}
return true;
}
-/*
- * 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)) {
+ if (!GeckoRules().InsertObjectAt(aRule, aIndex)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void
GroupRule::AppendRulesToCssText(nsAString& aCssText) const
{
aCssText.AppendLiteral(" {\n");
-
- // get all the rules
- for (int32_t index = 0, count = mRules.Count(); index < count; ++index) {
- Rule* rule = mRules.ObjectAt(index);
+ 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()
{
- if (!mRuleCollection) {
- mRuleCollection = new css::GroupRuleRuleList(this);
- }
-
- return mRuleCollection;
+ 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();
@@ -298,23 +337,23 @@ 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;
}
- if (aIndex > uint32_t(mRules.Count())) {
+ uint32_t count = StyleRuleCount();
+ if (aIndex > count) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return 0;
}
- NS_ASSERTION(uint32_t(mRules.Count()) <= INT32_MAX,
- "Too many style rules!");
+ NS_ASSERTION(count <= INT32_MAX, "Too many style rules!");
uint32_t retval;
nsresult rv =
sheet->AsGecko()->InsertRuleIntoGroup(aRule, this, aIndex, &retval);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return 0;
}
@@ -333,39 +372,26 @@ void
GroupRule::DeleteRule(uint32_t aIndex, ErrorResult& aRv)
{
StyleSheet* sheet = GetStyleSheet();
if (NS_WARN_IF(!sheet)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
- if (aIndex >= uint32_t(mRules.Count())) {
+ uint32_t count = StyleRuleCount();
+ if (aIndex >= count) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
- NS_ASSERTION(uint32_t(mRules.Count()) <= INT32_MAX,
- "Too many style rules!");
+ NS_ASSERTION(count <= INT32_MAX, "Too many style rules!");
nsresult rv = sheet->AsGecko()->DeleteRuleFromGroup(this, aIndex);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
-/* virtual */ size_t
-GroupRule::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
-{
- size_t n = mRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
- for (size_t i = 0; i < mRules.Length(); i++) {
- n += mRules[i]->SizeOfIncludingThis(aMallocSizeOf);
- }
-
- // Measurement of the following members may be added later if DMD finds it is
- // worthwhile:
- // - mRuleCollection
- return n;
-}
-
+#undef CALL_INNER
} // namespace css
-} // namespace mozill
+} // namespace mozilla
--- a/layout/style/GroupRule.h
+++ b/layout/style/GroupRule.h
@@ -10,75 +10,130 @@
#ifndef mozilla_css_GroupRule_h__
#define mozilla_css_GroupRule_h__
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/IncrementalClearCOMRuleArray.h"
#include "mozilla/MemoryReporting.h"
+#include "mozilla/Variant.h"
#include "mozilla/css/Rule.h"
#include "nsCycleCollectionParticipant.h"
class nsPresContext;
class nsMediaQueryResultCacheKey;
namespace mozilla {
class StyleSheet;
namespace dom {
class CSSRuleList;
} // namespace dom
namespace css {
+class GroupRule;
class GroupRuleRuleList;
+struct GeckoGroupRuleRules
+{
+ GeckoGroupRuleRules();
+ GeckoGroupRuleRules(GeckoGroupRuleRules&& aOther);
+ GeckoGroupRuleRules(const GeckoGroupRuleRules& aCopy);
+ ~GeckoGroupRuleRules();
+
+ void SetParentRule(GroupRule* aParentRule) {
+ for (Rule* rule : mRules) {
+ rule->SetParentRule(aParentRule);
+ }
+ }
+ void SetStyleSheet(StyleSheet* aSheet) {
+ for (Rule* rule : mRules) {
+ rule->SetStyleSheet(aSheet);
+ }
+ }
+
+ void Clear();
+ void Traverse(nsCycleCollectionTraversalCallback& cb);
+
+#ifdef DEBUG
+ void List(FILE* out, int32_t aIndent) const;
+#endif
+
+ int32_t StyleRuleCount() const { return mRules.Count(); }
+ Rule* GetStyleRuleAt(int32_t aIndex) const {
+ return mRules.SafeObjectAt(aIndex);
+ }
+
+ nsresult DeleteStyleRuleAt(uint32_t aIndex);
+
+ dom::CSSRuleList* CssRules(GroupRule* aParentRule);
+
+ size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+
+ IncrementalClearCOMRuleArray mRules;
+ RefPtr<GroupRuleRuleList> mRuleCollection; // lazily constructed
+};
+
+#define REDIRECT_TO_INNER(call_) \
+ return mInner.as<GeckoGroupRuleRules>().call_;
+
// inherits from Rule so it can be shared between
// MediaRule and DocumentRule
class GroupRule : public Rule
{
protected:
GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber);
GroupRule(const GroupRule& aCopy);
virtual ~GroupRule();
public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GroupRule, Rule)
NS_DECL_ISUPPORTS_INHERITED
virtual bool IsCCLeaf() const override;
#ifdef DEBUG
- virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
+ void List(FILE* out = stdout, int32_t aIndent = 0) const override {
+ REDIRECT_TO_INNER(List(out, aIndent))
+ }
#endif
virtual void SetStyleSheet(StyleSheet* aSheet) override;
public:
void AppendStyleRule(Rule* aRule);
- int32_t StyleRuleCount() const { return mRules.Count(); }
- Rule* GetStyleRuleAt(int32_t aIndex) const;
+ int32_t StyleRuleCount() const {
+ REDIRECT_TO_INNER(StyleRuleCount())
+ }
+ Rule* GetStyleRuleAt(uint32_t aIndex) const {
+ REDIRECT_TO_INNER(GetStyleRuleAt(aIndex))
+ }
typedef bool (*RuleEnumFunc)(Rule* aElement, void* aData);
bool EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const;
/*
- * The next three methods should never be called unless you have first
+ * The next two methods should never be called unless you have first
* called WillDirty() on the parent stylesheet. After they are
* called, DidDirty() needs to be called on the sheet.
*/
- nsresult DeleteStyleRuleAt(uint32_t aIndex);
+ nsresult DeleteStyleRuleAt(uint32_t aIndex) {
+ REDIRECT_TO_INNER(DeleteStyleRuleAt(aIndex));
+ }
nsresult InsertStyleRuleAt(uint32_t aIndex, Rule* aRule);
virtual bool UseForPresentation(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey& aKey) = 0;
// non-virtual -- it is only called by subclasses
- size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
+ REDIRECT_TO_INNER(SizeOfExcludingThis(aMallocSizeOf))
+ }
virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override = 0;
// WebIDL API
dom::CSSRuleList* CssRules();
uint32_t InsertRule(const nsAString& aRule, uint32_t aIndex,
ErrorResult& aRv);
void DeleteRule(uint32_t aIndex, ErrorResult& aRv);
@@ -88,20 +143,30 @@ protected:
// to implement common methods on nsIDOMCSSMediaRule and
// nsIDOMCSSMozDocumentRule
nsresult GetCssRules(nsIDOMCSSRuleList* *aRuleList);
nsresult InsertRule(const nsAString & aRule, uint32_t aIndex,
uint32_t* _retval);
nsresult DeleteRule(uint32_t aIndex);
- IncrementalClearCOMRuleArray mRules;
- RefPtr<GroupRuleRuleList> mRuleCollection; // lazily constructed
+ // Must only be called if this is a Gecko GroupRule.
+ IncrementalClearCOMRuleArray& GeckoRules() {
+ return mInner.as<GeckoGroupRuleRules>().mRules;
+ }
+ const IncrementalClearCOMRuleArray& GeckoRules() const {
+ return mInner.as<GeckoGroupRuleRules>().mRules;
+ }
+
+private:
+ Variant<GeckoGroupRuleRules> mInner;
};
+#undef REDIRECT_TO_INNER
+
// Implementation of WebIDL CSSConditionRule.
class ConditionRule : public GroupRule
{
protected:
using GroupRule::GroupRule;
public:
// GetConditionText signature matches nsIDOMCSSConditionRule, so subclasses
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -1993,18 +1993,18 @@ nsCSSKeyframesRule::Type() const
void
nsCSSKeyframesRule::GetCssTextImpl(nsAString& aCssText) const
{
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);
+ for (const Rule* rule : GeckoRules()) {
+ static_cast<const nsCSSKeyframeRule*>(rule)->GetCssText(tmp);
aCssText.Append(tmp);
aCssText.Append('\n');
}
aCssText.Append('}');
}
NS_IMETHODIMP
nsCSSKeyframesRule::GetName(nsAString& aName)
@@ -2074,23 +2074,24 @@ static const uint32_t RULE_NOT_FOUND = u
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)) {
+ IncrementalClearCOMRuleArray& rules = GeckoRules();
// 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) {
+ for (uint32_t i = rules.Count(); i-- != 0; ) {
+ if (static_cast<nsCSSKeyframeRule*>(rules[i])->GetKeys() == keys) {
return i;
}
}
}
return RULE_NOT_FOUND;
}
@@ -2125,17 +2126,17 @@ nsCSSKeyframesRule::FindRule(const nsASt
nsCSSKeyframeRule*
nsCSSKeyframesRule::FindRule(const nsAString& aKey)
{
uint32_t index = FindRuleIndexForKey(aKey);
if (index == RULE_NOT_FOUND) {
return nullptr;
}
- return static_cast<nsCSSKeyframeRule*>(mRules[index]);
+ return static_cast<nsCSSKeyframeRule*>(GeckoRules()[index]);
}
// GroupRule interface
/* virtual */ bool
nsCSSKeyframesRule::UseForPresentation(nsPresContext* aPresContext,
nsMediaQueryResultCacheKey& aKey)
{
MOZ_ASSERT(false, "should not be called");