|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* |
|
7 * representation of CSS style rules (selectors+declaration), CSS |
|
8 * selectors, and DOM objects for style rules, selectors, and |
|
9 * declarations |
|
10 */ |
|
11 |
|
12 #include "mozilla/css/StyleRule.h" |
|
13 |
|
14 #include "mozilla/MemoryReporting.h" |
|
15 #include "mozilla/css/GroupRule.h" |
|
16 #include "mozilla/css/Declaration.h" |
|
17 #include "nsCSSStyleSheet.h" |
|
18 #include "nsIDocument.h" |
|
19 #include "nsIAtom.h" |
|
20 #include "nsString.h" |
|
21 #include "nsStyleUtil.h" |
|
22 #include "nsICSSStyleRuleDOMWrapper.h" |
|
23 #include "nsDOMCSSDeclaration.h" |
|
24 #include "nsNameSpaceManager.h" |
|
25 #include "nsXMLNameSpaceMap.h" |
|
26 #include "nsCSSPseudoElements.h" |
|
27 #include "nsCSSPseudoClasses.h" |
|
28 #include "nsCSSAnonBoxes.h" |
|
29 #include "nsTArray.h" |
|
30 #include "nsDOMClassInfoID.h" |
|
31 #include "nsContentUtils.h" |
|
32 #include "nsError.h" |
|
33 #include "mozAutoDocUpdate.h" |
|
34 |
|
35 class nsIDOMCSSStyleDeclaration; |
|
36 class nsIDOMCSSStyleSheet; |
|
37 |
|
38 using namespace mozilla; |
|
39 |
|
40 #define NS_IF_CLONE(member_) \ |
|
41 PR_BEGIN_MACRO \ |
|
42 if (member_) { \ |
|
43 result->member_ = member_->Clone(); \ |
|
44 if (!result->member_) { \ |
|
45 delete result; \ |
|
46 return nullptr; \ |
|
47 } \ |
|
48 } \ |
|
49 PR_END_MACRO |
|
50 |
|
51 #define NS_IF_DELETE(ptr) \ |
|
52 PR_BEGIN_MACRO \ |
|
53 delete ptr; \ |
|
54 ptr = nullptr; \ |
|
55 PR_END_MACRO |
|
56 |
|
57 /* ************************************************************************** */ |
|
58 |
|
59 nsAtomList::nsAtomList(nsIAtom* aAtom) |
|
60 : mAtom(aAtom), |
|
61 mNext(nullptr) |
|
62 { |
|
63 MOZ_COUNT_CTOR(nsAtomList); |
|
64 } |
|
65 |
|
66 nsAtomList::nsAtomList(const nsString& aAtomValue) |
|
67 : mAtom(nullptr), |
|
68 mNext(nullptr) |
|
69 { |
|
70 MOZ_COUNT_CTOR(nsAtomList); |
|
71 mAtom = do_GetAtom(aAtomValue); |
|
72 } |
|
73 |
|
74 nsAtomList* |
|
75 nsAtomList::Clone(bool aDeep) const |
|
76 { |
|
77 nsAtomList *result = new nsAtomList(mAtom); |
|
78 if (!result) |
|
79 return nullptr; |
|
80 |
|
81 if (aDeep) |
|
82 NS_CSS_CLONE_LIST_MEMBER(nsAtomList, this, mNext, result, (false)); |
|
83 return result; |
|
84 } |
|
85 |
|
86 size_t |
|
87 nsAtomList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
|
88 { |
|
89 size_t n = 0; |
|
90 const nsAtomList* a = this; |
|
91 while (a) { |
|
92 n += aMallocSizeOf(a); |
|
93 |
|
94 // The following members aren't measured: |
|
95 // - a->mAtom, because it may be shared |
|
96 |
|
97 a = a->mNext; |
|
98 } |
|
99 return n; |
|
100 } |
|
101 |
|
102 nsAtomList::~nsAtomList(void) |
|
103 { |
|
104 MOZ_COUNT_DTOR(nsAtomList); |
|
105 NS_CSS_DELETE_LIST_MEMBER(nsAtomList, this, mNext); |
|
106 } |
|
107 |
|
108 nsPseudoClassList::nsPseudoClassList(nsCSSPseudoClasses::Type aType) |
|
109 : mType(aType), |
|
110 mNext(nullptr) |
|
111 { |
|
112 NS_ASSERTION(!nsCSSPseudoClasses::HasStringArg(aType) && |
|
113 !nsCSSPseudoClasses::HasNthPairArg(aType), |
|
114 "unexpected pseudo-class"); |
|
115 MOZ_COUNT_CTOR(nsPseudoClassList); |
|
116 u.mMemory = nullptr; |
|
117 } |
|
118 |
|
119 nsPseudoClassList::nsPseudoClassList(nsCSSPseudoClasses::Type aType, |
|
120 const char16_t* aString) |
|
121 : mType(aType), |
|
122 mNext(nullptr) |
|
123 { |
|
124 NS_ASSERTION(nsCSSPseudoClasses::HasStringArg(aType), |
|
125 "unexpected pseudo-class"); |
|
126 NS_ASSERTION(aString, "string expected"); |
|
127 MOZ_COUNT_CTOR(nsPseudoClassList); |
|
128 u.mString = NS_strdup(aString); |
|
129 } |
|
130 |
|
131 nsPseudoClassList::nsPseudoClassList(nsCSSPseudoClasses::Type aType, |
|
132 const int32_t* aIntPair) |
|
133 : mType(aType), |
|
134 mNext(nullptr) |
|
135 { |
|
136 NS_ASSERTION(nsCSSPseudoClasses::HasNthPairArg(aType), |
|
137 "unexpected pseudo-class"); |
|
138 NS_ASSERTION(aIntPair, "integer pair expected"); |
|
139 MOZ_COUNT_CTOR(nsPseudoClassList); |
|
140 u.mNumbers = |
|
141 static_cast<int32_t*>(nsMemory::Clone(aIntPair, sizeof(int32_t) * 2)); |
|
142 } |
|
143 |
|
144 // adopts aSelectorList |
|
145 nsPseudoClassList::nsPseudoClassList(nsCSSPseudoClasses::Type aType, |
|
146 nsCSSSelectorList* aSelectorList) |
|
147 : mType(aType), |
|
148 mNext(nullptr) |
|
149 { |
|
150 NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType), |
|
151 "unexpected pseudo-class"); |
|
152 NS_ASSERTION(aSelectorList, "selector list expected"); |
|
153 MOZ_COUNT_CTOR(nsPseudoClassList); |
|
154 u.mSelectors = aSelectorList; |
|
155 } |
|
156 |
|
157 nsPseudoClassList* |
|
158 nsPseudoClassList::Clone(bool aDeep) const |
|
159 { |
|
160 nsPseudoClassList *result; |
|
161 if (!u.mMemory) { |
|
162 result = new nsPseudoClassList(mType); |
|
163 } else if (nsCSSPseudoClasses::HasStringArg(mType)) { |
|
164 result = new nsPseudoClassList(mType, u.mString); |
|
165 } else if (nsCSSPseudoClasses::HasNthPairArg(mType)) { |
|
166 result = new nsPseudoClassList(mType, u.mNumbers); |
|
167 } else { |
|
168 NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(mType), |
|
169 "unexpected pseudo-class"); |
|
170 // This constructor adopts its selector list argument. |
|
171 result = new nsPseudoClassList(mType, u.mSelectors->Clone()); |
|
172 } |
|
173 |
|
174 if (aDeep) |
|
175 NS_CSS_CLONE_LIST_MEMBER(nsPseudoClassList, this, mNext, result, |
|
176 (false)); |
|
177 |
|
178 return result; |
|
179 } |
|
180 |
|
181 size_t |
|
182 nsPseudoClassList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
|
183 { |
|
184 size_t n = 0; |
|
185 const nsPseudoClassList* p = this; |
|
186 while (p) { |
|
187 n += aMallocSizeOf(p); |
|
188 if (!p->u.mMemory) { |
|
189 // do nothing |
|
190 |
|
191 } else if (nsCSSPseudoClasses::HasStringArg(p->mType)) { |
|
192 n += aMallocSizeOf(p->u.mString); |
|
193 |
|
194 } else if (nsCSSPseudoClasses::HasNthPairArg(p->mType)) { |
|
195 n += aMallocSizeOf(p->u.mNumbers); |
|
196 |
|
197 } else { |
|
198 NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(p->mType), |
|
199 "unexpected pseudo-class"); |
|
200 n += p->u.mSelectors->SizeOfIncludingThis(aMallocSizeOf); |
|
201 } |
|
202 p = p->mNext; |
|
203 } |
|
204 return n; |
|
205 } |
|
206 |
|
207 nsPseudoClassList::~nsPseudoClassList(void) |
|
208 { |
|
209 MOZ_COUNT_DTOR(nsPseudoClassList); |
|
210 if (nsCSSPseudoClasses::HasSelectorListArg(mType)) { |
|
211 delete u.mSelectors; |
|
212 } else if (u.mMemory) { |
|
213 NS_Free(u.mMemory); |
|
214 } |
|
215 NS_CSS_DELETE_LIST_MEMBER(nsPseudoClassList, this, mNext); |
|
216 } |
|
217 |
|
218 nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr) |
|
219 : mValue(), |
|
220 mNext(nullptr), |
|
221 mLowercaseAttr(nullptr), |
|
222 mCasedAttr(nullptr), |
|
223 mNameSpace(aNameSpace), |
|
224 mFunction(NS_ATTR_FUNC_SET), |
|
225 mCaseSensitive(1) |
|
226 { |
|
227 MOZ_COUNT_CTOR(nsAttrSelector); |
|
228 |
|
229 nsAutoString lowercase; |
|
230 nsContentUtils::ASCIIToLower(aAttr, lowercase); |
|
231 |
|
232 mCasedAttr = do_GetAtom(aAttr); |
|
233 mLowercaseAttr = do_GetAtom(lowercase); |
|
234 } |
|
235 |
|
236 nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunction, |
|
237 const nsString& aValue, bool aCaseSensitive) |
|
238 : mValue(aValue), |
|
239 mNext(nullptr), |
|
240 mLowercaseAttr(nullptr), |
|
241 mCasedAttr(nullptr), |
|
242 mNameSpace(aNameSpace), |
|
243 mFunction(aFunction), |
|
244 mCaseSensitive(aCaseSensitive) |
|
245 { |
|
246 MOZ_COUNT_CTOR(nsAttrSelector); |
|
247 |
|
248 nsAutoString lowercase; |
|
249 nsContentUtils::ASCIIToLower(aAttr, lowercase); |
|
250 |
|
251 mCasedAttr = do_GetAtom(aAttr); |
|
252 mLowercaseAttr = do_GetAtom(lowercase); |
|
253 } |
|
254 |
|
255 nsAttrSelector::nsAttrSelector(int32_t aNameSpace, nsIAtom* aLowercaseAttr, |
|
256 nsIAtom* aCasedAttr, uint8_t aFunction, |
|
257 const nsString& aValue, bool aCaseSensitive) |
|
258 : mValue(aValue), |
|
259 mNext(nullptr), |
|
260 mLowercaseAttr(aLowercaseAttr), |
|
261 mCasedAttr(aCasedAttr), |
|
262 mNameSpace(aNameSpace), |
|
263 mFunction(aFunction), |
|
264 mCaseSensitive(aCaseSensitive) |
|
265 { |
|
266 MOZ_COUNT_CTOR(nsAttrSelector); |
|
267 } |
|
268 |
|
269 nsAttrSelector* |
|
270 nsAttrSelector::Clone(bool aDeep) const |
|
271 { |
|
272 nsAttrSelector *result = |
|
273 new nsAttrSelector(mNameSpace, mLowercaseAttr, mCasedAttr, |
|
274 mFunction, mValue, mCaseSensitive); |
|
275 |
|
276 if (aDeep) |
|
277 NS_CSS_CLONE_LIST_MEMBER(nsAttrSelector, this, mNext, result, (false)); |
|
278 |
|
279 return result; |
|
280 } |
|
281 |
|
282 nsAttrSelector::~nsAttrSelector(void) |
|
283 { |
|
284 MOZ_COUNT_DTOR(nsAttrSelector); |
|
285 |
|
286 NS_CSS_DELETE_LIST_MEMBER(nsAttrSelector, this, mNext); |
|
287 } |
|
288 |
|
289 // -- nsCSSSelector ------------------------------- |
|
290 |
|
291 nsCSSSelector::nsCSSSelector(void) |
|
292 : mLowercaseTag(nullptr), |
|
293 mCasedTag(nullptr), |
|
294 mIDList(nullptr), |
|
295 mClassList(nullptr), |
|
296 mPseudoClassList(nullptr), |
|
297 mAttrList(nullptr), |
|
298 mNegations(nullptr), |
|
299 mNext(nullptr), |
|
300 mNameSpace(kNameSpaceID_Unknown), |
|
301 mOperator(0), |
|
302 mPseudoType(nsCSSPseudoElements::ePseudo_NotPseudoElement) |
|
303 { |
|
304 MOZ_COUNT_CTOR(nsCSSSelector); |
|
305 static_assert(nsCSSPseudoElements::ePseudo_MAX < INT16_MAX, |
|
306 "nsCSSPseudoElements::Type values overflow mPseudoType"); |
|
307 } |
|
308 |
|
309 nsCSSSelector* |
|
310 nsCSSSelector::Clone(bool aDeepNext, bool aDeepNegations) const |
|
311 { |
|
312 nsCSSSelector *result = new nsCSSSelector(); |
|
313 if (!result) |
|
314 return nullptr; |
|
315 |
|
316 result->mNameSpace = mNameSpace; |
|
317 result->mLowercaseTag = mLowercaseTag; |
|
318 result->mCasedTag = mCasedTag; |
|
319 result->mOperator = mOperator; |
|
320 result->mPseudoType = mPseudoType; |
|
321 |
|
322 NS_IF_CLONE(mIDList); |
|
323 NS_IF_CLONE(mClassList); |
|
324 NS_IF_CLONE(mPseudoClassList); |
|
325 NS_IF_CLONE(mAttrList); |
|
326 |
|
327 // No need to worry about multiple levels of recursion since an |
|
328 // mNegations can't have an mNext. |
|
329 NS_ASSERTION(!mNegations || !mNegations->mNext, |
|
330 "mNegations can't have non-null mNext"); |
|
331 if (aDeepNegations) { |
|
332 NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNegations, result, |
|
333 (true, false)); |
|
334 } |
|
335 |
|
336 if (aDeepNext) { |
|
337 NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNext, result, |
|
338 (false, true)); |
|
339 } |
|
340 |
|
341 return result; |
|
342 } |
|
343 |
|
344 nsCSSSelector::~nsCSSSelector(void) |
|
345 { |
|
346 MOZ_COUNT_DTOR(nsCSSSelector); |
|
347 Reset(); |
|
348 // No need to worry about multiple levels of recursion since an |
|
349 // mNegations can't have an mNext. |
|
350 NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNext); |
|
351 } |
|
352 |
|
353 void nsCSSSelector::Reset(void) |
|
354 { |
|
355 mNameSpace = kNameSpaceID_Unknown; |
|
356 mLowercaseTag = nullptr; |
|
357 mCasedTag = nullptr; |
|
358 NS_IF_DELETE(mIDList); |
|
359 NS_IF_DELETE(mClassList); |
|
360 NS_IF_DELETE(mPseudoClassList); |
|
361 NS_IF_DELETE(mAttrList); |
|
362 // No need to worry about multiple levels of recursion since an |
|
363 // mNegations can't have an mNext. |
|
364 NS_ASSERTION(!mNegations || !mNegations->mNext, |
|
365 "mNegations can't have non-null mNext"); |
|
366 NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNegations); |
|
367 mOperator = char16_t(0); |
|
368 } |
|
369 |
|
370 void nsCSSSelector::SetNameSpace(int32_t aNameSpace) |
|
371 { |
|
372 mNameSpace = aNameSpace; |
|
373 } |
|
374 |
|
375 void nsCSSSelector::SetTag(const nsString& aTag) |
|
376 { |
|
377 if (aTag.IsEmpty()) { |
|
378 mLowercaseTag = mCasedTag = nullptr; |
|
379 return; |
|
380 } |
|
381 |
|
382 mCasedTag = do_GetAtom(aTag); |
|
383 |
|
384 nsAutoString lowercase; |
|
385 nsContentUtils::ASCIIToLower(aTag, lowercase); |
|
386 mLowercaseTag = do_GetAtom(lowercase); |
|
387 } |
|
388 |
|
389 void nsCSSSelector::AddID(const nsString& aID) |
|
390 { |
|
391 if (!aID.IsEmpty()) { |
|
392 nsAtomList** list = &mIDList; |
|
393 while (nullptr != *list) { |
|
394 list = &((*list)->mNext); |
|
395 } |
|
396 *list = new nsAtomList(aID); |
|
397 } |
|
398 } |
|
399 |
|
400 void nsCSSSelector::AddClass(const nsString& aClass) |
|
401 { |
|
402 if (!aClass.IsEmpty()) { |
|
403 nsAtomList** list = &mClassList; |
|
404 while (nullptr != *list) { |
|
405 list = &((*list)->mNext); |
|
406 } |
|
407 *list = new nsAtomList(aClass); |
|
408 } |
|
409 } |
|
410 |
|
411 void nsCSSSelector::AddPseudoClass(nsCSSPseudoClasses::Type aType) |
|
412 { |
|
413 AddPseudoClassInternal(new nsPseudoClassList(aType)); |
|
414 } |
|
415 |
|
416 void nsCSSSelector::AddPseudoClass(nsCSSPseudoClasses::Type aType, |
|
417 const char16_t* aString) |
|
418 { |
|
419 AddPseudoClassInternal(new nsPseudoClassList(aType, aString)); |
|
420 } |
|
421 |
|
422 void nsCSSSelector::AddPseudoClass(nsCSSPseudoClasses::Type aType, |
|
423 const int32_t* aIntPair) |
|
424 { |
|
425 AddPseudoClassInternal(new nsPseudoClassList(aType, aIntPair)); |
|
426 } |
|
427 |
|
428 void nsCSSSelector::AddPseudoClass(nsCSSPseudoClasses::Type aType, |
|
429 nsCSSSelectorList* aSelectorList) |
|
430 { |
|
431 // Take ownership of nsCSSSelectorList instead of copying. |
|
432 AddPseudoClassInternal(new nsPseudoClassList(aType, aSelectorList)); |
|
433 } |
|
434 |
|
435 void nsCSSSelector::AddPseudoClassInternal(nsPseudoClassList *aPseudoClass) |
|
436 { |
|
437 nsPseudoClassList** list = &mPseudoClassList; |
|
438 while (nullptr != *list) { |
|
439 list = &((*list)->mNext); |
|
440 } |
|
441 *list = aPseudoClass; |
|
442 } |
|
443 |
|
444 void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr) |
|
445 { |
|
446 if (!aAttr.IsEmpty()) { |
|
447 nsAttrSelector** list = &mAttrList; |
|
448 while (nullptr != *list) { |
|
449 list = &((*list)->mNext); |
|
450 } |
|
451 *list = new nsAttrSelector(aNameSpace, aAttr); |
|
452 } |
|
453 } |
|
454 |
|
455 void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunc, |
|
456 const nsString& aValue, bool aCaseSensitive) |
|
457 { |
|
458 if (!aAttr.IsEmpty()) { |
|
459 nsAttrSelector** list = &mAttrList; |
|
460 while (nullptr != *list) { |
|
461 list = &((*list)->mNext); |
|
462 } |
|
463 *list = new nsAttrSelector(aNameSpace, aAttr, aFunc, aValue, aCaseSensitive); |
|
464 } |
|
465 } |
|
466 |
|
467 void nsCSSSelector::SetOperator(char16_t aOperator) |
|
468 { |
|
469 mOperator = aOperator; |
|
470 } |
|
471 |
|
472 int32_t nsCSSSelector::CalcWeightWithoutNegations() const |
|
473 { |
|
474 int32_t weight = 0; |
|
475 |
|
476 #ifdef MOZ_XUL |
|
477 MOZ_ASSERT(!(IsPseudoElement() && |
|
478 PseudoType() != nsCSSPseudoElements::ePseudo_XULTree && |
|
479 mClassList), |
|
480 "If non-XUL-tree pseudo-elements can have class selectors " |
|
481 "after them, specificity calculation must be updated"); |
|
482 #else |
|
483 MOZ_ASSERT(!(IsPseudoElement() && mClassList), |
|
484 "If pseudo-elements can have class selectors " |
|
485 "after them, specificity calculation must be updated"); |
|
486 #endif |
|
487 MOZ_ASSERT(!(IsPseudoElement() && (mIDList || mAttrList)), |
|
488 "If pseudo-elements can have id or attribute selectors " |
|
489 "after them, specificity calculation must be updated"); |
|
490 |
|
491 if (nullptr != mCasedTag) { |
|
492 weight += 0x000001; |
|
493 } |
|
494 nsAtomList* list = mIDList; |
|
495 while (nullptr != list) { |
|
496 weight += 0x010000; |
|
497 list = list->mNext; |
|
498 } |
|
499 list = mClassList; |
|
500 #ifdef MOZ_XUL |
|
501 // XUL tree pseudo-elements abuse mClassList to store some private |
|
502 // data; ignore that. |
|
503 if (PseudoType() == nsCSSPseudoElements::ePseudo_XULTree) { |
|
504 list = nullptr; |
|
505 } |
|
506 #endif |
|
507 while (nullptr != list) { |
|
508 weight += 0x000100; |
|
509 list = list->mNext; |
|
510 } |
|
511 // FIXME (bug 561154): This is incorrect for :-moz-any(), which isn't |
|
512 // really a pseudo-class. In order to handle :-moz-any() correctly, |
|
513 // we need to compute specificity after we match, based on which |
|
514 // option we matched with (and thus also need to try the |
|
515 // highest-specificity options first). |
|
516 nsPseudoClassList *plist = mPseudoClassList; |
|
517 while (nullptr != plist) { |
|
518 weight += 0x000100; |
|
519 plist = plist->mNext; |
|
520 } |
|
521 nsAttrSelector* attr = mAttrList; |
|
522 while (nullptr != attr) { |
|
523 weight += 0x000100; |
|
524 attr = attr->mNext; |
|
525 } |
|
526 return weight; |
|
527 } |
|
528 |
|
529 int32_t nsCSSSelector::CalcWeight() const |
|
530 { |
|
531 // Loop over this selector and all its negations. |
|
532 int32_t weight = 0; |
|
533 for (const nsCSSSelector *n = this; n; n = n->mNegations) { |
|
534 weight += n->CalcWeightWithoutNegations(); |
|
535 } |
|
536 return weight; |
|
537 } |
|
538 |
|
539 // |
|
540 // Builds the textual representation of a selector. Called by DOM 2 CSS |
|
541 // StyleRule:selectorText |
|
542 // |
|
543 void |
|
544 nsCSSSelector::ToString(nsAString& aString, nsCSSStyleSheet* aSheet, |
|
545 bool aAppend) const |
|
546 { |
|
547 if (!aAppend) |
|
548 aString.Truncate(); |
|
549 |
|
550 // selectors are linked from right-to-left, so the next selector in |
|
551 // the linked list actually precedes this one in the resulting string |
|
552 nsAutoTArray<const nsCSSSelector*, 8> stack; |
|
553 for (const nsCSSSelector *s = this; s; s = s->mNext) { |
|
554 stack.AppendElement(s); |
|
555 } |
|
556 |
|
557 while (!stack.IsEmpty()) { |
|
558 uint32_t index = stack.Length() - 1; |
|
559 const nsCSSSelector *s = stack.ElementAt(index); |
|
560 stack.RemoveElementAt(index); |
|
561 |
|
562 s->AppendToStringWithoutCombinators(aString, aSheet); |
|
563 |
|
564 // Append the combinator, if needed. |
|
565 if (!stack.IsEmpty()) { |
|
566 const nsCSSSelector *next = stack.ElementAt(index - 1); |
|
567 char16_t oper = s->mOperator; |
|
568 if (next->IsPseudoElement()) { |
|
569 NS_ASSERTION(oper == char16_t(':'), |
|
570 "improperly chained pseudo element"); |
|
571 } else { |
|
572 NS_ASSERTION(oper != char16_t(0), |
|
573 "compound selector without combinator"); |
|
574 |
|
575 aString.Append(char16_t(' ')); |
|
576 if (oper != char16_t(' ')) { |
|
577 aString.Append(oper); |
|
578 aString.Append(char16_t(' ')); |
|
579 } |
|
580 } |
|
581 } |
|
582 } |
|
583 } |
|
584 |
|
585 void |
|
586 nsCSSSelector::AppendToStringWithoutCombinators |
|
587 (nsAString& aString, nsCSSStyleSheet* aSheet) const |
|
588 { |
|
589 AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, false); |
|
590 |
|
591 for (const nsCSSSelector* negation = mNegations; negation; |
|
592 negation = negation->mNegations) { |
|
593 aString.AppendLiteral(":not("); |
|
594 negation->AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, |
|
595 true); |
|
596 aString.Append(char16_t(')')); |
|
597 } |
|
598 } |
|
599 |
|
600 void |
|
601 nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations |
|
602 (nsAString& aString, nsCSSStyleSheet* aSheet, |
|
603 bool aIsNegated) const |
|
604 { |
|
605 nsAutoString temp; |
|
606 bool isPseudoElement = IsPseudoElement(); |
|
607 |
|
608 // For non-pseudo-element selectors or for lone pseudo-elements, deal with |
|
609 // namespace prefixes. |
|
610 bool wroteNamespace = false; |
|
611 if (!isPseudoElement || !mNext) { |
|
612 // append the namespace prefix if needed |
|
613 nsXMLNameSpaceMap *sheetNS = aSheet ? aSheet->GetNameSpaceMap() : nullptr; |
|
614 |
|
615 // sheetNS is non-null if and only if we had an @namespace rule. If it's |
|
616 // null, that means that the only namespaces we could have are the |
|
617 // wildcard namespace (which can be implicit in this case) and the "none" |
|
618 // namespace, which then needs to be explicitly specified. |
|
619 if (!sheetNS) { |
|
620 NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown || |
|
621 mNameSpace == kNameSpaceID_None, |
|
622 "How did we get this namespace?"); |
|
623 if (mNameSpace == kNameSpaceID_None) { |
|
624 aString.Append(char16_t('|')); |
|
625 wroteNamespace = true; |
|
626 } |
|
627 } else if (sheetNS->FindNameSpaceID(nullptr) == mNameSpace) { |
|
628 // We have the default namespace (possibly including the wildcard |
|
629 // namespace). Do nothing. |
|
630 NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown || |
|
631 CanBeNamespaced(aIsNegated), |
|
632 "How did we end up with this namespace?"); |
|
633 } else if (mNameSpace == kNameSpaceID_None) { |
|
634 NS_ASSERTION(CanBeNamespaced(aIsNegated), |
|
635 "How did we end up with this namespace?"); |
|
636 aString.Append(char16_t('|')); |
|
637 wroteNamespace = true; |
|
638 } else if (mNameSpace != kNameSpaceID_Unknown) { |
|
639 NS_ASSERTION(CanBeNamespaced(aIsNegated), |
|
640 "How did we end up with this namespace?"); |
|
641 nsIAtom *prefixAtom = sheetNS->FindPrefix(mNameSpace); |
|
642 NS_ASSERTION(prefixAtom, "how'd we get a non-default namespace " |
|
643 "without a prefix?"); |
|
644 nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(prefixAtom), |
|
645 aString); |
|
646 aString.Append(char16_t('|')); |
|
647 wroteNamespace = true; |
|
648 } else { |
|
649 // A selector for an element in any namespace, while the default |
|
650 // namespace is something else. :not() is special in that the default |
|
651 // namespace is not implied for non-type selectors, so if this is a |
|
652 // negated non-type selector we don't need to output an explicit wildcard |
|
653 // namespace here, since those default to a wildcard namespace. |
|
654 if (CanBeNamespaced(aIsNegated)) { |
|
655 aString.AppendLiteral("*|"); |
|
656 wroteNamespace = true; |
|
657 } |
|
658 } |
|
659 } |
|
660 |
|
661 if (!mLowercaseTag) { |
|
662 // Universal selector: avoid writing the universal selector when we |
|
663 // can avoid it, especially since we're required to avoid it for the |
|
664 // inside of :not() |
|
665 if (wroteNamespace || |
|
666 (!mIDList && !mClassList && !mPseudoClassList && !mAttrList && |
|
667 (aIsNegated || !mNegations))) { |
|
668 aString.Append(char16_t('*')); |
|
669 } |
|
670 } else { |
|
671 // Append the tag name |
|
672 nsAutoString tag; |
|
673 (isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag); |
|
674 if (isPseudoElement) { |
|
675 if (!mNext) { |
|
676 // Lone pseudo-element selector -- toss in a wildcard type selector |
|
677 // XXXldb Why? |
|
678 aString.Append(char16_t('*')); |
|
679 } |
|
680 if (!nsCSSPseudoElements::IsCSS2PseudoElement(mLowercaseTag)) { |
|
681 aString.Append(char16_t(':')); |
|
682 } |
|
683 // This should not be escaped since (a) the pseudo-element string |
|
684 // has a ":" that can't be escaped and (b) all pseudo-elements at |
|
685 // this point are known, and therefore we know they don't need |
|
686 // escaping. |
|
687 aString.Append(tag); |
|
688 } else { |
|
689 nsStyleUtil::AppendEscapedCSSIdent(tag, aString); |
|
690 } |
|
691 } |
|
692 |
|
693 // Append the id, if there is one |
|
694 if (mIDList) { |
|
695 nsAtomList* list = mIDList; |
|
696 while (list != nullptr) { |
|
697 list->mAtom->ToString(temp); |
|
698 aString.Append(char16_t('#')); |
|
699 nsStyleUtil::AppendEscapedCSSIdent(temp, aString); |
|
700 list = list->mNext; |
|
701 } |
|
702 } |
|
703 |
|
704 // Append each class in the linked list |
|
705 if (mClassList) { |
|
706 if (isPseudoElement) { |
|
707 #ifdef MOZ_XUL |
|
708 NS_ABORT_IF_FALSE(nsCSSAnonBoxes::IsTreePseudoElement(mLowercaseTag), |
|
709 "must be tree pseudo-element"); |
|
710 |
|
711 aString.Append(char16_t('(')); |
|
712 for (nsAtomList* list = mClassList; list; list = list->mNext) { |
|
713 nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(list->mAtom), aString); |
|
714 aString.Append(char16_t(',')); |
|
715 } |
|
716 // replace the final comma with a close-paren |
|
717 aString.Replace(aString.Length() - 1, 1, char16_t(')')); |
|
718 #else |
|
719 NS_ERROR("Can't happen"); |
|
720 #endif |
|
721 } else { |
|
722 nsAtomList* list = mClassList; |
|
723 while (list != nullptr) { |
|
724 list->mAtom->ToString(temp); |
|
725 aString.Append(char16_t('.')); |
|
726 nsStyleUtil::AppendEscapedCSSIdent(temp, aString); |
|
727 list = list->mNext; |
|
728 } |
|
729 } |
|
730 } |
|
731 |
|
732 // Append each attribute selector in the linked list |
|
733 if (mAttrList) { |
|
734 nsAttrSelector* list = mAttrList; |
|
735 while (list != nullptr) { |
|
736 aString.Append(char16_t('[')); |
|
737 // Append the namespace prefix |
|
738 if (list->mNameSpace == kNameSpaceID_Unknown) { |
|
739 aString.Append(char16_t('*')); |
|
740 aString.Append(char16_t('|')); |
|
741 } else if (list->mNameSpace != kNameSpaceID_None) { |
|
742 if (aSheet) { |
|
743 nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap(); |
|
744 nsIAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace); |
|
745 // Default namespaces don't apply to attribute selectors, so |
|
746 // we must have a useful prefix. |
|
747 NS_ASSERTION(prefixAtom, |
|
748 "How did we end up with a namespace if the prefix " |
|
749 "is unknown?"); |
|
750 nsAutoString prefix; |
|
751 prefixAtom->ToString(prefix); |
|
752 nsStyleUtil::AppendEscapedCSSIdent(prefix, aString); |
|
753 aString.Append(char16_t('|')); |
|
754 } |
|
755 } |
|
756 // Append the attribute name |
|
757 list->mCasedAttr->ToString(temp); |
|
758 nsStyleUtil::AppendEscapedCSSIdent(temp, aString); |
|
759 |
|
760 if (list->mFunction != NS_ATTR_FUNC_SET) { |
|
761 // Append the function |
|
762 if (list->mFunction == NS_ATTR_FUNC_INCLUDES) |
|
763 aString.Append(char16_t('~')); |
|
764 else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH) |
|
765 aString.Append(char16_t('|')); |
|
766 else if (list->mFunction == NS_ATTR_FUNC_BEGINSMATCH) |
|
767 aString.Append(char16_t('^')); |
|
768 else if (list->mFunction == NS_ATTR_FUNC_ENDSMATCH) |
|
769 aString.Append(char16_t('$')); |
|
770 else if (list->mFunction == NS_ATTR_FUNC_CONTAINSMATCH) |
|
771 aString.Append(char16_t('*')); |
|
772 |
|
773 aString.Append(char16_t('=')); |
|
774 |
|
775 // Append the value |
|
776 nsStyleUtil::AppendEscapedCSSString(list->mValue, aString); |
|
777 } |
|
778 |
|
779 aString.Append(char16_t(']')); |
|
780 |
|
781 list = list->mNext; |
|
782 } |
|
783 } |
|
784 |
|
785 // Append each pseudo-class in the linked list |
|
786 for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) { |
|
787 nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp); |
|
788 // This should not be escaped since (a) the pseudo-class string |
|
789 // has a ":" that can't be escaped and (b) all pseudo-classes at |
|
790 // this point are known, and therefore we know they don't need |
|
791 // escaping. |
|
792 aString.Append(temp); |
|
793 if (list->u.mMemory) { |
|
794 aString.Append(char16_t('(')); |
|
795 if (nsCSSPseudoClasses::HasStringArg(list->mType)) { |
|
796 nsStyleUtil::AppendEscapedCSSIdent( |
|
797 nsDependentString(list->u.mString), aString); |
|
798 } else if (nsCSSPseudoClasses::HasNthPairArg(list->mType)) { |
|
799 int32_t a = list->u.mNumbers[0], |
|
800 b = list->u.mNumbers[1]; |
|
801 temp.Truncate(); |
|
802 if (a != 0) { |
|
803 if (a == -1) { |
|
804 temp.Append(char16_t('-')); |
|
805 } else if (a != 1) { |
|
806 temp.AppendInt(a); |
|
807 } |
|
808 temp.Append(char16_t('n')); |
|
809 } |
|
810 if (b != 0 || a == 0) { |
|
811 if (b >= 0 && a != 0) // check a != 0 for whether we printed above |
|
812 temp.Append(char16_t('+')); |
|
813 temp.AppendInt(b); |
|
814 } |
|
815 aString.Append(temp); |
|
816 } else { |
|
817 NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(list->mType), |
|
818 "unexpected pseudo-class"); |
|
819 nsString tmp; |
|
820 list->u.mSelectors->ToString(tmp, aSheet); |
|
821 aString.Append(tmp); |
|
822 } |
|
823 aString.Append(char16_t(')')); |
|
824 } |
|
825 } |
|
826 } |
|
827 |
|
828 bool |
|
829 nsCSSSelector::CanBeNamespaced(bool aIsNegated) const |
|
830 { |
|
831 return !aIsNegated || |
|
832 (!mIDList && !mClassList && !mPseudoClassList && !mAttrList); |
|
833 } |
|
834 |
|
835 size_t |
|
836 nsCSSSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
|
837 { |
|
838 size_t n = 0; |
|
839 const nsCSSSelector* s = this; |
|
840 while (s) { |
|
841 n += aMallocSizeOf(s); |
|
842 |
|
843 #define MEASURE(x) n += x ? x->SizeOfIncludingThis(aMallocSizeOf) : 0; |
|
844 |
|
845 MEASURE(s->mIDList); |
|
846 MEASURE(s->mClassList); |
|
847 MEASURE(s->mPseudoClassList); |
|
848 MEASURE(s->mNegations); |
|
849 |
|
850 // Measurement of the following members may be added later if DMD finds it is |
|
851 // worthwhile: |
|
852 // - s->mAttrList |
|
853 // |
|
854 // The following members aren't measured: |
|
855 // - s->mLowercaseTag, because it's an atom and therefore shared |
|
856 // - s->mCasedTag, because it's an atom and therefore shared |
|
857 |
|
858 s = s->mNext; |
|
859 } |
|
860 return n; |
|
861 } |
|
862 |
|
863 // -- nsCSSSelectorList ------------------------------- |
|
864 |
|
865 nsCSSSelectorList::nsCSSSelectorList(void) |
|
866 : mSelectors(nullptr), |
|
867 mWeight(0), |
|
868 mNext(nullptr) |
|
869 { |
|
870 MOZ_COUNT_CTOR(nsCSSSelectorList); |
|
871 } |
|
872 |
|
873 nsCSSSelectorList::~nsCSSSelectorList() |
|
874 { |
|
875 MOZ_COUNT_DTOR(nsCSSSelectorList); |
|
876 delete mSelectors; |
|
877 NS_CSS_DELETE_LIST_MEMBER(nsCSSSelectorList, this, mNext); |
|
878 } |
|
879 |
|
880 nsCSSSelector* |
|
881 nsCSSSelectorList::AddSelector(char16_t aOperator) |
|
882 { |
|
883 nsCSSSelector* newSel = new nsCSSSelector(); |
|
884 |
|
885 if (mSelectors) { |
|
886 NS_ASSERTION(aOperator != char16_t(0), "chaining without combinator"); |
|
887 mSelectors->SetOperator(aOperator); |
|
888 } else { |
|
889 NS_ASSERTION(aOperator == char16_t(0), "combinator without chaining"); |
|
890 } |
|
891 |
|
892 newSel->mNext = mSelectors; |
|
893 mSelectors = newSel; |
|
894 return newSel; |
|
895 } |
|
896 |
|
897 void |
|
898 nsCSSSelectorList::ToString(nsAString& aResult, nsCSSStyleSheet* aSheet) |
|
899 { |
|
900 aResult.Truncate(); |
|
901 nsCSSSelectorList *p = this; |
|
902 for (;;) { |
|
903 p->mSelectors->ToString(aResult, aSheet, true); |
|
904 p = p->mNext; |
|
905 if (!p) |
|
906 break; |
|
907 aResult.AppendLiteral(", "); |
|
908 } |
|
909 } |
|
910 |
|
911 nsCSSSelectorList* |
|
912 nsCSSSelectorList::Clone(bool aDeep) const |
|
913 { |
|
914 nsCSSSelectorList *result = new nsCSSSelectorList(); |
|
915 result->mWeight = mWeight; |
|
916 NS_IF_CLONE(mSelectors); |
|
917 |
|
918 if (aDeep) { |
|
919 NS_CSS_CLONE_LIST_MEMBER(nsCSSSelectorList, this, mNext, result, |
|
920 (false)); |
|
921 } |
|
922 return result; |
|
923 } |
|
924 |
|
925 size_t |
|
926 nsCSSSelectorList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
|
927 { |
|
928 size_t n = 0; |
|
929 const nsCSSSelectorList* s = this; |
|
930 while (s) { |
|
931 n += aMallocSizeOf(s); |
|
932 n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0; |
|
933 s = s->mNext; |
|
934 } |
|
935 return n; |
|
936 } |
|
937 |
|
938 // -- ImportantRule ---------------------------------- |
|
939 |
|
940 namespace mozilla { |
|
941 namespace css { |
|
942 |
|
943 ImportantRule::ImportantRule(Declaration* aDeclaration) |
|
944 : mDeclaration(aDeclaration) |
|
945 { |
|
946 } |
|
947 |
|
948 ImportantRule::~ImportantRule() |
|
949 { |
|
950 } |
|
951 |
|
952 NS_IMPL_ISUPPORTS(ImportantRule, nsIStyleRule) |
|
953 |
|
954 /* virtual */ void |
|
955 ImportantRule::MapRuleInfoInto(nsRuleData* aRuleData) |
|
956 { |
|
957 mDeclaration->MapImportantRuleInfoInto(aRuleData); |
|
958 } |
|
959 |
|
960 #ifdef DEBUG |
|
961 /* virtual */ void |
|
962 ImportantRule::List(FILE* out, int32_t aIndent) const |
|
963 { |
|
964 // Indent |
|
965 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); |
|
966 |
|
967 fprintf(out, "! Important declaration=%p\n", |
|
968 static_cast<void*>(mDeclaration)); |
|
969 } |
|
970 #endif |
|
971 |
|
972 } // namespace css |
|
973 } // namespace mozilla |
|
974 |
|
975 // -------------------------------------------------------- |
|
976 |
|
977 namespace mozilla { |
|
978 namespace css { |
|
979 class DOMCSSStyleRule; |
|
980 } |
|
981 } |
|
982 |
|
983 class DOMCSSDeclarationImpl : public nsDOMCSSDeclaration |
|
984 { |
|
985 public: |
|
986 DOMCSSDeclarationImpl(css::StyleRule *aRule); |
|
987 virtual ~DOMCSSDeclarationImpl(void); |
|
988 |
|
989 NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) MOZ_OVERRIDE; |
|
990 void DropReference(void); |
|
991 virtual css::Declaration* GetCSSDeclaration(bool aAllocate) MOZ_OVERRIDE; |
|
992 virtual nsresult SetCSSDeclaration(css::Declaration* aDecl) MOZ_OVERRIDE; |
|
993 virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) MOZ_OVERRIDE; |
|
994 virtual nsIDocument* DocToUpdate() MOZ_OVERRIDE; |
|
995 |
|
996 // Override |AddRef| and |Release| for being a member of |
|
997 // |DOMCSSStyleRule|. Also, we need to forward QI for cycle |
|
998 // collection things to DOMCSSStyleRule. |
|
999 NS_DECL_ISUPPORTS_INHERITED |
|
1000 |
|
1001 virtual nsINode *GetParentObject() MOZ_OVERRIDE |
|
1002 { |
|
1003 return mRule ? mRule->GetDocument() : nullptr; |
|
1004 } |
|
1005 |
|
1006 friend class css::DOMCSSStyleRule; |
|
1007 |
|
1008 protected: |
|
1009 // This reference is not reference-counted. The rule object tells us |
|
1010 // when it's about to go away. |
|
1011 css::StyleRule *mRule; |
|
1012 |
|
1013 inline css::DOMCSSStyleRule* DomRule(); |
|
1014 |
|
1015 private: |
|
1016 // NOT TO BE IMPLEMENTED |
|
1017 // This object cannot be allocated on its own. It must be a member of |
|
1018 // DOMCSSStyleRule. |
|
1019 void* operator new(size_t size) CPP_THROW_NEW; |
|
1020 }; |
|
1021 |
|
1022 namespace mozilla { |
|
1023 namespace css { |
|
1024 |
|
1025 class DOMCSSStyleRule : public nsICSSStyleRuleDOMWrapper |
|
1026 { |
|
1027 public: |
|
1028 DOMCSSStyleRule(StyleRule *aRule); |
|
1029 virtual ~DOMCSSStyleRule(); |
|
1030 |
|
1031 NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
|
1032 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMCSSStyleRule) |
|
1033 NS_DECL_NSIDOMCSSRULE |
|
1034 NS_DECL_NSIDOMCSSSTYLERULE |
|
1035 |
|
1036 // nsICSSStyleRuleDOMWrapper |
|
1037 NS_IMETHOD GetCSSStyleRule(StyleRule **aResult); |
|
1038 |
|
1039 DOMCSSDeclarationImpl* DOMDeclaration() { return &mDOMDeclaration; } |
|
1040 |
|
1041 friend class ::DOMCSSDeclarationImpl; |
|
1042 |
|
1043 protected: |
|
1044 DOMCSSDeclarationImpl mDOMDeclaration; |
|
1045 |
|
1046 StyleRule* Rule() { |
|
1047 return mDOMDeclaration.mRule; |
|
1048 } |
|
1049 }; |
|
1050 |
|
1051 } // namespace css |
|
1052 } // namespace mozilla |
|
1053 |
|
1054 DOMCSSDeclarationImpl::DOMCSSDeclarationImpl(css::StyleRule *aRule) |
|
1055 : mRule(aRule) |
|
1056 { |
|
1057 MOZ_COUNT_CTOR(DOMCSSDeclarationImpl); |
|
1058 } |
|
1059 |
|
1060 DOMCSSDeclarationImpl::~DOMCSSDeclarationImpl(void) |
|
1061 { |
|
1062 NS_ASSERTION(!mRule, "DropReference not called."); |
|
1063 |
|
1064 MOZ_COUNT_DTOR(DOMCSSDeclarationImpl); |
|
1065 } |
|
1066 |
|
1067 inline css::DOMCSSStyleRule* DOMCSSDeclarationImpl::DomRule() |
|
1068 { |
|
1069 return reinterpret_cast<css::DOMCSSStyleRule*> |
|
1070 (reinterpret_cast<char*>(this) - |
|
1071 offsetof(css::DOMCSSStyleRule, mDOMDeclaration)); |
|
1072 } |
|
1073 |
|
1074 NS_IMPL_ADDREF_USING_AGGREGATOR(DOMCSSDeclarationImpl, DomRule()) |
|
1075 NS_IMPL_RELEASE_USING_AGGREGATOR(DOMCSSDeclarationImpl, DomRule()) |
|
1076 |
|
1077 NS_INTERFACE_MAP_BEGIN(DOMCSSDeclarationImpl) |
|
1078 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
1079 // We forward the cycle collection interfaces to DomRule(), which is |
|
1080 // never null (in fact, we're part of that object!) |
|
1081 if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) || |
|
1082 aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) { |
|
1083 return DomRule()->QueryInterface(aIID, aInstancePtr); |
|
1084 } |
|
1085 else |
|
1086 NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration) |
|
1087 |
|
1088 void |
|
1089 DOMCSSDeclarationImpl::DropReference(void) |
|
1090 { |
|
1091 mRule = nullptr; |
|
1092 } |
|
1093 |
|
1094 css::Declaration* |
|
1095 DOMCSSDeclarationImpl::GetCSSDeclaration(bool aAllocate) |
|
1096 { |
|
1097 if (mRule) { |
|
1098 return mRule->GetDeclaration(); |
|
1099 } else { |
|
1100 return nullptr; |
|
1101 } |
|
1102 } |
|
1103 |
|
1104 void |
|
1105 DOMCSSDeclarationImpl::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) |
|
1106 { |
|
1107 GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv); |
|
1108 } |
|
1109 |
|
1110 NS_IMETHODIMP |
|
1111 DOMCSSDeclarationImpl::GetParentRule(nsIDOMCSSRule **aParent) |
|
1112 { |
|
1113 NS_ENSURE_ARG_POINTER(aParent); |
|
1114 |
|
1115 if (!mRule) { |
|
1116 *aParent = nullptr; |
|
1117 return NS_OK; |
|
1118 } |
|
1119 |
|
1120 NS_IF_ADDREF(*aParent = mRule->GetDOMRule()); |
|
1121 return NS_OK; |
|
1122 } |
|
1123 |
|
1124 nsresult |
|
1125 DOMCSSDeclarationImpl::SetCSSDeclaration(css::Declaration* aDecl) |
|
1126 { |
|
1127 NS_PRECONDITION(mRule, |
|
1128 "can only be called when |GetCSSDeclaration| returned a declaration"); |
|
1129 |
|
1130 nsCOMPtr<nsIDocument> owningDoc; |
|
1131 nsCOMPtr<nsIStyleSheet> sheet = mRule->GetStyleSheet(); |
|
1132 if (sheet) { |
|
1133 owningDoc = sheet->GetOwningDocument(); |
|
1134 } |
|
1135 |
|
1136 mozAutoDocUpdate updateBatch(owningDoc, UPDATE_STYLE, true); |
|
1137 |
|
1138 nsRefPtr<css::StyleRule> oldRule = mRule; |
|
1139 mRule = oldRule->DeclarationChanged(aDecl, true).take(); |
|
1140 if (!mRule) |
|
1141 return NS_ERROR_OUT_OF_MEMORY; |
|
1142 nsrefcnt cnt = mRule->Release(); |
|
1143 if (cnt == 0) { |
|
1144 NS_NOTREACHED("container didn't take ownership"); |
|
1145 mRule = nullptr; |
|
1146 return NS_ERROR_UNEXPECTED; |
|
1147 } |
|
1148 |
|
1149 if (owningDoc) { |
|
1150 owningDoc->StyleRuleChanged(sheet, oldRule, mRule); |
|
1151 } |
|
1152 return NS_OK; |
|
1153 } |
|
1154 |
|
1155 nsIDocument* |
|
1156 DOMCSSDeclarationImpl::DocToUpdate() |
|
1157 { |
|
1158 return nullptr; |
|
1159 } |
|
1160 |
|
1161 // needs to be outside the namespace |
|
1162 DOMCI_DATA(CSSStyleRule, css::DOMCSSStyleRule) |
|
1163 |
|
1164 namespace mozilla { |
|
1165 namespace css { |
|
1166 |
|
1167 DOMCSSStyleRule::DOMCSSStyleRule(StyleRule* aRule) |
|
1168 : mDOMDeclaration(aRule) |
|
1169 { |
|
1170 } |
|
1171 |
|
1172 DOMCSSStyleRule::~DOMCSSStyleRule() |
|
1173 { |
|
1174 } |
|
1175 |
|
1176 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMCSSStyleRule) |
|
1177 NS_INTERFACE_MAP_ENTRY(nsICSSStyleRuleDOMWrapper) |
|
1178 NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleRule) |
|
1179 NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) |
|
1180 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
1181 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSStyleRule) |
|
1182 NS_INTERFACE_MAP_END |
|
1183 |
|
1184 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMCSSStyleRule) |
|
1185 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMCSSStyleRule) |
|
1186 |
|
1187 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMCSSStyleRule) |
|
1188 |
|
1189 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMCSSStyleRule) |
|
1190 // Trace the wrapper for our declaration. This just expands out |
|
1191 // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use |
|
1192 // directly because the wrapper is on the declaration, not on us. |
|
1193 tmp->DOMDeclaration()->TraceWrapper(aCallbacks, aClosure); |
|
1194 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
1195 |
|
1196 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMCSSStyleRule) |
|
1197 // Unlink the wrapper for our declaraton. This just expands out |
|
1198 // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use |
|
1199 // directly because the wrapper is on the declaration, not on us. |
|
1200 tmp->DOMDeclaration()->ReleaseWrapper(static_cast<nsISupports*>(p)); |
|
1201 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
1202 |
|
1203 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMCSSStyleRule) |
|
1204 // Just NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS here: that will call |
|
1205 // into our Trace hook, where we do the right thing with declarations |
|
1206 // already. |
|
1207 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
1208 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
1209 |
|
1210 NS_IMETHODIMP |
|
1211 DOMCSSStyleRule::GetType(uint16_t* aType) |
|
1212 { |
|
1213 *aType = nsIDOMCSSRule::STYLE_RULE; |
|
1214 |
|
1215 return NS_OK; |
|
1216 } |
|
1217 |
|
1218 NS_IMETHODIMP |
|
1219 DOMCSSStyleRule::GetCssText(nsAString& aCssText) |
|
1220 { |
|
1221 if (!Rule()) { |
|
1222 aCssText.Truncate(); |
|
1223 } else { |
|
1224 Rule()->GetCssText(aCssText); |
|
1225 } |
|
1226 return NS_OK; |
|
1227 } |
|
1228 |
|
1229 NS_IMETHODIMP |
|
1230 DOMCSSStyleRule::SetCssText(const nsAString& aCssText) |
|
1231 { |
|
1232 if (Rule()) { |
|
1233 Rule()->SetCssText(aCssText); |
|
1234 } |
|
1235 return NS_OK; |
|
1236 } |
|
1237 |
|
1238 NS_IMETHODIMP |
|
1239 DOMCSSStyleRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet) |
|
1240 { |
|
1241 if (!Rule()) { |
|
1242 *aSheet = nullptr; |
|
1243 return NS_OK; |
|
1244 } |
|
1245 return Rule()->GetParentStyleSheet(aSheet); |
|
1246 } |
|
1247 |
|
1248 NS_IMETHODIMP |
|
1249 DOMCSSStyleRule::GetParentRule(nsIDOMCSSRule** aParentRule) |
|
1250 { |
|
1251 if (!Rule()) { |
|
1252 *aParentRule = nullptr; |
|
1253 return NS_OK; |
|
1254 } |
|
1255 return Rule()->GetParentRule(aParentRule); |
|
1256 } |
|
1257 |
|
1258 NS_IMETHODIMP |
|
1259 DOMCSSStyleRule::GetSelectorText(nsAString& aSelectorText) |
|
1260 { |
|
1261 if (!Rule()) { |
|
1262 aSelectorText.Truncate(); |
|
1263 } else { |
|
1264 Rule()->GetSelectorText(aSelectorText); |
|
1265 } |
|
1266 return NS_OK; |
|
1267 } |
|
1268 |
|
1269 NS_IMETHODIMP |
|
1270 DOMCSSStyleRule::SetSelectorText(const nsAString& aSelectorText) |
|
1271 { |
|
1272 if (Rule()) { |
|
1273 Rule()->SetSelectorText(aSelectorText); |
|
1274 } |
|
1275 return NS_OK; |
|
1276 } |
|
1277 |
|
1278 NS_IMETHODIMP |
|
1279 DOMCSSStyleRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle) |
|
1280 { |
|
1281 *aStyle = &mDOMDeclaration; |
|
1282 NS_ADDREF(*aStyle); |
|
1283 return NS_OK; |
|
1284 } |
|
1285 |
|
1286 NS_IMETHODIMP |
|
1287 DOMCSSStyleRule::GetCSSStyleRule(StyleRule **aResult) |
|
1288 { |
|
1289 *aResult = Rule(); |
|
1290 NS_IF_ADDREF(*aResult); |
|
1291 return NS_OK; |
|
1292 } |
|
1293 |
|
1294 } // namespace css |
|
1295 } // namespace mozilla |
|
1296 |
|
1297 // -- StyleRule ------------------------------------ |
|
1298 |
|
1299 namespace mozilla { |
|
1300 namespace css { |
|
1301 |
|
1302 StyleRule::StyleRule(nsCSSSelectorList* aSelector, |
|
1303 Declaration* aDeclaration) |
|
1304 : Rule(), |
|
1305 mSelector(aSelector), |
|
1306 mDeclaration(aDeclaration), |
|
1307 mImportantRule(nullptr), |
|
1308 mDOMRule(nullptr), |
|
1309 mLineNumber(0), |
|
1310 mColumnNumber(0), |
|
1311 mWasMatched(false) |
|
1312 { |
|
1313 NS_PRECONDITION(aDeclaration, "must have a declaration"); |
|
1314 } |
|
1315 |
|
1316 // for |Clone| |
|
1317 StyleRule::StyleRule(const StyleRule& aCopy) |
|
1318 : Rule(aCopy), |
|
1319 mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr), |
|
1320 mDeclaration(new Declaration(*aCopy.mDeclaration)), |
|
1321 mImportantRule(nullptr), |
|
1322 mDOMRule(nullptr), |
|
1323 mLineNumber(aCopy.mLineNumber), |
|
1324 mColumnNumber(aCopy.mColumnNumber), |
|
1325 mWasMatched(false) |
|
1326 { |
|
1327 // rest is constructed lazily on existing data |
|
1328 } |
|
1329 |
|
1330 // for |SetCSSDeclaration| |
|
1331 StyleRule::StyleRule(StyleRule& aCopy, |
|
1332 Declaration* aDeclaration) |
|
1333 : Rule(aCopy), |
|
1334 mSelector(aCopy.mSelector), |
|
1335 mDeclaration(aDeclaration), |
|
1336 mImportantRule(nullptr), |
|
1337 mDOMRule(aCopy.mDOMRule), |
|
1338 mLineNumber(aCopy.mLineNumber), |
|
1339 mColumnNumber(aCopy.mColumnNumber), |
|
1340 mWasMatched(false) |
|
1341 { |
|
1342 // The DOM rule is replacing |aCopy| with |this|, so transfer |
|
1343 // the reverse pointer as well (and transfer ownership). |
|
1344 aCopy.mDOMRule = nullptr; |
|
1345 |
|
1346 // Similarly for the selector. |
|
1347 aCopy.mSelector = nullptr; |
|
1348 |
|
1349 // We are probably replacing the old declaration with |aDeclaration| |
|
1350 // instead of taking ownership of the old declaration; only null out |
|
1351 // aCopy.mDeclaration if we are taking ownership. |
|
1352 if (mDeclaration == aCopy.mDeclaration) { |
|
1353 // This should only ever happen if the declaration was modifiable. |
|
1354 mDeclaration->AssertMutable(); |
|
1355 aCopy.mDeclaration = nullptr; |
|
1356 } |
|
1357 } |
|
1358 |
|
1359 StyleRule::~StyleRule() |
|
1360 { |
|
1361 delete mSelector; |
|
1362 delete mDeclaration; |
|
1363 NS_IF_RELEASE(mImportantRule); |
|
1364 if (mDOMRule) { |
|
1365 mDOMRule->DOMDeclaration()->DropReference(); |
|
1366 NS_RELEASE(mDOMRule); |
|
1367 } |
|
1368 } |
|
1369 |
|
1370 // QueryInterface implementation for StyleRule |
|
1371 NS_INTERFACE_MAP_BEGIN(StyleRule) |
|
1372 if (aIID.Equals(NS_GET_IID(mozilla::css::StyleRule))) { |
|
1373 *aInstancePtr = this; |
|
1374 NS_ADDREF_THIS(); |
|
1375 return NS_OK; |
|
1376 } |
|
1377 else |
|
1378 NS_INTERFACE_MAP_ENTRY(nsIStyleRule) |
|
1379 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) |
|
1380 NS_INTERFACE_MAP_END |
|
1381 |
|
1382 NS_IMPL_ADDREF(StyleRule) |
|
1383 NS_IMPL_RELEASE(StyleRule) |
|
1384 |
|
1385 void |
|
1386 StyleRule::RuleMatched() |
|
1387 { |
|
1388 if (!mWasMatched) { |
|
1389 NS_ABORT_IF_FALSE(!mImportantRule, "should not have important rule yet"); |
|
1390 |
|
1391 mWasMatched = true; |
|
1392 mDeclaration->SetImmutable(); |
|
1393 if (mDeclaration->HasImportantData()) { |
|
1394 NS_ADDREF(mImportantRule = new ImportantRule(mDeclaration)); |
|
1395 } |
|
1396 } |
|
1397 } |
|
1398 |
|
1399 /* virtual */ int32_t |
|
1400 StyleRule::GetType() const |
|
1401 { |
|
1402 return Rule::STYLE_RULE; |
|
1403 } |
|
1404 |
|
1405 /* virtual */ already_AddRefed<Rule> |
|
1406 StyleRule::Clone() const |
|
1407 { |
|
1408 nsRefPtr<Rule> clone = new StyleRule(*this); |
|
1409 return clone.forget(); |
|
1410 } |
|
1411 |
|
1412 /* virtual */ nsIDOMCSSRule* |
|
1413 StyleRule::GetDOMRule() |
|
1414 { |
|
1415 if (!mDOMRule) { |
|
1416 if (!GetStyleSheet()) { |
|
1417 // Inline style rules aren't supposed to have a DOM rule object, only |
|
1418 // a declaration. But if we do have one already, from a style sheet |
|
1419 // rule that used to be in a document, we still want to return it. |
|
1420 return nullptr; |
|
1421 } |
|
1422 mDOMRule = new DOMCSSStyleRule(this); |
|
1423 NS_ADDREF(mDOMRule); |
|
1424 } |
|
1425 return mDOMRule; |
|
1426 } |
|
1427 |
|
1428 /* virtual */ nsIDOMCSSRule* |
|
1429 StyleRule::GetExistingDOMRule() |
|
1430 { |
|
1431 return mDOMRule; |
|
1432 } |
|
1433 |
|
1434 /* virtual */ already_AddRefed<StyleRule> |
|
1435 StyleRule::DeclarationChanged(Declaration* aDecl, |
|
1436 bool aHandleContainer) |
|
1437 { |
|
1438 nsRefPtr<StyleRule> clone = new StyleRule(*this, aDecl); |
|
1439 |
|
1440 if (aHandleContainer) { |
|
1441 nsCSSStyleSheet* sheet = GetStyleSheet(); |
|
1442 if (mParentRule) { |
|
1443 if (sheet) { |
|
1444 sheet->ReplaceRuleInGroup(mParentRule, this, clone); |
|
1445 } else { |
|
1446 mParentRule->ReplaceStyleRule(this, clone); |
|
1447 } |
|
1448 } else if (sheet) { |
|
1449 sheet->ReplaceStyleRule(this, clone); |
|
1450 } |
|
1451 } |
|
1452 |
|
1453 return clone.forget(); |
|
1454 } |
|
1455 |
|
1456 /* virtual */ void |
|
1457 StyleRule::MapRuleInfoInto(nsRuleData* aRuleData) |
|
1458 { |
|
1459 NS_ABORT_IF_FALSE(mWasMatched, |
|
1460 "somebody forgot to call css::StyleRule::RuleMatched"); |
|
1461 mDeclaration->MapNormalRuleInfoInto(aRuleData); |
|
1462 } |
|
1463 |
|
1464 #ifdef DEBUG |
|
1465 /* virtual */ void |
|
1466 StyleRule::List(FILE* out, int32_t aIndent) const |
|
1467 { |
|
1468 // Indent |
|
1469 for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); |
|
1470 |
|
1471 nsAutoString buffer; |
|
1472 if (mSelector) |
|
1473 mSelector->ToString(buffer, GetStyleSheet()); |
|
1474 |
|
1475 buffer.AppendLiteral(" "); |
|
1476 fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out); |
|
1477 if (nullptr != mDeclaration) { |
|
1478 mDeclaration->List(out); |
|
1479 } |
|
1480 else { |
|
1481 fputs("{ null declaration }", out); |
|
1482 } |
|
1483 fputs("\n", out); |
|
1484 } |
|
1485 #endif |
|
1486 |
|
1487 void |
|
1488 StyleRule::GetCssText(nsAString& aCssText) |
|
1489 { |
|
1490 if (mSelector) { |
|
1491 mSelector->ToString(aCssText, GetStyleSheet()); |
|
1492 aCssText.Append(char16_t(' ')); |
|
1493 } |
|
1494 aCssText.Append(char16_t('{')); |
|
1495 aCssText.Append(char16_t(' ')); |
|
1496 if (mDeclaration) |
|
1497 { |
|
1498 nsAutoString tempString; |
|
1499 mDeclaration->ToString( tempString ); |
|
1500 aCssText.Append( tempString ); |
|
1501 } |
|
1502 aCssText.Append(char16_t(' ')); |
|
1503 aCssText.Append(char16_t('}')); |
|
1504 } |
|
1505 |
|
1506 void |
|
1507 StyleRule::SetCssText(const nsAString& aCssText) |
|
1508 { |
|
1509 // XXX TBI - need to re-parse rule & declaration |
|
1510 } |
|
1511 |
|
1512 void |
|
1513 StyleRule::GetSelectorText(nsAString& aSelectorText) |
|
1514 { |
|
1515 if (mSelector) |
|
1516 mSelector->ToString(aSelectorText, GetStyleSheet()); |
|
1517 else |
|
1518 aSelectorText.Truncate(); |
|
1519 } |
|
1520 |
|
1521 void |
|
1522 StyleRule::SetSelectorText(const nsAString& aSelectorText) |
|
1523 { |
|
1524 // XXX TBI - get a parser and re-parse the selectors, |
|
1525 // XXX then need to re-compute the cascade |
|
1526 // XXX and dirty sheet |
|
1527 } |
|
1528 |
|
1529 /* virtual */ size_t |
|
1530 StyleRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
|
1531 { |
|
1532 size_t n = aMallocSizeOf(this); |
|
1533 n += mSelector ? mSelector->SizeOfIncludingThis(aMallocSizeOf) : 0; |
|
1534 n += mDeclaration ? mDeclaration->SizeOfIncludingThis(aMallocSizeOf) : 0; |
|
1535 |
|
1536 // Measurement of the following members may be added later if DMD finds it is |
|
1537 // worthwhile: |
|
1538 // - mImportantRule; |
|
1539 // - mDOMRule; |
|
1540 |
|
1541 return n; |
|
1542 } |
|
1543 |
|
1544 |
|
1545 } // namespace css |
|
1546 } // namespace mozilla |