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 : #include "mozilla/StyleSheet.h"
8 :
9 : #include "mozilla/dom/CSSImportRule.h"
10 : #include "mozilla/dom/CSSRuleList.h"
11 : #include "mozilla/dom/MediaList.h"
12 : #include "mozilla/dom/ShadowRoot.h"
13 : #include "mozilla/ServoCSSRuleList.h"
14 : #include "mozilla/ServoStyleSheet.h"
15 : #include "mozilla/StyleSheetInlines.h"
16 : #include "mozilla/CSSStyleSheet.h"
17 :
18 : #include "mozAutoDocUpdate.h"
19 : #include "NullPrincipal.h"
20 :
21 : namespace mozilla {
22 :
23 60 : StyleSheet::StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMode)
24 : : mParent(nullptr)
25 : , mDocument(nullptr)
26 : , mOwningNode(nullptr)
27 : , mOwnerRule(nullptr)
28 : , mParsingMode(aParsingMode)
29 : , mType(aType)
30 : , mDisabled(false)
31 : , mDirty(false)
32 : , mDocumentAssociationMode(NotOwnedByDocument)
33 60 : , mInner(nullptr)
34 : {
35 60 : }
36 :
37 15 : StyleSheet::StyleSheet(const StyleSheet& aCopy,
38 : StyleSheet* aParentToUse,
39 : dom::CSSImportRule* aOwnerRuleToUse,
40 : nsIDocument* aDocumentToUse,
41 15 : nsINode* aOwningNodeToUse)
42 : : mParent(aParentToUse)
43 : , mTitle(aCopy.mTitle)
44 : , mDocument(aDocumentToUse)
45 : , mOwningNode(aOwningNodeToUse)
46 : , mOwnerRule(aOwnerRuleToUse)
47 15 : , mParsingMode(aCopy.mParsingMode)
48 15 : , mType(aCopy.mType)
49 15 : , mDisabled(aCopy.mDisabled)
50 15 : , mDirty(aCopy.mDirty)
51 : // We only use this constructor during cloning. It's the cloner's
52 : // responsibility to notify us if we end up being owned by a document.
53 : , mDocumentAssociationMode(NotOwnedByDocument)
54 75 : , mInner(aCopy.mInner) // Shallow copy, but concrete subclasses will fix up.
55 : {
56 15 : MOZ_ASSERT(mInner, "Should only copy StyleSheets with an mInner.");
57 15 : mInner->AddSheet(this);
58 :
59 15 : if (aCopy.mMedia) {
60 : // XXX This is wrong; we should be keeping @import rules and
61 : // sheets in sync!
62 0 : mMedia = aCopy.mMedia->Clone();
63 : }
64 15 : }
65 :
66 0 : StyleSheet::~StyleSheet()
67 : {
68 0 : MOZ_ASSERT(!mInner, "Inner should have been dropped in LastRelease");
69 0 : }
70 :
71 : void
72 0 : StyleSheet::LastRelease()
73 : {
74 0 : MOZ_ASSERT(mInner, "Should have an mInner at time of destruction.");
75 0 : MOZ_ASSERT(mInner->mSheets.Contains(this), "Our mInner should include us.");
76 :
77 0 : UnparentChildren();
78 0 : if (IsGecko()) {
79 0 : AsGecko()->LastRelease();
80 : } else {
81 0 : AsServo()->LastRelease();
82 : }
83 :
84 0 : mInner->RemoveSheet(this);
85 0 : mInner = nullptr;
86 :
87 0 : DropMedia();
88 0 : }
89 :
90 : void
91 0 : StyleSheet::UnlinkInner()
92 : {
93 : // We can only have a cycle through our inner if we have a unique inner,
94 : // because otherwise there are no JS wrappers for anything in the inner.
95 0 : if (mInner->mSheets.Length() != 1) {
96 0 : return;
97 : }
98 :
99 : // Have to be a bit careful with child sheets, because we want to
100 : // drop their mNext pointers and null out their mParent and
101 : // mDocument, but don't want to work with deleted objects. And we
102 : // don't want to do any addrefing in the process, just to make sure
103 : // we don't confuse the cycle collector (though on the face of it,
104 : // addref/release pairs during unlink should probably be ok).
105 0 : RefPtr<StyleSheet> child;
106 0 : child.swap(SheetInfo().mFirstChild);
107 0 : while (child) {
108 0 : MOZ_ASSERT(child->mParent == this, "We have a unique inner!");
109 0 : child->mParent = nullptr;
110 : // We (and child) might still think we're owned by a document, because
111 : // unlink order is non-deterministic, so the document's unlink, which would
112 : // tell us it does't own us anymore, may not have happened yet. But if
113 : // we're being unlinked, clearly we're not owned by a document anymore
114 : // conceptually!
115 0 : child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
116 :
117 0 : RefPtr<StyleSheet> next;
118 : // Null out child->mNext, but don't let it die yet
119 0 : next.swap(child->mNext);
120 : // Switch to looking at the old value of child->mNext next iteration
121 0 : child.swap(next);
122 : // "next" is now our previous value of child; it'll get released
123 : // as we loop around.
124 : }
125 : }
126 :
127 : void
128 0 : StyleSheet::TraverseInner(nsCycleCollectionTraversalCallback &cb)
129 : {
130 : // We can only have a cycle through our inner if we have a unique inner,
131 : // because otherwise there are no JS wrappers for anything in the inner.
132 0 : if (mInner->mSheets.Length() != 1) {
133 0 : return;
134 : }
135 :
136 0 : StyleSheet* childSheet = GetFirstChild();
137 0 : while (childSheet) {
138 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
139 0 : cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, childSheet));
140 0 : childSheet = childSheet->mNext;
141 : }
142 : }
143 :
144 : // QueryInterface implementation for StyleSheet
145 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheet)
146 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
147 0 : NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
148 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet)
149 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet)
150 0 : NS_INTERFACE_MAP_END
151 :
152 776 : NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheet)
153 : // We want to disconnect from our inner as soon as our refcount drops to zero,
154 : // without waiting for async deletion by the cycle collector. Otherwise we
155 : // might end up cloning the inner if someone mutates another sheet that shares
156 : // it with us, even though there is only one such sheet and we're about to go
157 : // away. This situation arises easily with sheet preloading.
158 410 : NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(StyleSheet, LastRelease())
159 :
160 : NS_IMPL_CYCLE_COLLECTION_CLASS(StyleSheet)
161 :
162 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StyleSheet)
163 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia)
164 0 : tmp->TraverseInner(cb);
165 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
166 :
167 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StyleSheet)
168 0 : tmp->DropMedia();
169 0 : tmp->UnlinkInner();
170 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
171 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
172 :
173 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(StyleSheet)
174 :
175 : mozilla::dom::CSSStyleSheetParsingMode
176 0 : StyleSheet::ParsingModeDOM()
177 : {
178 : #define CHECK(X, Y) \
179 : static_assert(static_cast<int>(X) == static_cast<int>(Y), \
180 : "mozilla::dom::CSSStyleSheetParsingMode and mozilla::css::SheetParsingMode should have identical values");
181 :
182 : CHECK(mozilla::dom::CSSStyleSheetParsingMode::Agent, css::eAgentSheetFeatures);
183 : CHECK(mozilla::dom::CSSStyleSheetParsingMode::User, css::eUserSheetFeatures);
184 : CHECK(mozilla::dom::CSSStyleSheetParsingMode::Author, css::eAuthorSheetFeatures);
185 :
186 : #undef CHECK
187 :
188 0 : return static_cast<mozilla::dom::CSSStyleSheetParsingMode>(mParsingMode);
189 : }
190 :
191 : bool
192 161 : StyleSheet::IsComplete() const
193 : {
194 161 : return SheetInfo().mComplete;
195 : }
196 :
197 : void
198 60 : StyleSheet::SetComplete()
199 : {
200 60 : NS_ASSERTION(!mDirty, "Can't set a dirty sheet complete!");
201 60 : SheetInfo().mComplete = true;
202 60 : if (mDocument && !mDisabled) {
203 : // Let the document know
204 13 : mDocument->BeginUpdate(UPDATE_STYLE);
205 13 : mDocument->SetStyleSheetApplicableState(this, true);
206 13 : mDocument->EndUpdate(UPDATE_STYLE);
207 : }
208 :
209 142 : if (mOwningNode && !mDisabled &&
210 71 : mOwningNode->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
211 0 : mOwningNode->IsContent()) {
212 0 : dom::ShadowRoot* shadowRoot = mOwningNode->AsContent()->GetContainingShadow();
213 0 : shadowRoot->StyleSheetChanged();
214 : }
215 60 : }
216 :
217 : void
218 76 : StyleSheet::SetEnabled(bool aEnabled)
219 : {
220 : // Internal method, so callers must handle BeginUpdate/EndUpdate
221 76 : bool oldDisabled = mDisabled;
222 76 : mDisabled = !aEnabled;
223 :
224 76 : if (IsComplete() && oldDisabled != mDisabled) {
225 0 : EnabledStateChanged();
226 :
227 0 : if (mDocument) {
228 0 : mDocument->SetStyleSheetApplicableState(this, !mDisabled);
229 : }
230 : }
231 76 : }
232 :
233 60 : StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
234 : ReferrerPolicy aReferrerPolicy,
235 60 : const dom::SRIMetadata& aIntegrity)
236 120 : : mPrincipal(NullPrincipal::Create())
237 : , mCORSMode(aCORSMode)
238 : , mReferrerPolicy(aReferrerPolicy)
239 : , mIntegrity(aIntegrity)
240 : , mComplete(false)
241 : #ifdef DEBUG
242 180 : , mPrincipalSet(false)
243 : #endif
244 : {
245 60 : if (!mPrincipal) {
246 0 : NS_RUNTIMEABORT("NullPrincipal::Init failed");
247 : }
248 60 : }
249 :
250 0 : StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy,
251 0 : StyleSheet* aPrimarySheet)
252 : : mSheetURI(aCopy.mSheetURI)
253 : , mOriginalSheetURI(aCopy.mOriginalSheetURI)
254 : , mBaseURI(aCopy.mBaseURI)
255 : , mPrincipal(aCopy.mPrincipal)
256 0 : , mCORSMode(aCopy.mCORSMode)
257 0 : , mReferrerPolicy(aCopy.mReferrerPolicy)
258 : , mIntegrity(aCopy.mIntegrity)
259 0 : , mComplete(aCopy.mComplete)
260 : , mFirstChild() // We don't rebuild the child because we're making a copy
261 : // without children.
262 : , mSourceMapURL(aCopy.mSourceMapURL)
263 : #ifdef DEBUG
264 0 : , mPrincipalSet(aCopy.mPrincipalSet)
265 : #endif
266 : {
267 0 : AddSheet(aPrimarySheet);
268 0 : }
269 :
270 0 : StyleSheetInfo::~StyleSheetInfo()
271 : {
272 0 : }
273 :
274 : void
275 75 : StyleSheetInfo::AddSheet(StyleSheet* aSheet)
276 : {
277 75 : mSheets.AppendElement(aSheet);
278 75 : }
279 :
280 : void
281 0 : StyleSheetInfo::RemoveSheet(StyleSheet* aSheet)
282 : {
283 0 : if ((aSheet == mSheets.ElementAt(0)) && (mSheets.Length() > 1)) {
284 0 : StyleSheet::ChildSheetListBuilder::ReparentChildList(mSheets[1], mFirstChild);
285 : }
286 :
287 0 : if (1 == mSheets.Length()) {
288 0 : NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent");
289 0 : delete this;
290 0 : return;
291 : }
292 :
293 0 : mSheets.RemoveElement(aSheet);
294 : }
295 :
296 : void
297 0 : StyleSheet::ChildSheetListBuilder::SetParentLinks(StyleSheet* aSheet)
298 : {
299 0 : aSheet->mParent = parent;
300 0 : aSheet->SetAssociatedDocument(parent->mDocument,
301 0 : parent->mDocumentAssociationMode);
302 0 : }
303 :
304 : void
305 0 : StyleSheet::ChildSheetListBuilder::ReparentChildList(StyleSheet* aPrimarySheet,
306 : StyleSheet* aFirstChild)
307 : {
308 0 : for (StyleSheet *child = aFirstChild; child; child = child->mNext) {
309 0 : child->mParent = aPrimarySheet;
310 0 : child->SetAssociatedDocument(aPrimarySheet->mDocument,
311 0 : aPrimarySheet->mDocumentAssociationMode);
312 : }
313 0 : }
314 :
315 : // nsIDOMStyleSheet interface
316 :
317 : NS_IMETHODIMP
318 0 : StyleSheet::GetType(nsAString& aType)
319 : {
320 0 : aType.AssignLiteral("text/css");
321 0 : return NS_OK;
322 : }
323 :
324 : NS_IMETHODIMP
325 0 : StyleSheet::GetDisabled(bool* aDisabled)
326 : {
327 0 : *aDisabled = Disabled();
328 0 : return NS_OK;
329 : }
330 :
331 : NS_IMETHODIMP
332 0 : StyleSheet::SetDisabled(bool aDisabled)
333 : {
334 : // DOM method, so handle BeginUpdate/EndUpdate
335 0 : MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, true);
336 0 : SetEnabled(!aDisabled);
337 0 : return NS_OK;
338 : }
339 :
340 : NS_IMETHODIMP
341 0 : StyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode)
342 : {
343 0 : nsCOMPtr<nsIDOMNode> ownerNode = do_QueryInterface(GetOwnerNode());
344 0 : ownerNode.forget(aOwnerNode);
345 0 : return NS_OK;
346 : }
347 :
348 : NS_IMETHODIMP
349 0 : StyleSheet::GetHref(nsAString& aHref)
350 : {
351 0 : if (nsIURI* sheetURI = SheetInfo().mOriginalSheetURI) {
352 0 : nsAutoCString str;
353 0 : nsresult rv = sheetURI->GetSpec(str);
354 0 : NS_ENSURE_SUCCESS(rv, rv);
355 0 : CopyUTF8toUTF16(str, aHref);
356 : } else {
357 0 : SetDOMStringToNull(aHref);
358 : }
359 0 : return NS_OK;
360 : }
361 :
362 : NS_IMETHODIMP
363 0 : StyleSheet::GetTitle(nsAString& aTitle)
364 : {
365 0 : aTitle.Assign(mTitle);
366 0 : return NS_OK;
367 : }
368 :
369 : // nsIDOMStyleSheet interface
370 :
371 : NS_IMETHODIMP
372 0 : StyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet)
373 : {
374 0 : NS_ENSURE_ARG_POINTER(aParentStyleSheet);
375 0 : NS_IF_ADDREF(*aParentStyleSheet = GetParentStyleSheet());
376 0 : return NS_OK;
377 : }
378 :
379 : NS_IMETHODIMP
380 0 : StyleSheet::GetMedia(nsIDOMMediaList** aMedia)
381 : {
382 0 : NS_ADDREF(*aMedia = Media());
383 0 : return NS_OK;
384 : }
385 :
386 : NS_IMETHODIMP
387 0 : StyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule)
388 : {
389 0 : NS_IF_ADDREF(*aOwnerRule = GetDOMOwnerRule());
390 0 : return NS_OK;
391 : }
392 :
393 : NS_IMETHODIMP
394 0 : StyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules)
395 : {
396 0 : ErrorResult rv;
397 : nsCOMPtr<nsIDOMCSSRuleList> rules =
398 0 : GetCssRules(*nsContentUtils::SubjectPrincipal(), rv);
399 0 : rules.forget(aCssRules);
400 0 : return rv.StealNSResult();
401 : }
402 :
403 : NS_IMETHODIMP
404 0 : StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
405 : uint32_t* aReturn)
406 : {
407 0 : ErrorResult rv;
408 0 : *aReturn =
409 0 : InsertRule(aRule, aIndex, *nsContentUtils::SubjectPrincipal(), rv);
410 0 : return rv.StealNSResult();
411 : }
412 :
413 : NS_IMETHODIMP
414 0 : StyleSheet::DeleteRule(uint32_t aIndex)
415 : {
416 0 : ErrorResult rv;
417 0 : DeleteRule(aIndex, *nsContentUtils::SubjectPrincipal(), rv);
418 0 : return rv.StealNSResult();
419 : }
420 :
421 : void
422 3079 : StyleSheet::WillDirty()
423 : {
424 3079 : if (mInner->mComplete) {
425 14 : EnsureUniqueInner();
426 : }
427 3079 : }
428 :
429 : void
430 121 : StyleSheet::AddStyleSet(const StyleSetHandle& aStyleSet)
431 : {
432 121 : NS_ASSERTION(!mStyleSets.Contains(aStyleSet),
433 : "style set already registered");
434 121 : mStyleSets.AppendElement(aStyleSet);
435 121 : }
436 :
437 : void
438 44 : StyleSheet::DropStyleSet(const StyleSetHandle& aStyleSet)
439 : {
440 88 : DebugOnly<bool> found = mStyleSets.RemoveElement(aStyleSet);
441 44 : NS_ASSERTION(found, "didn't find style set");
442 44 : }
443 :
444 : void
445 14 : StyleSheet::EnsureUniqueInner()
446 : {
447 14 : MOZ_ASSERT(mInner->mSheets.Length() != 0,
448 : "unexpected number of outers");
449 14 : mDirty = true;
450 :
451 14 : if (mInner->mSheets.Length() == 1) {
452 : // already unique
453 14 : return;
454 : }
455 :
456 0 : StyleSheetInfo* clone = mInner->CloneFor(this);
457 0 : MOZ_ASSERT(clone);
458 0 : mInner->RemoveSheet(this);
459 0 : mInner = clone;
460 :
461 0 : if (CSSStyleSheet* geckoSheet = GetAsGecko()) {
462 : // Ensure we're using the new rules.
463 : //
464 : // NOTE: In Servo, all kind of changes that change the set of selectors or
465 : // rules we match are covered by the PresShell notifications. In Gecko
466 : // that's true too, but this is probably needed because selectors are not
467 : // refcounted and can become stale.
468 0 : geckoSheet->ClearRuleCascades();
469 : } else {
470 : // Fixup the child lists and parent links in the Servo sheet. This is done
471 : // here instead of in StyleSheetInner::CloneFor, because it's just more
472 : // convenient to do so instead.
473 0 : AsServo()->BuildChildListAfterInnerClone();
474 : }
475 :
476 : // let our containing style sets know that if we call
477 : // nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
478 : // document
479 0 : for (StyleSetHandle& setHandle : mStyleSets) {
480 0 : setHandle->SetNeedsRestyleAfterEnsureUniqueInner();
481 : }
482 : }
483 :
484 : void
485 0 : StyleSheet::AppendAllChildSheets(nsTArray<StyleSheet*>& aArray)
486 : {
487 0 : for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) {
488 0 : aArray.AppendElement(child);
489 : }
490 0 : }
491 :
492 : // WebIDL CSSStyleSheet API
493 :
494 : #define FORWARD_INTERNAL(method_, args_) \
495 : if (IsServo()) { \
496 : return AsServo()->method_ args_; \
497 : } \
498 : return AsGecko()->method_ args_;
499 :
500 : dom::CSSRuleList*
501 0 : StyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal,
502 : ErrorResult& aRv)
503 : {
504 0 : if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
505 0 : return nullptr;
506 : }
507 0 : FORWARD_INTERNAL(GetCssRulesInternal, ())
508 : }
509 :
510 : void
511 0 : StyleSheet::GetSourceMapURL(nsAString& aSourceMapURL)
512 : {
513 0 : aSourceMapURL = mInner->mSourceMapURL;
514 0 : }
515 :
516 : void
517 0 : StyleSheet::SetSourceMapURL(const nsAString& aSourceMapURL)
518 : {
519 0 : mInner->mSourceMapURL = aSourceMapURL;
520 0 : }
521 :
522 : css::Rule*
523 0 : StyleSheet::GetDOMOwnerRule() const
524 : {
525 0 : return mOwnerRule;
526 : }
527 :
528 : uint32_t
529 0 : StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
530 : nsIPrincipal& aSubjectPrincipal,
531 : ErrorResult& aRv)
532 : {
533 0 : if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
534 0 : return 0;
535 : }
536 0 : FORWARD_INTERNAL(InsertRuleInternal, (aRule, aIndex, aRv))
537 : }
538 :
539 : void
540 0 : StyleSheet::DeleteRule(uint32_t aIndex,
541 : nsIPrincipal& aSubjectPrincipal,
542 : ErrorResult& aRv)
543 : {
544 0 : if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
545 0 : return;
546 : }
547 0 : FORWARD_INTERNAL(DeleteRuleInternal, (aIndex, aRv))
548 : }
549 :
550 : nsresult
551 0 : StyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex)
552 : {
553 0 : NS_ENSURE_ARG_POINTER(aGroup);
554 0 : NS_ASSERTION(IsComplete(), "No deleting from an incomplete sheet!");
555 0 : RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
556 0 : NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
557 :
558 : // check that the rule actually belongs to this sheet!
559 0 : if (this != rule->GetStyleSheet()) {
560 0 : return NS_ERROR_INVALID_ARG;
561 : }
562 :
563 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
564 :
565 0 : WillDirty();
566 :
567 0 : nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
568 0 : NS_ENSURE_SUCCESS(result, result);
569 :
570 0 : rule->SetStyleSheet(nullptr);
571 :
572 0 : DidDirty();
573 :
574 0 : if (mDocument) {
575 0 : mDocument->StyleRuleRemoved(this, rule);
576 : }
577 :
578 0 : return NS_OK;
579 : }
580 :
581 : nsresult
582 0 : StyleSheet::InsertRuleIntoGroup(const nsAString& aRule,
583 : css::GroupRule* aGroup,
584 : uint32_t aIndex)
585 : {
586 0 : NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
587 : // check that the group actually belongs to this sheet!
588 0 : if (this != aGroup->GetStyleSheet()) {
589 0 : return NS_ERROR_INVALID_ARG;
590 : }
591 :
592 : // parse and grab the rule
593 0 : mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
594 :
595 0 : WillDirty();
596 :
597 : nsresult result;
598 0 : if (IsGecko()) {
599 0 : result = AsGecko()->InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
600 : } else {
601 0 : result = AsServo()->InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
602 : }
603 0 : NS_ENSURE_SUCCESS(result, result);
604 :
605 0 : DidDirty();
606 :
607 0 : if (mDocument) {
608 0 : mDocument->StyleRuleAdded(this, aGroup->GetStyleRuleAt(aIndex));
609 : }
610 :
611 0 : return NS_OK;
612 : }
613 :
614 : uint64_t
615 0 : StyleSheet::FindOwningWindowInnerID() const
616 : {
617 0 : uint64_t windowID = 0;
618 0 : if (mDocument) {
619 0 : windowID = mDocument->InnerWindowID();
620 : }
621 :
622 0 : if (windowID == 0 && mOwningNode) {
623 0 : windowID = mOwningNode->OwnerDoc()->InnerWindowID();
624 : }
625 :
626 0 : RefPtr<css::Rule> ownerRule;
627 0 : if (windowID == 0 && (ownerRule = GetDOMOwnerRule())) {
628 0 : RefPtr<StyleSheet> sheet = ownerRule->GetStyleSheet();
629 0 : if (sheet) {
630 0 : windowID = sheet->FindOwningWindowInnerID();
631 : }
632 : }
633 :
634 0 : if (windowID == 0 && mParent) {
635 0 : windowID = mParent->FindOwningWindowInnerID();
636 : }
637 :
638 0 : return windowID;
639 : }
640 :
641 : void
642 0 : StyleSheet::EnabledStateChanged()
643 : {
644 0 : FORWARD_INTERNAL(EnabledStateChangedInternal, ())
645 : }
646 :
647 : #undef FORWARD_INTERNAL
648 :
649 : void
650 0 : StyleSheet::UnparentChildren()
651 : {
652 : // XXXbz this is a little bogus; see the XXX comment where we
653 : // declare mFirstChild in StyleSheetInfo.
654 0 : for (StyleSheet* child = GetFirstChild();
655 0 : child;
656 0 : child = child->mNext) {
657 0 : if (child->mParent == this) {
658 0 : child->mParent = nullptr;
659 0 : MOZ_ASSERT(child->mDocumentAssociationMode == NotOwnedByDocument,
660 : "How did we get to the destructor, exactly, if we're owned "
661 : "by a document?");
662 0 : child->mDocument = nullptr;
663 : }
664 : }
665 0 : }
666 :
667 : void
668 0 : StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
669 : ErrorResult& aRv)
670 : {
671 0 : StyleSheetInfo& info = SheetInfo();
672 :
673 0 : if (aSubjectPrincipal.Subsumes(info.mPrincipal)) {
674 0 : return;
675 : }
676 :
677 : // Allow access only if CORS mode is not NONE and the security flag
678 : // is not turned off.
679 0 : if (GetCORSMode() == CORS_NONE &&
680 0 : !nsContentUtils::BypassCSSOMOriginCheck()) {
681 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
682 0 : return;
683 : }
684 :
685 : // Now make sure we set the principal of our inner to the subjectPrincipal.
686 : // We do this because we're in a situation where the caller would not normally
687 : // be able to access the sheet, but the sheet has opted in to being read.
688 : // Unfortunately, that means it's also opted in to being _edited_, and if the
689 : // caller now makes edits to the sheet we want the resulting resource loads,
690 : // if any, to look as if they are coming from the caller's principal, not the
691 : // original sheet principal.
692 : //
693 : // That means we need a unique inner, of course. But we don't want to do that
694 : // if we're not complete yet. Luckily, all the callers of this method throw
695 : // anyway if not complete, so we can just do that here too.
696 0 : if (!info.mComplete) {
697 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
698 0 : return;
699 : }
700 :
701 0 : WillDirty();
702 :
703 0 : info.mPrincipal = &aSubjectPrincipal;
704 :
705 0 : DidDirty();
706 : }
707 :
708 : bool
709 0 : StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
710 : ErrorResult& aRv)
711 : {
712 : // Rules are not available on incomplete sheets.
713 0 : if (!SheetInfo().mComplete) {
714 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
715 0 : return false;
716 : }
717 : //-- Security check: Only scripts whose principal subsumes that of the
718 : // style sheet can access rule collections.
719 0 : SubjectSubsumesInnerPrincipal(aSubjectPrincipal, aRv);
720 0 : if (NS_WARN_IF(aRv.Failed())) {
721 0 : return false;
722 : }
723 0 : return true;
724 : }
725 :
726 : StyleSheet*
727 16 : StyleSheet::GetFirstChild() const
728 : {
729 16 : return SheetInfo().mFirstChild;
730 : }
731 :
732 : void
733 14 : StyleSheet::SetAssociatedDocument(nsIDocument* aDocument,
734 : DocumentAssociationMode aAssociationMode)
735 : {
736 14 : MOZ_ASSERT(aDocument || aAssociationMode == NotOwnedByDocument);
737 :
738 : // not ref counted
739 14 : mDocument = aDocument;
740 14 : mDocumentAssociationMode = aAssociationMode;
741 :
742 : // Now set the same document on all our child sheets....
743 : // XXXbz this is a little bogus; see the XXX comment where we
744 : // declare mFirstChild.
745 14 : for (StyleSheet* child = GetFirstChild();
746 14 : child; child = child->mNext) {
747 0 : if (child->mParent == this) {
748 0 : child->SetAssociatedDocument(aDocument, aAssociationMode);
749 : }
750 : }
751 14 : }
752 :
753 : void
754 0 : StyleSheet::ClearAssociatedDocument()
755 : {
756 0 : SetAssociatedDocument(nullptr, NotOwnedByDocument);
757 0 : }
758 :
759 : void
760 3 : StyleSheet::PrependStyleSheet(StyleSheet* aSheet)
761 : {
762 3 : WillDirty();
763 3 : PrependStyleSheetSilently(aSheet);
764 3 : DidDirty();
765 3 : }
766 :
767 : void
768 3 : StyleSheet::PrependStyleSheetSilently(StyleSheet* aSheet)
769 : {
770 3 : MOZ_ASSERT(aSheet);
771 :
772 3 : aSheet->mNext = SheetInfo().mFirstChild;
773 3 : SheetInfo().mFirstChild = aSheet;
774 :
775 : // This is not reference counted. Our parent tells us when
776 : // it's going away.
777 3 : aSheet->mParent = this;
778 3 : aSheet->SetAssociatedDocument(mDocument, mDocumentAssociationMode);
779 3 : }
780 :
781 : size_t
782 5 : StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
783 : {
784 5 : size_t n = 0;
785 5 : const StyleSheet* s = this;
786 15 : while (s) {
787 5 : n += aMallocSizeOf(s);
788 :
789 : // Measurement of the following members may be added later if DMD finds it
790 : // is worthwhile:
791 : // - s->mTitle
792 : // - s->mMedia
793 : // - s->mStyleSets
794 :
795 5 : s = s->mNext;
796 : }
797 5 : return n;
798 : }
799 :
800 : #ifdef DEBUG
801 : void
802 0 : StyleSheet::List(FILE* out, int32_t aIndent) const
803 : {
804 : int32_t index;
805 :
806 : // Indent
807 0 : nsAutoCString str;
808 0 : for (index = aIndent; --index >= 0; ) {
809 0 : str.AppendLiteral(" ");
810 : }
811 :
812 0 : str.AppendLiteral("CSS Style Sheet: ");
813 0 : nsAutoCString urlSpec;
814 0 : nsresult rv = GetSheetURI()->GetSpec(urlSpec);
815 0 : if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) {
816 0 : str.Append(urlSpec);
817 : }
818 :
819 0 : if (mMedia) {
820 0 : str.AppendLiteral(" media: ");
821 0 : nsAutoString buffer;
822 0 : mMedia->GetText(buffer);
823 0 : AppendUTF16toUTF8(buffer, str);
824 : }
825 0 : str.Append('\n');
826 0 : fprintf_stderr(out, "%s", str.get());
827 :
828 0 : for (const StyleSheet* child = GetFirstChild();
829 0 : child;
830 0 : child = child->mNext) {
831 0 : child->List(out, aIndent + 1);
832 : }
833 0 : }
834 : #endif
835 :
836 : void
837 73 : StyleSheet::SetMedia(dom::MediaList* aMedia)
838 : {
839 73 : mMedia = aMedia;
840 73 : }
841 :
842 : void
843 0 : StyleSheet::DropMedia()
844 : {
845 0 : if (mMedia) {
846 0 : mMedia->SetStyleSheet(nullptr);
847 0 : mMedia = nullptr;
848 : }
849 0 : }
850 :
851 : dom::MediaList*
852 3 : StyleSheet::Media()
853 : {
854 3 : if (!mMedia) {
855 0 : mMedia = dom::MediaList::Create(mType, nsString());
856 0 : mMedia->SetStyleSheet(this);
857 : }
858 :
859 3 : return mMedia;
860 : }
861 :
862 : // nsWrapperCache
863 :
864 : JSObject*
865 0 : StyleSheet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
866 : {
867 0 : return dom::CSSStyleSheetBinding::Wrap(aCx, this, aGivenProto);
868 : }
869 :
870 : /* static */ bool
871 0 : StyleSheet::RuleHasPendingChildSheet(css::Rule* aRule)
872 : {
873 0 : MOZ_ASSERT(aRule->GetType() == css::Rule::IMPORT_RULE);
874 0 : auto rule = static_cast<dom::CSSImportRule*>(aRule);
875 0 : if (StyleSheet* childSheet = rule->GetStyleSheet()) {
876 0 : return !childSheet->IsComplete();
877 : }
878 0 : return false;
879 : }
880 :
881 : } // namespace mozilla
|