Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * internal interface representing CSS style rules that contain other
9 : * rules, such as @media rules
10 : */
11 :
12 : #include "mozilla/css/GroupRule.h"
13 :
14 : #include "mozilla/dom/CSSRuleList.h"
15 :
16 : using namespace mozilla::dom;
17 :
18 : namespace mozilla {
19 : namespace css {
20 :
21 : #define CALL_INNER(inner_, call_) \
22 : ((inner_).is<GeckoGroupRuleRules>() \
23 : ? (inner_).as<GeckoGroupRuleRules>().call_ \
24 : : (inner_).as<ServoGroupRuleRules>().call_)
25 :
26 : // -------------------------------
27 : // Style Rule List for group rules
28 : //
29 :
30 : class GroupRuleRuleList final : public dom::CSSRuleList
31 : {
32 : public:
33 : explicit GroupRuleRuleList(GroupRule *aGroupRule);
34 :
35 : virtual CSSStyleSheet* GetParentObject() override;
36 :
37 : virtual Rule*
38 : IndexedGetter(uint32_t aIndex, bool& aFound) override;
39 : virtual uint32_t
40 : Length() override;
41 :
42 0 : void DropReference() { mGroupRule = nullptr; }
43 :
44 : private:
45 : ~GroupRuleRuleList();
46 :
47 : private:
48 : GroupRule* mGroupRule;
49 : };
50 :
51 0 : GroupRuleRuleList::GroupRuleRuleList(GroupRule *aGroupRule)
52 : {
53 : // Not reference counted to avoid circular references.
54 : // The rule will tell us when its going away.
55 0 : mGroupRule = aGroupRule;
56 0 : }
57 :
58 0 : GroupRuleRuleList::~GroupRuleRuleList()
59 : {
60 0 : }
61 :
62 : CSSStyleSheet*
63 0 : GroupRuleRuleList::GetParentObject()
64 : {
65 0 : if (!mGroupRule) {
66 0 : return nullptr;
67 : }
68 0 : StyleSheet* sheet = mGroupRule->GetStyleSheet();
69 0 : return sheet ? sheet->AsGecko() : nullptr;
70 : }
71 :
72 : uint32_t
73 0 : GroupRuleRuleList::Length()
74 : {
75 0 : if (!mGroupRule) {
76 0 : return 0;
77 : }
78 :
79 0 : return AssertedCast<uint32_t>(mGroupRule->StyleRuleCount());
80 : }
81 :
82 : Rule*
83 0 : GroupRuleRuleList::IndexedGetter(uint32_t aIndex, bool& aFound)
84 : {
85 0 : aFound = false;
86 :
87 0 : if (mGroupRule) {
88 0 : RefPtr<Rule> rule = mGroupRule->GetStyleRuleAt(aIndex);
89 0 : if (rule) {
90 0 : aFound = true;
91 0 : return rule;
92 : }
93 : }
94 :
95 0 : return nullptr;
96 : }
97 :
98 : // -------------------------------
99 : // GeckoGroupRuleRules
100 : //
101 :
102 81 : GeckoGroupRuleRules::GeckoGroupRuleRules()
103 : {
104 81 : }
105 :
106 81 : GeckoGroupRuleRules::GeckoGroupRuleRules(GeckoGroupRuleRules&& aOther)
107 81 : : mRules(Move(aOther.mRules))
108 81 : , mRuleCollection(Move(aOther.mRuleCollection))
109 : {
110 81 : }
111 :
112 0 : GeckoGroupRuleRules::GeckoGroupRuleRules(const GeckoGroupRuleRules& aCopy)
113 : {
114 0 : for (const Rule* rule : aCopy.mRules) {
115 0 : RefPtr<Rule> clone = rule->Clone();
116 0 : mRules.AppendObject(clone);
117 : }
118 0 : }
119 :
120 162 : GeckoGroupRuleRules::~GeckoGroupRuleRules()
121 : {
122 81 : for (Rule* rule : mRules) {
123 0 : rule->SetParentRule(nullptr);
124 : }
125 81 : if (mRuleCollection) {
126 0 : mRuleCollection->DropReference();
127 : }
128 81 : }
129 :
130 : void
131 0 : GeckoGroupRuleRules::Clear()
132 : {
133 0 : mRules.Clear();
134 0 : if (mRuleCollection) {
135 0 : mRuleCollection->DropReference();
136 0 : mRuleCollection = nullptr;
137 : }
138 0 : }
139 :
140 : void
141 0 : GeckoGroupRuleRules::Traverse(nsCycleCollectionTraversalCallback& cb)
142 : {
143 0 : IncrementalClearCOMRuleArray& rules = mRules;
144 0 : for (int32_t i = 0, count = rules.Count(); i < count; ++i) {
145 0 : if (!rules[i]->IsCCLeaf()) {
146 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRules[i]");
147 0 : cb.NoteXPCOMChild(rules[i]);
148 : }
149 : }
150 0 : ImplCycleCollectionTraverse(cb, mRuleCollection, "mRuleCollection");
151 0 : }
152 :
153 : #ifdef DEBUG
154 : void
155 0 : GeckoGroupRuleRules::List(FILE* out, int32_t aIndent) const
156 : {
157 0 : for (const Rule* rule : mRules) {
158 0 : rule->List(out, aIndent + 1);
159 : }
160 0 : }
161 : #endif
162 :
163 : nsresult
164 0 : GeckoGroupRuleRules::DeleteStyleRuleAt(uint32_t aIndex)
165 : {
166 0 : Rule* rule = mRules.SafeObjectAt(aIndex);
167 0 : if (rule) {
168 0 : rule->SetStyleSheet(nullptr);
169 0 : rule->SetParentRule(nullptr);
170 : }
171 0 : return mRules.RemoveObjectAt(aIndex) ? NS_OK : NS_ERROR_ILLEGAL_VALUE;
172 : }
173 :
174 : CSSRuleList*
175 0 : GeckoGroupRuleRules::CssRules(GroupRule* aParentRule)
176 : {
177 0 : if (!mRuleCollection) {
178 0 : mRuleCollection = new GroupRuleRuleList(aParentRule);
179 : }
180 0 : return mRuleCollection;
181 : }
182 :
183 : size_t
184 0 : GeckoGroupRuleRules::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
185 : {
186 0 : size_t n = mRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
187 0 : for (const Rule* rule : mRules) {
188 0 : n += rule->SizeOfIncludingThis(aMallocSizeOf);
189 : }
190 :
191 : // Measurement of the following members may be added later if DMD finds it is
192 : // worthwhile:
193 : // - mRuleCollection
194 0 : return n;
195 : }
196 :
197 : // -------------------------------
198 : // ServoGroupRuleRules
199 : //
200 :
201 : #ifdef DEBUG
202 : void
203 0 : ServoGroupRuleRules::List(FILE* out, int32_t aIndent) const
204 : {
205 : // TODO list something reasonable?
206 0 : }
207 : #endif
208 :
209 : size_t
210 0 : ServoGroupRuleRules::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
211 : {
212 : // TODO how to implement?
213 0 : return 0;
214 : }
215 :
216 : // -------------------------------
217 : // GroupRule
218 : //
219 :
220 81 : GroupRule::GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber)
221 : : Rule(aLineNumber, aColumnNumber)
222 81 : , mInner(GeckoGroupRuleRules())
223 : {
224 81 : }
225 :
226 0 : GroupRule::GroupRule(already_AddRefed<ServoCssRules> aRules)
227 : : Rule(0, 0) // TODO
228 0 : , mInner(ServoGroupRuleRules(Move(aRules)))
229 : {
230 0 : mInner.as<ServoGroupRuleRules>().SetParentRule(this);
231 0 : }
232 :
233 0 : GroupRule::GroupRule(const GroupRule& aCopy)
234 : : Rule(aCopy)
235 0 : , mInner(aCopy.mInner)
236 : {
237 0 : CALL_INNER(mInner, SetParentRule(this));
238 0 : }
239 :
240 0 : GroupRule::~GroupRule()
241 : {
242 0 : MOZ_ASSERT(!mSheet, "SetStyleSheet should have been called");
243 0 : }
244 :
245 212 : NS_IMPL_ADDREF_INHERITED(GroupRule, Rule)
246 131 : NS_IMPL_RELEASE_INHERITED(GroupRule, Rule)
247 :
248 33 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GroupRule)
249 0 : NS_INTERFACE_MAP_END_INHERITING(Rule)
250 :
251 : bool
252 0 : GroupRule::IsCCLeaf() const
253 : {
254 : // Let's not worry for now about sorting out whether we're a leaf or not.
255 0 : return false;
256 : }
257 :
258 : NS_IMPL_CYCLE_COLLECTION_CLASS(GroupRule)
259 :
260 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(GroupRule, Rule)
261 0 : CALL_INNER(tmp->mInner, SetParentRule(nullptr));
262 : // If tmp does not have a stylesheet, neither do its descendants. In that
263 : // case, don't try to null out their stylesheet, to avoid O(N^2) behavior in
264 : // depth of group rule nesting. But if tmp _does_ have a stylesheet (which
265 : // can happen if it gets unlinked earlier than its owning stylesheet), then we
266 : // need to null out the stylesheet pointer on descendants now, before we clear
267 : // tmp->mRules.
268 0 : if (tmp->GetStyleSheet()) {
269 0 : CALL_INNER(tmp->mInner, SetStyleSheet(nullptr));
270 : }
271 0 : CALL_INNER(tmp->mInner, Clear());
272 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
273 :
274 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(GroupRule, Rule)
275 0 : CALL_INNER(tmp->mInner, Traverse(cb));
276 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
277 :
278 : /* virtual */ void
279 81 : GroupRule::SetStyleSheet(StyleSheet* aSheet)
280 : {
281 : // Don't set the sheet on the kids if it's already the same as the sheet we
282 : // already have. This is needed to avoid O(N^2) behavior in group nesting
283 : // depth when seting the sheet to null during unlink, if we happen to unlin in
284 : // order from most nested rule up to least nested rule.
285 81 : if (aSheet != GetStyleSheet()) {
286 81 : CALL_INNER(mInner, SetStyleSheet(aSheet));
287 81 : Rule::SetStyleSheet(aSheet);
288 : }
289 81 : }
290 :
291 : void
292 207 : GroupRule::AppendStyleRule(Rule* aRule)
293 : {
294 207 : GeckoRules().AppendObject(aRule);
295 207 : StyleSheet* sheet = GetStyleSheet();
296 207 : aRule->SetStyleSheet(sheet);
297 207 : aRule->SetParentRule(this);
298 207 : if (sheet) {
299 0 : sheet->AsGecko()->SetModifiedByChildRule();
300 : }
301 207 : }
302 :
303 : bool
304 140 : GroupRule::EnumerateRulesForwards(RuleEnumFunc aFunc, void * aData) const
305 : {
306 498 : for (const Rule* rule : GeckoRules()) {
307 358 : if (!aFunc(const_cast<Rule*>(rule), aData)) {
308 0 : return false;
309 : }
310 : }
311 140 : return true;
312 : }
313 :
314 : nsresult
315 0 : GroupRule::InsertStyleRuleAt(uint32_t aIndex, Rule* aRule)
316 : {
317 0 : aRule->SetStyleSheet(GetStyleSheet());
318 0 : aRule->SetParentRule(this);
319 0 : if (!GeckoRules().InsertObjectAt(aRule, aIndex)) {
320 0 : return NS_ERROR_FAILURE;
321 : }
322 0 : return NS_OK;
323 : }
324 :
325 : void
326 0 : GroupRule::AppendRulesToCssText(nsAString& aCssText) const
327 : {
328 0 : aCssText.AppendLiteral(" {\n");
329 0 : for (const Rule* rule : GeckoRules()) {
330 0 : nsAutoString cssText;
331 0 : rule->GetCssText(cssText);
332 0 : aCssText.AppendLiteral(" ");
333 0 : aCssText.Append(cssText);
334 0 : aCssText.Append('\n');
335 : }
336 0 : aCssText.Append('}');
337 0 : }
338 :
339 : // nsIDOMCSSMediaRule or nsIDOMCSSMozDocumentRule methods
340 : nsresult
341 0 : GroupRule::GetCssRules(nsIDOMCSSRuleList* *aRuleList)
342 : {
343 0 : NS_ADDREF(*aRuleList = CssRules());
344 0 : return NS_OK;
345 : }
346 :
347 : CSSRuleList*
348 0 : GroupRule::CssRules()
349 : {
350 0 : return CALL_INNER(mInner, CssRules(this));
351 : }
352 :
353 : nsresult
354 0 : GroupRule::InsertRule(const nsAString & aRule, uint32_t aIndex, uint32_t* _retval)
355 : {
356 0 : ErrorResult rv;
357 0 : *_retval = InsertRule(aRule, aIndex, rv);
358 0 : return rv.StealNSResult();
359 : }
360 :
361 : uint32_t
362 0 : GroupRule::InsertRule(const nsAString& aRule, uint32_t aIndex, ErrorResult& aRv)
363 : {
364 0 : StyleSheet* sheet = GetStyleSheet();
365 0 : if (NS_WARN_IF(!sheet)) {
366 0 : aRv.Throw(NS_ERROR_FAILURE);
367 0 : return 0;
368 : }
369 :
370 0 : uint32_t count = StyleRuleCount();
371 0 : if (aIndex > count) {
372 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
373 0 : return 0;
374 : }
375 :
376 0 : NS_ASSERTION(count <= INT32_MAX, "Too many style rules!");
377 :
378 0 : nsresult rv = sheet->InsertRuleIntoGroup(aRule, this, aIndex);
379 0 : if (NS_FAILED(rv)) {
380 0 : aRv.Throw(rv);
381 0 : return 0;
382 : }
383 0 : return aIndex;
384 : }
385 :
386 : nsresult
387 0 : GroupRule::DeleteRule(uint32_t aIndex)
388 : {
389 0 : ErrorResult rv;
390 0 : DeleteRule(aIndex, rv);
391 0 : return rv.StealNSResult();
392 : }
393 :
394 : void
395 0 : GroupRule::DeleteRule(uint32_t aIndex, ErrorResult& aRv)
396 : {
397 0 : StyleSheet* sheet = GetStyleSheet();
398 0 : if (NS_WARN_IF(!sheet)) {
399 0 : aRv.Throw(NS_ERROR_FAILURE);
400 0 : return;
401 : }
402 :
403 0 : uint32_t count = StyleRuleCount();
404 0 : if (aIndex >= count) {
405 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
406 0 : return;
407 : }
408 :
409 0 : NS_ASSERTION(count <= INT32_MAX, "Too many style rules!");
410 :
411 0 : nsresult rv = sheet->DeleteRuleFromGroup(this, aIndex);
412 0 : if (NS_FAILED(rv)) {
413 0 : aRv.Throw(rv);
414 : }
415 : }
416 :
417 : #undef CALL_INNER
418 :
419 : } // namespace css
420 : } // namespace mozilla
|