Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim:cindent:tabstop=2:expandtab:shiftwidth=2:
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 : /* representation of a CSS style sheet */
8 :
9 : #include "mozilla/CSSStyleSheet.h"
10 :
11 : #include "nsIAtom.h"
12 : #include "nsCSSRuleProcessor.h"
13 : #include "mozilla/MemoryReporting.h"
14 : #include "mozilla/dom/Element.h"
15 : #include "mozilla/css/NameSpaceRule.h"
16 : #include "mozilla/css/GroupRule.h"
17 : #include "mozilla/css/ImportRule.h"
18 : #include "nsCSSRules.h"
19 : #include "nsMediaList.h"
20 : #include "nsIDocument.h"
21 : #include "nsPresContext.h"
22 : #include "nsGkAtoms.h"
23 : #include "nsQueryObject.h"
24 : #include "nsString.h"
25 : #include "nsStyleSet.h"
26 : #include "nsTArray.h"
27 : #include "nsIDOMCSSStyleSheet.h"
28 : #include "mozilla/dom/CSSRuleList.h"
29 : #include "nsIDOMMediaList.h"
30 : #include "nsIDOMNode.h"
31 : #include "nsError.h"
32 : #include "nsCSSParser.h"
33 : #include "mozilla/css/Loader.h"
34 : #include "nsNameSpaceManager.h"
35 : #include "nsXMLNameSpaceMap.h"
36 : #include "nsCOMPtr.h"
37 : #include "nsContentUtils.h"
38 : #include "nsIScriptSecurityManager.h"
39 : #include "mozAutoDocUpdate.h"
40 : #include "nsRuleNode.h"
41 : #include "nsMediaFeatures.h"
42 : #include "nsDOMClassInfoID.h"
43 : #include "mozilla/Likely.h"
44 : #include "nsComponentManagerUtils.h"
45 : #include "NullPrincipal.h"
46 : #include "mozilla/RuleProcessorCache.h"
47 : #include "nsIStyleSheetLinkingElement.h"
48 : #include "nsDOMWindowUtils.h"
49 :
50 : using namespace mozilla;
51 : using namespace mozilla::dom;
52 :
53 : // -------------------------------
54 : // Style Rule List for the DOM
55 : //
56 : class CSSRuleListImpl final : public CSSRuleList
57 : {
58 : public:
59 : explicit CSSRuleListImpl(CSSStyleSheet *aStyleSheet);
60 :
61 : virtual CSSStyleSheet* GetParentObject() override;
62 :
63 : virtual css::Rule*
64 : IndexedGetter(uint32_t aIndex, bool& aFound) override;
65 : virtual uint32_t
66 : Length() override;
67 :
68 0 : void DropReference() { mStyleSheet = nullptr; }
69 :
70 : protected:
71 : virtual ~CSSRuleListImpl();
72 :
73 : CSSStyleSheet* mStyleSheet;
74 : };
75 :
76 0 : CSSRuleListImpl::CSSRuleListImpl(CSSStyleSheet *aStyleSheet)
77 : {
78 : // Not reference counted to avoid circular references.
79 : // The style sheet will tell us when its going away.
80 0 : mStyleSheet = aStyleSheet;
81 0 : }
82 :
83 0 : CSSRuleListImpl::~CSSRuleListImpl()
84 : {
85 0 : }
86 :
87 : CSSStyleSheet*
88 0 : CSSRuleListImpl::GetParentObject()
89 : {
90 0 : return mStyleSheet;
91 : }
92 :
93 : uint32_t
94 0 : CSSRuleListImpl::Length()
95 : {
96 0 : if (!mStyleSheet) {
97 0 : return 0;
98 : }
99 :
100 0 : return AssertedCast<uint32_t>(mStyleSheet->StyleRuleCount());
101 : }
102 :
103 : css::Rule*
104 0 : CSSRuleListImpl::IndexedGetter(uint32_t aIndex, bool& aFound)
105 : {
106 0 : aFound = false;
107 :
108 0 : if (mStyleSheet) {
109 : // ensure rules have correct parent
110 0 : mStyleSheet->EnsureUniqueInner();
111 0 : css::Rule* rule = mStyleSheet->GetStyleRuleAt(aIndex);
112 0 : if (rule) {
113 0 : aFound = true;
114 0 : return rule;
115 : }
116 : }
117 :
118 : // Per spec: "Return Value ... null if ... not a valid index."
119 0 : return nullptr;
120 : }
121 :
122 : namespace mozilla {
123 :
124 : // -------------------------------
125 : // CSS Style Sheet Inner Data Container
126 : //
127 :
128 :
129 60 : CSSStyleSheetInner::CSSStyleSheetInner(CORSMode aCORSMode,
130 : ReferrerPolicy aReferrerPolicy,
131 60 : const SRIMetadata& aIntegrity)
132 60 : : StyleSheetInfo(aCORSMode, aReferrerPolicy, aIntegrity)
133 : {
134 60 : MOZ_COUNT_CTOR(CSSStyleSheetInner);
135 60 : }
136 :
137 : bool
138 0 : CSSStyleSheet::RebuildChildList(css::Rule* aRule,
139 : ChildSheetListBuilder* aBuilder)
140 : {
141 0 : int32_t type = aRule->GetType();
142 0 : if (type < css::Rule::IMPORT_RULE) {
143 : // Keep going till we get to the import rules.
144 0 : return true;
145 : }
146 :
147 0 : if (type != css::Rule::IMPORT_RULE) {
148 : // We're past all the import rules; stop the enumeration.
149 0 : return false;
150 : }
151 :
152 : // XXXbz We really need to decomtaminate all this stuff. Is there a reason
153 : // that I can't just QI to ImportRule and get a CSSStyleSheet
154 : // directly from it?
155 0 : nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(aRule));
156 0 : NS_ASSERTION(importRule, "GetType lied");
157 :
158 0 : nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
159 0 : importRule->GetStyleSheet(getter_AddRefs(childSheet));
160 :
161 : // Have to do this QI to be safe, since XPConnect can fake
162 : // nsIDOMCSSStyleSheets
163 0 : RefPtr<CSSStyleSheet> sheet = do_QueryObject(childSheet);
164 0 : if (!sheet) {
165 0 : return true;
166 : }
167 :
168 0 : (*aBuilder->sheetSlot) = sheet;
169 0 : aBuilder->SetParentLinks(*aBuilder->sheetSlot);
170 0 : aBuilder->sheetSlot = &(*aBuilder->sheetSlot)->mNext;
171 0 : return true;
172 : }
173 :
174 : size_t
175 5 : CSSStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
176 : {
177 5 : size_t n = StyleSheet::SizeOfIncludingThis(aMallocSizeOf);
178 5 : const CSSStyleSheet* s = this;
179 15 : while (s) {
180 : // Each inner can be shared by multiple sheets. So we only count the inner
181 : // if this sheet is the last one in the list of those sharing it. As a
182 : // result, the last such sheet takes all the blame for the memory
183 : // consumption of the inner, which isn't ideal but it's better than
184 : // double-counting the inner. We use last instead of first since the first
185 : // sheet may be held in the nsXULPrototypeCache and not used in a window at
186 : // all.
187 5 : if (s->Inner()->mSheets.LastElement() == s) {
188 5 : n += s->Inner()->SizeOfIncludingThis(aMallocSizeOf);
189 : }
190 :
191 : // Measurement of the following members may be added later if DMD finds it
192 : // is worthwhile:
193 : // - s->mRuleCollection
194 : // - s->mRuleProcessors
195 : //
196 : // The following members are not measured:
197 : // - s->mOwnerRule, because it's non-owning
198 : // - s->mScopeElement, because it's non-owning
199 :
200 5 : s = s->mNext ? s->mNext->AsGecko() : nullptr;
201 : }
202 5 : return n;
203 : }
204 :
205 0 : CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheetInner& aCopy,
206 0 : CSSStyleSheet* aPrimarySheet)
207 0 : : StyleSheetInfo(aCopy, aPrimarySheet)
208 : {
209 0 : MOZ_COUNT_CTOR(CSSStyleSheetInner);
210 0 : for (css::Rule* rule : aCopy.mOrderedRules) {
211 0 : RefPtr<css::Rule> clone = rule->Clone();
212 0 : mOrderedRules.AppendObject(clone);
213 0 : clone->SetStyleSheet(aPrimarySheet);
214 : }
215 :
216 0 : StyleSheet::ChildSheetListBuilder builder = { &mFirstChild, aPrimarySheet };
217 0 : for (css::Rule* rule : mOrderedRules) {
218 0 : if (!CSSStyleSheet::RebuildChildList(rule, &builder)) {
219 0 : break;
220 : }
221 : }
222 :
223 0 : RebuildNameSpaces();
224 0 : }
225 :
226 0 : CSSStyleSheetInner::~CSSStyleSheetInner()
227 : {
228 0 : MOZ_COUNT_DTOR(CSSStyleSheetInner);
229 0 : for (css::Rule* rule : mOrderedRules) {
230 0 : rule->SetStyleSheet(nullptr);
231 : }
232 0 : }
233 :
234 : StyleSheetInfo*
235 0 : CSSStyleSheetInner::CloneFor(StyleSheet* aPrimarySheet)
236 : {
237 : return new CSSStyleSheetInner(*this,
238 0 : static_cast<CSSStyleSheet*>(aPrimarySheet));
239 : }
240 :
241 : void
242 0 : CSSStyleSheetInner::RemoveSheet(StyleSheet* aSheet)
243 : {
244 0 : if (aSheet == mSheets.ElementAt(0) && mSheets.Length() > 1) {
245 0 : StyleSheet* sheet = mSheets[1];
246 0 : for (css::Rule* rule : mOrderedRules) {
247 0 : rule->SetStyleSheet(sheet);
248 : }
249 : }
250 :
251 : // Don't do anything after this call, because superclass implementation
252 : // may delete this.
253 0 : StyleSheetInfo::RemoveSheet(aSheet);
254 0 : }
255 :
256 : static void
257 64 : AddNamespaceRuleToMap(css::Rule* aRule, nsXMLNameSpaceMap* aMap)
258 : {
259 64 : NS_ASSERTION(aRule->GetType() == css::Rule::NAMESPACE_RULE, "Bogus rule type");
260 :
261 128 : RefPtr<css::NameSpaceRule> nameSpaceRule = do_QueryObject(aRule);
262 :
263 128 : nsAutoString urlSpec;
264 64 : nameSpaceRule->GetURLSpec(urlSpec);
265 :
266 64 : aMap->AddPrefix(nameSpaceRule->GetPrefix(), urlSpec);
267 64 : }
268 :
269 : void
270 0 : CSSStyleSheetInner::RebuildNameSpaces()
271 : {
272 : // Just nuke our existing namespace map, if any
273 0 : if (NS_SUCCEEDED(CreateNamespaceMap())) {
274 0 : for (css::Rule* rule : mOrderedRules) {
275 0 : switch (rule->GetType()) {
276 : case css::Rule::NAMESPACE_RULE:
277 0 : AddNamespaceRuleToMap(rule, mNameSpaceMap);
278 0 : continue;
279 : case css::Rule::CHARSET_RULE:
280 : case css::Rule::IMPORT_RULE:
281 0 : continue;
282 : }
283 0 : break;
284 : }
285 : }
286 0 : }
287 :
288 : nsresult
289 39 : CSSStyleSheetInner::CreateNamespaceMap()
290 : {
291 39 : mNameSpaceMap = nsXMLNameSpaceMap::Create(false);
292 39 : NS_ENSURE_TRUE(mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY);
293 : // Override the default namespace map behavior for the null prefix to
294 : // return the wildcard namespace instead of the null namespace.
295 39 : mNameSpaceMap->AddPrefix(nullptr, kNameSpaceID_Unknown);
296 39 : return NS_OK;
297 : }
298 :
299 : size_t
300 5 : CSSStyleSheetInner::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
301 : {
302 5 : size_t n = aMallocSizeOf(this);
303 5 : n += mOrderedRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
304 19 : for (size_t i = 0; i < mOrderedRules.Length(); i++) {
305 14 : n += mOrderedRules[i]->SizeOfIncludingThis(aMallocSizeOf);
306 : }
307 5 : n += mFirstChild ? mFirstChild->SizeOfIncludingThis(aMallocSizeOf) : 0;
308 :
309 : // Measurement of the following members may be added later if DMD finds it is
310 : // worthwhile:
311 : // - mSheetURI
312 : // - mOriginalSheetURI
313 : // - mBaseURI
314 : // - mPrincipal
315 : // - mNameSpaceMap
316 : //
317 : // The following members are not measured:
318 : // - mSheets, because it's non-owning
319 :
320 5 : return n;
321 : }
322 :
323 : // -------------------------------
324 : // CSS Style Sheet
325 : //
326 :
327 2 : CSSStyleSheet::CSSStyleSheet(css::SheetParsingMode aParsingMode,
328 2 : CORSMode aCORSMode, ReferrerPolicy aReferrerPolicy)
329 : : StyleSheet(StyleBackendType::Gecko, aParsingMode),
330 : mInRuleProcessorCache(false),
331 : mScopeElement(nullptr),
332 2 : mRuleProcessors(nullptr)
333 : {
334 2 : mInner = new CSSStyleSheetInner(aCORSMode, aReferrerPolicy,
335 6 : SRIMetadata());
336 2 : mInner->AddSheet(this);
337 2 : }
338 :
339 58 : CSSStyleSheet::CSSStyleSheet(css::SheetParsingMode aParsingMode,
340 : CORSMode aCORSMode,
341 : ReferrerPolicy aReferrerPolicy,
342 58 : const SRIMetadata& aIntegrity)
343 : : StyleSheet(StyleBackendType::Gecko, aParsingMode),
344 : mInRuleProcessorCache(false),
345 : mScopeElement(nullptr),
346 58 : mRuleProcessors(nullptr)
347 : {
348 58 : mInner = new CSSStyleSheetInner(aCORSMode, aReferrerPolicy,
349 58 : aIntegrity);
350 58 : mInner->AddSheet(this);
351 58 : }
352 :
353 15 : CSSStyleSheet::CSSStyleSheet(const CSSStyleSheet& aCopy,
354 : CSSStyleSheet* aParentToUse,
355 : dom::CSSImportRule* aOwnerRuleToUse,
356 : nsIDocument* aDocumentToUse,
357 15 : nsINode* aOwningNodeToUse)
358 : : StyleSheet(aCopy,
359 : aParentToUse,
360 : aOwnerRuleToUse,
361 : aDocumentToUse,
362 : aOwningNodeToUse)
363 : , mInRuleProcessorCache(false)
364 : , mScopeElement(nullptr)
365 15 : , mRuleProcessors(nullptr)
366 : {
367 15 : if (mDirty) { // CSSOM's been there, force full copy now
368 0 : NS_ASSERTION(mInner->mComplete,
369 : "Why have rules been accessed on an incomplete sheet?");
370 : // FIXME: handle failure?
371 : //
372 : // NOTE: It's important to call this from the subclass, since it could
373 : // access uninitialized members otherwise.
374 0 : EnsureUniqueInner();
375 : }
376 15 : }
377 :
378 0 : CSSStyleSheet::~CSSStyleSheet()
379 : {
380 0 : }
381 :
382 : void
383 0 : CSSStyleSheet::LastRelease()
384 : {
385 0 : DropRuleCollection();
386 : // XXX The document reference is not reference counted and should
387 : // not be released. The document will let us know when it is going
388 : // away.
389 0 : if (mRuleProcessors) {
390 0 : NS_ASSERTION(mRuleProcessors->Length() == 0, "destructing sheet with rule processor reference");
391 0 : delete mRuleProcessors; // weak refs, should be empty here anyway
392 0 : mRuleProcessors = nullptr;
393 : }
394 0 : if (mInRuleProcessorCache) {
395 0 : RuleProcessorCache::RemoveSheet(this);
396 : }
397 0 : }
398 :
399 : void
400 0 : CSSStyleSheet::DropRuleCollection()
401 : {
402 0 : if (mRuleCollection) {
403 0 : mRuleCollection->DropReference();
404 0 : mRuleCollection = nullptr;
405 : }
406 0 : }
407 :
408 : void
409 0 : CSSStyleSheet::UnlinkInner()
410 : {
411 : // We can only have a cycle through our inner if we have a unique inner,
412 : // because otherwise there are no JS wrappers for anything in the inner.
413 0 : if (mInner->mSheets.Length() != 1) {
414 0 : return;
415 : }
416 :
417 0 : for (css::Rule* rule : Inner()->mOrderedRules) {
418 0 : rule->SetStyleSheet(nullptr);
419 : }
420 0 : Inner()->mOrderedRules.Clear();
421 :
422 0 : StyleSheet::UnlinkInner();
423 : }
424 :
425 : void
426 0 : CSSStyleSheet::TraverseInner(nsCycleCollectionTraversalCallback &cb)
427 : {
428 : // We can only have a cycle through our inner if we have a unique inner,
429 : // because otherwise there are no JS wrappers for anything in the inner.
430 0 : if (mInner->mSheets.Length() != 1) {
431 0 : return;
432 : }
433 :
434 0 : const nsCOMArray<css::Rule>& rules = Inner()->mOrderedRules;
435 0 : for (int32_t i = 0, count = rules.Count(); i < count; ++i) {
436 0 : if (!rules[i]->IsCCLeaf()) {
437 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOrderedRules[i]");
438 0 : cb.NoteXPCOMChild(rules[i]);
439 : }
440 : }
441 :
442 0 : StyleSheet::TraverseInner(cb);
443 : }
444 :
445 : // QueryInterface implementation for CSSStyleSheet
446 75 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CSSStyleSheet)
447 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMCSSStyleSheet)
448 0 : if (aIID.Equals(NS_GET_IID(CSSStyleSheet)))
449 0 : foundInterface = reinterpret_cast<nsISupports*>(this);
450 : else
451 0 : NS_INTERFACE_MAP_END_INHERITING(StyleSheet)
452 :
453 776 : NS_IMPL_ADDREF_INHERITED(CSSStyleSheet, StyleSheet)
454 410 : NS_IMPL_RELEASE_INHERITED(CSSStyleSheet, StyleSheet)
455 :
456 : NS_IMPL_CYCLE_COLLECTION_CLASS(CSSStyleSheet)
457 :
458 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSStyleSheet)
459 : // We do not unlink mNext; our parent will handle that. If we
460 : // unlinked it here, our parent would not be able to walk its list
461 : // of child sheets and null out the back-references to it, if we got
462 : // unlinked before it does.
463 0 : tmp->DropRuleCollection();
464 0 : tmp->mScopeElement = nullptr;
465 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(StyleSheet)
466 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSStyleSheet, StyleSheet)
467 : // We do not traverse mNext; our parent will handle that. See
468 : // comments in Unlink for why.
469 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleCollection)
470 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScopeElement)
471 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
472 :
473 : nsresult
474 144 : CSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor* aProcessor)
475 : {
476 144 : if (! mRuleProcessors) {
477 70 : mRuleProcessors = new AutoTArray<nsCSSRuleProcessor*, 8>();
478 70 : if (!mRuleProcessors)
479 0 : return NS_ERROR_OUT_OF_MEMORY;
480 : }
481 144 : NS_ASSERTION(mRuleProcessors->NoIndex == mRuleProcessors->IndexOf(aProcessor),
482 : "processor already registered");
483 144 : mRuleProcessors->AppendElement(aProcessor); // weak ref
484 144 : return NS_OK;
485 : }
486 :
487 : nsresult
488 31 : CSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor* aProcessor)
489 : {
490 31 : if (!mRuleProcessors)
491 0 : return NS_ERROR_FAILURE;
492 31 : return mRuleProcessors->RemoveElement(aProcessor)
493 31 : ? NS_OK
494 31 : : NS_ERROR_FAILURE;
495 : }
496 :
497 : bool
498 144 : CSSStyleSheet::UseForPresentation(nsPresContext* aPresContext,
499 : nsMediaQueryResultCacheKey& aKey) const
500 : {
501 144 : if (mMedia) {
502 8 : MOZ_ASSERT(aPresContext);
503 8 : auto media = static_cast<nsMediaList*>(mMedia.get());
504 8 : return media->Matches(aPresContext, &aKey);
505 : }
506 136 : return true;
507 : }
508 :
509 :
510 : bool
511 161 : CSSStyleSheet::HasRules() const
512 : {
513 161 : return StyleRuleCount() != 0;
514 : }
515 :
516 : void
517 0 : CSSStyleSheet::EnabledStateChangedInternal()
518 : {
519 0 : ClearRuleCascades();
520 0 : }
521 :
522 : void
523 3074 : CSSStyleSheet::AppendStyleRule(css::Rule* aRule)
524 : {
525 3074 : NS_PRECONDITION(nullptr != aRule, "null arg");
526 :
527 3074 : WillDirty();
528 3074 : Inner()->mOrderedRules.AppendObject(aRule);
529 3074 : aRule->SetStyleSheet(this);
530 3074 : DidDirty();
531 :
532 3074 : if (css::Rule::NAMESPACE_RULE == aRule->GetType()) {
533 : #ifdef DEBUG
534 : nsresult rv =
535 : #endif
536 64 : RegisterNamespaceRule(aRule);
537 64 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
538 : "RegisterNamespaceRule returned error");
539 : }
540 3074 : }
541 :
542 : int32_t
543 221 : CSSStyleSheet::StyleRuleCount() const
544 : {
545 221 : return Inner()->mOrderedRules.Count();
546 : }
547 :
548 : css::Rule*
549 0 : CSSStyleSheet::GetStyleRuleAt(int32_t aIndex) const
550 : {
551 : // Important: If this function is ever made scriptable, we must add
552 : // a security check here. See GetCssRules below for an example.
553 0 : return Inner()->mOrderedRules.SafeObjectAt(aIndex);
554 : }
555 :
556 : already_AddRefed<StyleSheet>
557 15 : CSSStyleSheet::Clone(StyleSheet* aCloneParent,
558 : dom::CSSImportRule* aCloneOwnerRule,
559 : nsIDocument* aCloneDocument,
560 : nsINode* aCloneOwningNode) const
561 : {
562 : RefPtr<StyleSheet> clone = new CSSStyleSheet(*this,
563 : static_cast<CSSStyleSheet*>(aCloneParent),
564 : aCloneOwnerRule,
565 : aCloneDocument,
566 30 : aCloneOwningNode);
567 30 : return clone.forget();
568 : }
569 :
570 : #ifdef DEBUG
571 : static void
572 0 : ListRules(const nsCOMArray<css::Rule>& aRules, FILE* aOut, int32_t aIndent)
573 : {
574 0 : for (int32_t index = aRules.Count() - 1; index >= 0; --index) {
575 0 : aRules.ObjectAt(index)->List(aOut, aIndent);
576 : }
577 0 : }
578 :
579 : void
580 0 : CSSStyleSheet::List(FILE* out, int32_t aIndent) const
581 : {
582 0 : StyleSheet::List(out, aIndent);
583 :
584 0 : fprintf_stderr(out, "%s", "Rules in source order:\n");
585 0 : ListRules(Inner()->mOrderedRules, out, aIndent);
586 0 : }
587 : #endif
588 :
589 : void
590 3152 : CSSStyleSheet::ClearRuleCascades()
591 : {
592 : // We might be in ClearRuleCascadesInternal because we had a modification
593 : // to the sheet that resulted in an nsCSSSelector being destroyed.
594 : // Tell the RestyleManager for each document we're used in
595 : // so that they can drop any nsCSSSelector pointers (used for
596 : // eRestyle_SomeDescendants) in their mPendingRestyles.
597 3152 : for (StyleSetHandle setHandle : mStyleSets) {
598 0 : setHandle->AsGecko()->ClearSelectors();
599 : }
600 :
601 3152 : bool removedSheetFromRuleProcessorCache = false;
602 3152 : if (mRuleProcessors) {
603 0 : nsCSSRuleProcessor **iter = mRuleProcessors->Elements(),
604 0 : **end = iter + mRuleProcessors->Length();
605 0 : for(; iter != end; ++iter) {
606 0 : if (!removedSheetFromRuleProcessorCache && (*iter)->IsShared()) {
607 : // Since the sheet has been modified, we need to remove all
608 : // RuleProcessorCache entries that contain this sheet, as the
609 : // list of @-moz-document rules might have changed.
610 0 : RuleProcessorCache::RemoveSheet(this);
611 0 : removedSheetFromRuleProcessorCache = true;
612 : }
613 0 : (*iter)->ClearRuleCascades();
614 : }
615 : }
616 :
617 3152 : if (mParent) {
618 73 : static_cast<CSSStyleSheet*>(mParent)->ClearRuleCascades();
619 : }
620 3152 : }
621 :
622 : void
623 3079 : CSSStyleSheet::DidDirty()
624 : {
625 3079 : MOZ_ASSERT(!mInner->mComplete || mDirty,
626 : "caller must have called WillDirty()");
627 3079 : ClearRuleCascades();
628 3079 : }
629 :
630 : nsresult
631 64 : CSSStyleSheet::RegisterNamespaceRule(css::Rule* aRule)
632 : {
633 64 : if (!Inner()->mNameSpaceMap) {
634 39 : nsresult rv = Inner()->CreateNamespaceMap();
635 39 : NS_ENSURE_SUCCESS(rv, rv);
636 : }
637 :
638 64 : AddNamespaceRuleToMap(aRule, Inner()->mNameSpaceMap);
639 64 : return NS_OK;
640 : }
641 :
642 : void
643 73 : CSSStyleSheet::SetScopeElement(dom::Element* aScopeElement)
644 : {
645 73 : mScopeElement = aScopeElement;
646 73 : }
647 :
648 : CSSRuleList*
649 0 : CSSStyleSheet::GetCssRulesInternal()
650 : {
651 0 : if (!mRuleCollection) {
652 0 : mRuleCollection = new CSSRuleListImpl(this);
653 : }
654 0 : return mRuleCollection;
655 : }
656 :
657 : uint32_t
658 0 : CSSStyleSheet::InsertRuleInternal(const nsAString& aRule,
659 : uint32_t aIndex,
660 : ErrorResult& aRv)
661 : {
662 0 : MOZ_ASSERT(mInner->mComplete);
663 :
664 0 : WillDirty();
665 :
666 0 : if (aIndex > uint32_t(Inner()->mOrderedRules.Count())) {
667 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
668 0 : return 0;
669 : }
670 :
671 0 : NS_ASSERTION(uint32_t(Inner()->mOrderedRules.Count()) <= INT32_MAX,
672 : "Too many style rules!");
673 :
674 : // Hold strong ref to the CSSLoader in case the document update
675 : // kills the document
676 0 : RefPtr<css::Loader> loader;
677 0 : if (mDocument) {
678 0 : loader = mDocument->CSSLoader();
679 0 : NS_ASSERTION(loader, "Document with no CSS loader!");
680 : }
681 :
682 0 : nsCSSParser css(loader, this);
683 :
684 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
685 :
686 0 : RefPtr<css::Rule> rule;
687 0 : aRv = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
688 0 : mInner->mPrincipal, getter_AddRefs(rule));
689 0 : if (NS_WARN_IF(aRv.Failed())) {
690 0 : return 0;
691 : }
692 :
693 : // Hierarchy checking.
694 0 : int32_t newType = rule->GetType();
695 :
696 : // check that we're not inserting before a charset rule
697 0 : css::Rule* nextRule = Inner()->mOrderedRules.SafeObjectAt(aIndex);
698 0 : if (nextRule) {
699 0 : int32_t nextType = nextRule->GetType();
700 0 : if (nextType == css::Rule::CHARSET_RULE) {
701 0 : aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
702 0 : return 0;
703 : }
704 :
705 0 : if (nextType == css::Rule::IMPORT_RULE &&
706 0 : newType != css::Rule::CHARSET_RULE &&
707 : newType != css::Rule::IMPORT_RULE) {
708 0 : aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
709 0 : return 0;
710 : }
711 :
712 0 : if (nextType == css::Rule::NAMESPACE_RULE &&
713 0 : newType != css::Rule::CHARSET_RULE &&
714 0 : newType != css::Rule::IMPORT_RULE &&
715 : newType != css::Rule::NAMESPACE_RULE) {
716 0 : aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
717 0 : return 0;
718 : }
719 : }
720 :
721 0 : if (aIndex != 0) {
722 : // no inserting charset at nonzero position
723 0 : if (newType == css::Rule::CHARSET_RULE) {
724 0 : aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
725 0 : return 0;
726 : }
727 :
728 0 : css::Rule* prevRule = Inner()->mOrderedRules.SafeObjectAt(aIndex - 1);
729 0 : int32_t prevType = prevRule->GetType();
730 :
731 0 : if (newType == css::Rule::IMPORT_RULE &&
732 0 : prevType != css::Rule::CHARSET_RULE &&
733 : prevType != css::Rule::IMPORT_RULE) {
734 0 : aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
735 0 : return 0;
736 : }
737 :
738 0 : if (newType == css::Rule::NAMESPACE_RULE &&
739 0 : prevType != css::Rule::CHARSET_RULE &&
740 0 : prevType != css::Rule::IMPORT_RULE &&
741 : prevType != css::Rule::NAMESPACE_RULE) {
742 0 : aRv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
743 0 : return 0;
744 : }
745 : }
746 :
747 0 : if (!Inner()->mOrderedRules.InsertObjectAt(rule, aIndex)) {
748 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
749 0 : return 0;
750 : }
751 :
752 0 : DidDirty();
753 :
754 0 : rule->SetStyleSheet(this);
755 :
756 0 : int32_t type = rule->GetType();
757 0 : if (type == css::Rule::NAMESPACE_RULE) {
758 : // XXXbz does this screw up when inserting a namespace rule before
759 : // another namespace rule that binds the same prefix to a different
760 : // namespace?
761 0 : aRv = RegisterNamespaceRule(rule);
762 0 : if (NS_WARN_IF(aRv.Failed())) {
763 0 : return 0;
764 : }
765 : }
766 :
767 : // We don't notify immediately for @import rules, but rather when
768 : // the sheet the rule is importing is loaded (see StyleSheetLoaded)
769 0 : if ((type != css::Rule::IMPORT_RULE || !RuleHasPendingChildSheet(rule)) &&
770 0 : mDocument) {
771 0 : mDocument->StyleRuleAdded(this, rule);
772 : }
773 :
774 0 : return aIndex;
775 : }
776 :
777 : void
778 0 : CSSStyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv)
779 : {
780 : // XXX TBI: handle @rule types
781 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
782 :
783 0 : WillDirty();
784 :
785 0 : if (aIndex >= uint32_t(Inner()->mOrderedRules.Count())) {
786 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
787 0 : return;
788 : }
789 :
790 0 : NS_ASSERTION(uint32_t(Inner()->mOrderedRules.Count()) <= INT32_MAX,
791 : "Too many style rules!");
792 :
793 : // Hold a strong ref to the rule so it doesn't die when we RemoveObjectAt
794 0 : RefPtr<css::Rule> rule = Inner()->mOrderedRules.ObjectAt(aIndex);
795 0 : if (rule) {
796 0 : Inner()->mOrderedRules.RemoveObjectAt(aIndex);
797 0 : rule->SetStyleSheet(nullptr);
798 0 : DidDirty();
799 :
800 0 : if (mDocument) {
801 0 : mDocument->StyleRuleRemoved(this, rule);
802 : }
803 : }
804 : }
805 :
806 : nsresult
807 0 : CSSStyleSheet::InsertRuleIntoGroupInternal(const nsAString& aRule,
808 : css::GroupRule* aGroup,
809 : uint32_t aIndex)
810 : {
811 : // Hold strong ref to the CSSLoader in case the document update
812 : // kills the document
813 0 : RefPtr<css::Loader> loader;
814 0 : if (mDocument) {
815 0 : loader = mDocument->CSSLoader();
816 0 : NS_ASSERTION(loader, "Document with no CSS loader!");
817 : }
818 :
819 0 : nsCSSParser css(loader, this);
820 :
821 0 : RefPtr<css::Rule> rule;
822 0 : nsresult result = css.ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
823 0 : mInner->mPrincipal, getter_AddRefs(rule));
824 0 : if (NS_FAILED(result))
825 0 : return result;
826 :
827 0 : switch (rule->GetType()) {
828 : case css::Rule::STYLE_RULE:
829 : case css::Rule::MEDIA_RULE:
830 : case css::Rule::FONT_FACE_RULE:
831 : case css::Rule::PAGE_RULE:
832 : case css::Rule::KEYFRAMES_RULE:
833 : case css::Rule::COUNTER_STYLE_RULE:
834 : case css::Rule::DOCUMENT_RULE:
835 : case css::Rule::SUPPORTS_RULE:
836 : // these types are OK to insert into a group
837 0 : break;
838 : case css::Rule::CHARSET_RULE:
839 : case css::Rule::IMPORT_RULE:
840 : case css::Rule::NAMESPACE_RULE:
841 : // these aren't
842 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
843 : default:
844 0 : NS_NOTREACHED("unexpected rule type");
845 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
846 : }
847 :
848 0 : return aGroup->InsertStyleRuleAt(aIndex, rule);
849 : }
850 :
851 : // nsICSSLoaderObserver implementation
852 : NS_IMETHODIMP
853 0 : CSSStyleSheet::StyleSheetLoaded(StyleSheet* aSheet,
854 : bool aWasAlternate,
855 : nsresult aStatus)
856 : {
857 0 : MOZ_ASSERT(aSheet->IsGecko(),
858 : "why we were called back with a ServoStyleSheet?");
859 :
860 0 : CSSStyleSheet* sheet = aSheet->AsGecko();
861 :
862 0 : if (sheet->GetParentSheet() == nullptr) {
863 0 : return NS_OK; // ignore if sheet has been detached already (see parseSheet)
864 : }
865 0 : NS_ASSERTION(this == sheet->GetParentSheet(),
866 : "We are being notified of a sheet load for a sheet that is not our child!");
867 :
868 0 : if (mDocument && NS_SUCCEEDED(aStatus)) {
869 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
870 :
871 : // XXXldb @import rules shouldn't even implement nsIStyleRule (but
872 : // they do)!
873 0 : mDocument->StyleRuleAdded(this, sheet->GetOwnerRule());
874 : }
875 :
876 0 : return NS_OK;
877 : }
878 :
879 : nsresult
880 2 : CSSStyleSheet::ReparseSheet(const nsAString& aInput)
881 : {
882 : // Not doing this if the sheet is not complete!
883 2 : if (!mInner->mComplete) {
884 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
885 : }
886 :
887 : // Hold strong ref to the CSSLoader in case the document update
888 : // kills the document
889 4 : RefPtr<css::Loader> loader;
890 2 : if (mDocument) {
891 0 : loader = mDocument->CSSLoader();
892 0 : NS_ASSERTION(loader, "Document with no CSS loader!");
893 : } else {
894 2 : loader = new css::Loader(StyleBackendType::Gecko, nullptr);
895 : }
896 :
897 4 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
898 :
899 2 : WillDirty();
900 :
901 : // detach existing rules (including child sheets via import rules)
902 4 : css::LoaderReusableStyleSheets reusableSheets;
903 : int ruleCount;
904 2 : while ((ruleCount = Inner()->mOrderedRules.Count()) != 0) {
905 0 : RefPtr<css::Rule> rule = Inner()->mOrderedRules.ObjectAt(ruleCount - 1);
906 0 : Inner()->mOrderedRules.RemoveObjectAt(ruleCount - 1);
907 0 : rule->SetStyleSheet(nullptr);
908 0 : if (rule->GetType() == css::Rule::IMPORT_RULE) {
909 0 : nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(rule));
910 0 : NS_ASSERTION(importRule, "GetType lied");
911 :
912 0 : nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
913 0 : importRule->GetStyleSheet(getter_AddRefs(childSheet));
914 :
915 0 : RefPtr<CSSStyleSheet> cssSheet = do_QueryObject(childSheet);
916 0 : if (cssSheet && cssSheet->GetOriginalURI()) {
917 0 : reusableSheets.AddReusableSheet(cssSheet);
918 : }
919 : }
920 0 : if (mDocument) {
921 0 : mDocument->StyleRuleRemoved(this, rule);
922 : }
923 : }
924 :
925 : // nuke child sheets list and current namespace map
926 2 : for (StyleSheet* child = GetFirstChild(); child; ) {
927 0 : NS_ASSERTION(child->mParent == this, "Child sheet is not parented to this!");
928 0 : StyleSheet* next = child->mNext;
929 0 : child->mParent = nullptr;
930 0 : child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
931 0 : child->mNext = nullptr;
932 0 : child = next;
933 : }
934 2 : SheetInfo().mFirstChild = nullptr;
935 2 : Inner()->mNameSpaceMap = nullptr;
936 :
937 2 : uint32_t lineNumber = 1;
938 2 : if (mOwningNode) {
939 0 : nsCOMPtr<nsIStyleSheetLinkingElement> link = do_QueryInterface(mOwningNode);
940 0 : if (link) {
941 0 : lineNumber = link->GetLineNumber();
942 : }
943 : }
944 :
945 4 : nsCSSParser parser(loader, this);
946 2 : nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI,
947 4 : mInner->mPrincipal, lineNumber, &reusableSheets);
948 2 : DidDirty(); // we are always 'dirty' here since we always remove rules first
949 2 : NS_ENSURE_SUCCESS(rv, rv);
950 :
951 : // notify document of all new rules
952 2 : if (mDocument) {
953 0 : for (int32_t index = 0; index < Inner()->mOrderedRules.Count(); ++index) {
954 0 : RefPtr<css::Rule> rule = Inner()->mOrderedRules.ObjectAt(index);
955 0 : if (rule->GetType() == css::Rule::IMPORT_RULE &&
956 0 : RuleHasPendingChildSheet(rule)) {
957 0 : continue; // notify when loaded (see StyleSheetLoaded)
958 : }
959 0 : mDocument->StyleRuleAdded(this, rule);
960 : }
961 : }
962 2 : return NS_OK;
963 : }
964 :
965 : } // namespace mozilla
|