gecko-dev/layout/style/StyleSheet.cpp

917 строки
24 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/StyleSheet.h"
#include "mozilla/dom/CSSImportRule.h"
#include "mozilla/dom/CSSRuleList.h"
#include "mozilla/dom/MediaList.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/ServoCSSRuleList.h"
#include "mozilla/ServoStyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/CSSStyleSheet.h"
#include "mozAutoDocUpdate.h"
#include "NullPrincipal.h"
namespace mozilla {
StyleSheet::StyleSheet(StyleBackendType aType, css::SheetParsingMode aParsingMode)
: mParent(nullptr)
, mDocument(nullptr)
, mOwningNode(nullptr)
, mOwnerRule(nullptr)
, mParsingMode(aParsingMode)
, mType(aType)
, mDisabled(false)
, mDirty(false)
, mDocumentAssociationMode(NotOwnedByDocument)
, mInner(nullptr)
{
}
StyleSheet::StyleSheet(const StyleSheet& aCopy,
StyleSheet* aParentToUse,
dom::CSSImportRule* aOwnerRuleToUse,
nsIDocument* aDocumentToUse,
nsINode* aOwningNodeToUse)
: mParent(aParentToUse)
, mTitle(aCopy.mTitle)
, mDocument(aDocumentToUse)
, mOwningNode(aOwningNodeToUse)
, mOwnerRule(aOwnerRuleToUse)
, mParsingMode(aCopy.mParsingMode)
, mType(aCopy.mType)
, mDisabled(aCopy.mDisabled)
, mDirty(aCopy.mDirty)
// We only use this constructor during cloning. It's the cloner's
// responsibility to notify us if we end up being owned by a document.
, mDocumentAssociationMode(NotOwnedByDocument)
, mInner(aCopy.mInner) // Shallow copy, but concrete subclasses will fix up.
{
MOZ_ASSERT(mInner, "Should only copy StyleSheets with an mInner.");
mInner->AddSheet(this);
if (aCopy.mMedia) {
// XXX This is wrong; we should be keeping @import rules and
// sheets in sync!
mMedia = aCopy.mMedia->Clone();
}
}
StyleSheet::~StyleSheet()
{
MOZ_ASSERT(!mInner, "Inner should have been dropped in LastRelease");
}
void
StyleSheet::LastRelease()
{
MOZ_ASSERT(mInner, "Should have an mInner at time of destruction.");
MOZ_ASSERT(mInner->mSheets.Contains(this), "Our mInner should include us.");
UnparentChildren();
if (IsGecko()) {
AsGecko()->LastRelease();
} else {
AsServo()->LastRelease();
}
mInner->RemoveSheet(this);
mInner = nullptr;
DropMedia();
}
void
StyleSheet::UnlinkInner()
{
// We can only have a cycle through our inner if we have a unique inner,
// because otherwise there are no JS wrappers for anything in the inner.
if (mInner->mSheets.Length() != 1) {
return;
}
// Have to be a bit careful with child sheets, because we want to
// drop their mNext pointers and null out their mParent and
// mDocument, but don't want to work with deleted objects. And we
// don't want to do any addrefing in the process, just to make sure
// we don't confuse the cycle collector (though on the face of it,
// addref/release pairs during unlink should probably be ok).
RefPtr<StyleSheet> child;
child.swap(SheetInfo().mFirstChild);
while (child) {
MOZ_ASSERT(child->mParent == this, "We have a unique inner!");
child->mParent = nullptr;
// We (and child) might still think we're owned by a document, because
// unlink order is non-deterministic, so the document's unlink, which would
// tell us it does't own us anymore, may not have happened yet. But if
// we're being unlinked, clearly we're not owned by a document anymore
// conceptually!
child->SetAssociatedDocument(nullptr, NotOwnedByDocument);
RefPtr<StyleSheet> next;
// Null out child->mNext, but don't let it die yet
next.swap(child->mNext);
// Switch to looking at the old value of child->mNext next iteration
child.swap(next);
// "next" is now our previous value of child; it'll get released
// as we loop around.
}
}
void
StyleSheet::TraverseInner(nsCycleCollectionTraversalCallback &cb)
{
// We can only have a cycle through our inner if we have a unique inner,
// because otherwise there are no JS wrappers for anything in the inner.
if (mInner->mSheets.Length() != 1) {
return;
}
StyleSheet* childSheet = GetFirstChild();
while (childSheet) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "child sheet");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, childSheet));
childSheet = childSheet->mNext;
}
}
// QueryInterface implementation for StyleSheet
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheet)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet)
NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheet)
// We want to disconnect from our inner as soon as our refcount drops to zero,
// without waiting for async deletion by the cycle collector. Otherwise we
// might end up cloning the inner if someone mutates another sheet that shares
// it with us, even though there is only one such sheet and we're about to go
// away. This situation arises easily with sheet preloading.
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(StyleSheet, LastRelease())
NS_IMPL_CYCLE_COLLECTION_CLASS(StyleSheet)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StyleSheet)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMedia)
tmp->TraverseInner(cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StyleSheet)
tmp->DropMedia();
tmp->UnlinkInner();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(StyleSheet)
mozilla::dom::CSSStyleSheetParsingMode
StyleSheet::ParsingModeDOM()
{
#define CHECK(X, Y) \
static_assert(static_cast<int>(X) == static_cast<int>(Y), \
"mozilla::dom::CSSStyleSheetParsingMode and mozilla::css::SheetParsingMode should have identical values");
CHECK(mozilla::dom::CSSStyleSheetParsingMode::Agent, css::eAgentSheetFeatures);
CHECK(mozilla::dom::CSSStyleSheetParsingMode::User, css::eUserSheetFeatures);
CHECK(mozilla::dom::CSSStyleSheetParsingMode::Author, css::eAuthorSheetFeatures);
#undef CHECK
return static_cast<mozilla::dom::CSSStyleSheetParsingMode>(mParsingMode);
}
bool
StyleSheet::IsComplete() const
{
return SheetInfo().mComplete;
}
void
StyleSheet::SetComplete()
{
NS_ASSERTION(!mDirty, "Can't set a dirty sheet complete!");
SheetInfo().mComplete = true;
if (mDocument && !mDisabled) {
// Let the document know
mDocument->BeginUpdate(UPDATE_STYLE);
mDocument->SetStyleSheetApplicableState(this, true);
mDocument->EndUpdate(UPDATE_STYLE);
}
if (mOwningNode && !mDisabled &&
mOwningNode->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
mOwningNode->IsContent()) {
dom::ShadowRoot* shadowRoot = mOwningNode->AsContent()->GetContainingShadow();
shadowRoot->StyleSheetChanged();
}
}
void
StyleSheet::SetEnabled(bool aEnabled)
{
// Internal method, so callers must handle BeginUpdate/EndUpdate
bool oldDisabled = mDisabled;
mDisabled = !aEnabled;
if (IsComplete() && oldDisabled != mDisabled) {
EnabledStateChanged();
if (mDocument) {
mDocument->SetStyleSheetApplicableState(this, !mDisabled);
}
}
}
StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
ReferrerPolicy aReferrerPolicy,
const dom::SRIMetadata& aIntegrity)
: mPrincipal(NullPrincipal::Create())
, mCORSMode(aCORSMode)
, mReferrerPolicy(aReferrerPolicy)
, mIntegrity(aIntegrity)
, mComplete(false)
#ifdef DEBUG
, mPrincipalSet(false)
#endif
{
if (!mPrincipal) {
MOZ_CRASH("NullPrincipal::Init failed");
}
}
StyleSheetInfo::StyleSheetInfo(StyleSheetInfo& aCopy,
StyleSheet* aPrimarySheet)
: mSheetURI(aCopy.mSheetURI)
, mOriginalSheetURI(aCopy.mOriginalSheetURI)
, mBaseURI(aCopy.mBaseURI)
, mPrincipal(aCopy.mPrincipal)
, mCORSMode(aCopy.mCORSMode)
, mReferrerPolicy(aCopy.mReferrerPolicy)
, mIntegrity(aCopy.mIntegrity)
, mComplete(aCopy.mComplete)
, mFirstChild() // We don't rebuild the child because we're making a copy
// without children.
, mSourceMapURL(aCopy.mSourceMapURL)
, mSourceMapURLFromComment(aCopy.mSourceMapURLFromComment)
, mSourceURL(aCopy.mSourceURL)
#ifdef DEBUG
, mPrincipalSet(aCopy.mPrincipalSet)
#endif
{
AddSheet(aPrimarySheet);
}
StyleSheetInfo::~StyleSheetInfo()
{
}
void
StyleSheetInfo::AddSheet(StyleSheet* aSheet)
{
mSheets.AppendElement(aSheet);
}
void
StyleSheetInfo::RemoveSheet(StyleSheet* aSheet)
{
if ((aSheet == mSheets.ElementAt(0)) && (mSheets.Length() > 1)) {
StyleSheet::ChildSheetListBuilder::ReparentChildList(mSheets[1], mFirstChild);
}
if (1 == mSheets.Length()) {
NS_ASSERTION(aSheet == mSheets.ElementAt(0), "bad parent");
delete this;
return;
}
mSheets.RemoveElement(aSheet);
}
void
StyleSheet::ChildSheetListBuilder::SetParentLinks(StyleSheet* aSheet)
{
aSheet->mParent = parent;
aSheet->SetAssociatedDocument(parent->mDocument,
parent->mDocumentAssociationMode);
}
void
StyleSheet::ChildSheetListBuilder::ReparentChildList(StyleSheet* aPrimarySheet,
StyleSheet* aFirstChild)
{
for (StyleSheet *child = aFirstChild; child; child = child->mNext) {
child->mParent = aPrimarySheet;
child->SetAssociatedDocument(aPrimarySheet->mDocument,
aPrimarySheet->mDocumentAssociationMode);
}
}
// nsIDOMStyleSheet interface
NS_IMETHODIMP
StyleSheet::GetType(nsAString& aType)
{
aType.AssignLiteral("text/css");
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetDisabled(bool* aDisabled)
{
*aDisabled = Disabled();
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::SetDisabled(bool aDisabled)
{
// DOM method, so handle BeginUpdate/EndUpdate
MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_STYLE, true);
SetEnabled(!aDisabled);
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode)
{
nsCOMPtr<nsIDOMNode> ownerNode = do_QueryInterface(GetOwnerNode());
ownerNode.forget(aOwnerNode);
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetHref(nsAString& aHref)
{
if (nsIURI* sheetURI = SheetInfo().mOriginalSheetURI) {
nsAutoCString str;
nsresult rv = sheetURI->GetSpec(str);
NS_ENSURE_SUCCESS(rv, rv);
CopyUTF8toUTF16(str, aHref);
} else {
SetDOMStringToNull(aHref);
}
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetTitle(nsAString& aTitle)
{
aTitle.Assign(mTitle);
return NS_OK;
}
// nsIDOMStyleSheet interface
NS_IMETHODIMP
StyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet)
{
NS_ENSURE_ARG_POINTER(aParentStyleSheet);
NS_IF_ADDREF(*aParentStyleSheet = GetParentStyleSheet());
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetMedia(nsIDOMMediaList** aMedia)
{
NS_ADDREF(*aMedia = Media());
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule)
{
NS_IF_ADDREF(*aOwnerRule = GetDOMOwnerRule());
return NS_OK;
}
NS_IMETHODIMP
StyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules)
{
ErrorResult rv;
nsCOMPtr<nsIDOMCSSRuleList> rules =
GetCssRules(*nsContentUtils::SubjectPrincipal(), rv);
rules.forget(aCssRules);
return rv.StealNSResult();
}
NS_IMETHODIMP
StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
uint32_t* aReturn)
{
ErrorResult rv;
*aReturn =
InsertRule(aRule, aIndex, *nsContentUtils::SubjectPrincipal(), rv);
return rv.StealNSResult();
}
NS_IMETHODIMP
StyleSheet::DeleteRule(uint32_t aIndex)
{
ErrorResult rv;
DeleteRule(aIndex, *nsContentUtils::SubjectPrincipal(), rv);
return rv.StealNSResult();
}
void
StyleSheet::WillDirty()
{
if (mInner->mComplete) {
EnsureUniqueInner();
}
}
void
StyleSheet::AddStyleSet(const StyleSetHandle& aStyleSet)
{
NS_ASSERTION(!mStyleSets.Contains(aStyleSet),
"style set already registered");
mStyleSets.AppendElement(aStyleSet);
}
void
StyleSheet::DropStyleSet(const StyleSetHandle& aStyleSet)
{
DebugOnly<bool> found = mStyleSets.RemoveElement(aStyleSet);
NS_ASSERTION(found, "didn't find style set");
}
void
StyleSheet::EnsureUniqueInner()
{
MOZ_ASSERT(mInner->mSheets.Length() != 0,
"unexpected number of outers");
mDirty = true;
if (HasUniqueInner()) {
// already unique
return;
}
// If this stylesheet is for XBL with Servo, don't bother cloning
// it, as it may break ServoStyleRuleMap. XBL stylesheets are not
// supposed to change anyway.
// The mDocument check is used as a fast reject path because no
// XBL stylesheets would have associated document, but in normal
// cases, content stylesheets should usually have one.
if (!mDocument && IsServo() &&
mStyleSets.Length() == 1 &&
mStyleSets[0]->AsServo()->IsForXBL()) {
return;
}
StyleSheetInfo* clone = mInner->CloneFor(this);
MOZ_ASSERT(clone);
mInner->RemoveSheet(this);
mInner = clone;
if (CSSStyleSheet* geckoSheet = GetAsGecko()) {
// Ensure we're using the new rules.
//
// NOTE: In Servo, all kind of changes that change the set of selectors or
// rules we match are covered by the PresShell notifications. In Gecko
// that's true too, but this is probably needed because selectors are not
// refcounted and can become stale.
geckoSheet->ClearRuleCascades();
} else {
// Fixup the child lists and parent links in the Servo sheet. This is done
// here instead of in StyleSheetInner::CloneFor, because it's just more
// convenient to do so instead.
AsServo()->BuildChildListAfterInnerClone();
}
// let our containing style sets know that if we call
// nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
// document
for (StyleSetHandle& setHandle : mStyleSets) {
setHandle->SetNeedsRestyleAfterEnsureUniqueInner();
}
}
void
StyleSheet::AppendAllChildSheets(nsTArray<StyleSheet*>& aArray)
{
for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) {
aArray.AppendElement(child);
}
}
// WebIDL CSSStyleSheet API
#define FORWARD_INTERNAL(method_, args_) \
if (IsServo()) { \
return AsServo()->method_ args_; \
} \
return AsGecko()->method_ args_;
dom::CSSRuleList*
StyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
return nullptr;
}
FORWARD_INTERNAL(GetCssRulesInternal, ())
}
void
StyleSheet::GetSourceMapURL(nsAString& aSourceMapURL)
{
if (mInner->mSourceMapURL.IsEmpty()) {
aSourceMapURL = mInner->mSourceMapURLFromComment;
} else {
aSourceMapURL = mInner->mSourceMapURL;
}
}
void
StyleSheet::SetSourceMapURL(const nsAString& aSourceMapURL)
{
mInner->mSourceMapURL = aSourceMapURL;
}
void
StyleSheet::SetSourceMapURLFromComment(const nsAString& aSourceMapURLFromComment)
{
mInner->mSourceMapURLFromComment = aSourceMapURLFromComment;
}
void
StyleSheet::GetSourceURL(nsAString& aSourceURL)
{
aSourceURL = mInner->mSourceURL;
}
void
StyleSheet::SetSourceURL(const nsAString& aSourceURL)
{
mInner->mSourceURL = aSourceURL;
}
css::Rule*
StyleSheet::GetDOMOwnerRule() const
{
return mOwnerRule;
}
uint32_t
StyleSheet::InsertRule(const nsAString& aRule, uint32_t aIndex,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
return 0;
}
FORWARD_INTERNAL(InsertRuleInternal, (aRule, aIndex, aRv))
}
void
StyleSheet::DeleteRule(uint32_t aIndex,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
return;
}
FORWARD_INTERNAL(DeleteRuleInternal, (aIndex, aRv))
}
nsresult
StyleSheet::DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex)
{
NS_ENSURE_ARG_POINTER(aGroup);
NS_ASSERTION(IsComplete(), "No deleting from an incomplete sheet!");
RefPtr<css::Rule> rule = aGroup->GetStyleRuleAt(aIndex);
NS_ENSURE_TRUE(rule, NS_ERROR_ILLEGAL_VALUE);
// check that the rule actually belongs to this sheet!
if (this != rule->GetStyleSheet()) {
return NS_ERROR_INVALID_ARG;
}
mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
WillDirty();
nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
NS_ENSURE_SUCCESS(result, result);
rule->SetStyleSheet(nullptr);
DidDirty();
if (mDocument) {
mDocument->StyleRuleRemoved(this, rule);
}
return NS_OK;
}
nsresult
StyleSheet::InsertRuleIntoGroup(const nsAString& aRule,
css::GroupRule* aGroup,
uint32_t aIndex)
{
NS_ASSERTION(IsComplete(), "No inserting into an incomplete sheet!");
// check that the group actually belongs to this sheet!
if (this != aGroup->GetStyleSheet()) {
return NS_ERROR_INVALID_ARG;
}
// parse and grab the rule
mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, true);
WillDirty();
nsresult result;
if (IsGecko()) {
result = AsGecko()->InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
} else {
result = AsServo()->InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
}
NS_ENSURE_SUCCESS(result, result);
DidDirty();
if (mDocument) {
mDocument->StyleRuleAdded(this, aGroup->GetStyleRuleAt(aIndex));
}
return NS_OK;
}
uint64_t
StyleSheet::FindOwningWindowInnerID() const
{
uint64_t windowID = 0;
if (mDocument) {
windowID = mDocument->InnerWindowID();
}
if (windowID == 0 && mOwningNode) {
windowID = mOwningNode->OwnerDoc()->InnerWindowID();
}
RefPtr<css::Rule> ownerRule;
if (windowID == 0 && (ownerRule = GetDOMOwnerRule())) {
RefPtr<StyleSheet> sheet = ownerRule->GetStyleSheet();
if (sheet) {
windowID = sheet->FindOwningWindowInnerID();
}
}
if (windowID == 0 && mParent) {
windowID = mParent->FindOwningWindowInnerID();
}
return windowID;
}
void
StyleSheet::EnabledStateChanged()
{
FORWARD_INTERNAL(EnabledStateChangedInternal, ())
}
#undef FORWARD_INTERNAL
void
StyleSheet::UnparentChildren()
{
// XXXbz this is a little bogus; see the XXX comment where we
// declare mFirstChild in StyleSheetInfo.
for (StyleSheet* child = GetFirstChild();
child;
child = child->mNext) {
if (child->mParent == this) {
child->mParent = nullptr;
MOZ_ASSERT(child->mDocumentAssociationMode == NotOwnedByDocument,
"How did we get to the destructor, exactly, if we're owned "
"by a document?");
child->mDocument = nullptr;
}
}
}
void
StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
StyleSheetInfo& info = SheetInfo();
if (aSubjectPrincipal.Subsumes(info.mPrincipal)) {
return;
}
// Allow access only if CORS mode is not NONE and the security flag
// is not turned off.
if (GetCORSMode() == CORS_NONE &&
!nsContentUtils::BypassCSSOMOriginCheck()) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
// Now make sure we set the principal of our inner to the subjectPrincipal.
// We do this because we're in a situation where the caller would not normally
// be able to access the sheet, but the sheet has opted in to being read.
// Unfortunately, that means it's also opted in to being _edited_, and if the
// caller now makes edits to the sheet we want the resulting resource loads,
// if any, to look as if they are coming from the caller's principal, not the
// original sheet principal.
//
// That means we need a unique inner, of course. But we don't want to do that
// if we're not complete yet. Luckily, all the callers of this method throw
// anyway if not complete, so we can just do that here too.
if (!info.mComplete) {
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return;
}
WillDirty();
info.mPrincipal = &aSubjectPrincipal;
DidDirty();
}
bool
StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv)
{
// Rules are not available on incomplete sheets.
if (!SheetInfo().mComplete) {
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return false;
}
//-- Security check: Only scripts whose principal subsumes that of the
// style sheet can access rule collections.
SubjectSubsumesInnerPrincipal(aSubjectPrincipal, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return false;
}
return true;
}
StyleSheet*
StyleSheet::GetFirstChild() const
{
return SheetInfo().mFirstChild;
}
void
StyleSheet::SetAssociatedDocument(nsIDocument* aDocument,
DocumentAssociationMode aAssociationMode)
{
MOZ_ASSERT(aDocument || aAssociationMode == NotOwnedByDocument);
// not ref counted
mDocument = aDocument;
mDocumentAssociationMode = aAssociationMode;
// Now set the same document on all our child sheets....
// XXXbz this is a little bogus; see the XXX comment where we
// declare mFirstChild.
for (StyleSheet* child = GetFirstChild();
child; child = child->mNext) {
if (child->mParent == this) {
child->SetAssociatedDocument(aDocument, aAssociationMode);
}
}
}
void
StyleSheet::ClearAssociatedDocument()
{
SetAssociatedDocument(nullptr, NotOwnedByDocument);
}
void
StyleSheet::PrependStyleSheet(StyleSheet* aSheet)
{
WillDirty();
PrependStyleSheetSilently(aSheet);
DidDirty();
}
void
StyleSheet::PrependStyleSheetSilently(StyleSheet* aSheet)
{
MOZ_ASSERT(aSheet);
aSheet->mNext = SheetInfo().mFirstChild;
SheetInfo().mFirstChild = aSheet;
// This is not reference counted. Our parent tells us when
// it's going away.
aSheet->mParent = this;
aSheet->SetAssociatedDocument(mDocument, mDocumentAssociationMode);
}
size_t
StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t n = 0;
const StyleSheet* s = this;
while (s) {
n += aMallocSizeOf(s);
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - s->mTitle
// - s->mMedia
// - s->mStyleSets
s = s->mNext;
}
return n;
}
#ifdef DEBUG
void
StyleSheet::List(FILE* out, int32_t aIndent) const
{
int32_t index;
// Indent
nsAutoCString str;
for (index = aIndent; --index >= 0; ) {
str.AppendLiteral(" ");
}
str.AppendLiteral("CSS Style Sheet: ");
nsAutoCString urlSpec;
nsresult rv = GetSheetURI()->GetSpec(urlSpec);
if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) {
str.Append(urlSpec);
}
if (mMedia) {
str.AppendLiteral(" media: ");
nsAutoString buffer;
mMedia->GetText(buffer);
AppendUTF16toUTF8(buffer, str);
}
str.Append('\n');
fprintf_stderr(out, "%s", str.get());
for (const StyleSheet* child = GetFirstChild();
child;
child = child->mNext) {
child->List(out, aIndent + 1);
}
}
#endif
void
StyleSheet::SetMedia(dom::MediaList* aMedia)
{
mMedia = aMedia;
}
void
StyleSheet::DropMedia()
{
if (mMedia) {
mMedia->SetStyleSheet(nullptr);
mMedia = nullptr;
}
}
dom::MediaList*
StyleSheet::Media()
{
if (!mMedia) {
mMedia = dom::MediaList::Create(mType, nsString());
mMedia->SetStyleSheet(this);
}
return mMedia;
}
// nsWrapperCache
JSObject*
StyleSheet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return dom::CSSStyleSheetBinding::Wrap(aCx, this, aGivenProto);
}
/* static */ bool
StyleSheet::RuleHasPendingChildSheet(css::Rule* aRule)
{
MOZ_ASSERT(aRule->GetType() == css::Rule::IMPORT_RULE);
auto rule = static_cast<dom::CSSImportRule*>(aRule);
if (StyleSheet* childSheet = rule->GetStyleSheet()) {
return !childSheet->IsComplete();
}
return false;
}
} // namespace mozilla