mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 08:42:13 +00:00
02f4dbbbf9
Referrer policy argurment is passed from sheet/doc to URLExtraData, default value is RP_Unset. We use default value in some cases, particularly when there's no certain spec talks about that (svg for example) MozReview-Commit-ID: 5VAX1ZUXD3i Differential Revision: https://phabricator.services.mozilla.com/D1922 --HG-- extra : moz-landing-system : lando
1334 lines
37 KiB
C++
1334 lines
37 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/ComputedStyleInlines.h"
|
|
#include "mozilla/css/ErrorReporter.h"
|
|
#include "mozilla/css/GroupRule.h"
|
|
#include "mozilla/dom/CSSImportRule.h"
|
|
#include "mozilla/dom/CSSRuleList.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/dom/MediaList.h"
|
|
#include "mozilla/dom/ShadowRoot.h"
|
|
#include "mozilla/dom/ShadowRootBinding.h"
|
|
#include "mozilla/NullPrincipal.h"
|
|
#include "mozilla/ServoBindings.h"
|
|
#include "mozilla/ServoCSSRuleList.h"
|
|
#include "mozilla/ServoStyleSet.h"
|
|
#include "mozilla/StaticPrefs.h"
|
|
#include "mozilla/StyleSheetInlines.h"
|
|
|
|
#include "mozAutoDocUpdate.h"
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace dom;
|
|
|
|
StyleSheet::StyleSheet(css::SheetParsingMode aParsingMode,
|
|
CORSMode aCORSMode,
|
|
net::ReferrerPolicy aReferrerPolicy,
|
|
const dom::SRIMetadata& aIntegrity)
|
|
: mParent(nullptr)
|
|
, mDocumentOrShadowRoot(nullptr)
|
|
, mOwningNode(nullptr)
|
|
, mOwnerRule(nullptr)
|
|
, mParsingMode(aParsingMode)
|
|
, mState(static_cast<State>(0))
|
|
, mAssociationMode(NotOwnedByDocumentOrShadowRoot)
|
|
, mInner(new StyleSheetInfo(aCORSMode, aReferrerPolicy, aIntegrity, aParsingMode))
|
|
{
|
|
mInner->AddSheet(this);
|
|
}
|
|
|
|
StyleSheet::StyleSheet(const StyleSheet& aCopy,
|
|
StyleSheet* aParentToUse,
|
|
dom::CSSImportRule* aOwnerRuleToUse,
|
|
dom::DocumentOrShadowRoot* aDocumentOrShadowRoot,
|
|
nsINode* aOwningNodeToUse)
|
|
: mParent(aParentToUse)
|
|
, mTitle(aCopy.mTitle)
|
|
, mDocumentOrShadowRoot(aDocumentOrShadowRoot)
|
|
, mOwningNode(aOwningNodeToUse)
|
|
, mOwnerRule(aOwnerRuleToUse)
|
|
, mParsingMode(aCopy.mParsingMode)
|
|
, mState(aCopy.mState)
|
|
// 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.
|
|
, mAssociationMode(NotOwnedByDocumentOrShadowRoot)
|
|
, 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 (HasForcedUniqueInner()) { // CSSOM's been there, force full copy now
|
|
MOZ_ASSERT(IsComplete(),
|
|
"Why have rules been accessed on an incomplete sheet?");
|
|
// FIXME: handle failure?
|
|
EnsureUniqueInner();
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
bool
|
|
StyleSheet::HasRules() const
|
|
{
|
|
return Servo_StyleSheet_HasRules(Inner().mContents);
|
|
}
|
|
|
|
nsIDocument*
|
|
StyleSheet::GetAssociatedDocument() const
|
|
{
|
|
return mDocumentOrShadowRoot
|
|
? mDocumentOrShadowRoot->AsNode().OwnerDoc()
|
|
: nullptr;
|
|
}
|
|
|
|
nsIDocument*
|
|
StyleSheet::GetComposedDoc() const
|
|
{
|
|
return mDocumentOrShadowRoot
|
|
? mDocumentOrShadowRoot->AsNode().GetComposedDoc()
|
|
: nullptr;
|
|
}
|
|
|
|
bool
|
|
StyleSheet::IsKeptAliveByDocument() const
|
|
{
|
|
if (mAssociationMode != OwnedByDocumentOrShadowRoot) {
|
|
return false;
|
|
}
|
|
|
|
return !!GetComposedDoc();
|
|
}
|
|
|
|
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();
|
|
|
|
mInner->RemoveSheet(this);
|
|
mInner = nullptr;
|
|
|
|
DropMedia();
|
|
DropRuleList();
|
|
}
|
|
|
|
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(Inner().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->ClearAssociatedDocumentOrShadowRoot();
|
|
|
|
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(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(nsISupports)
|
|
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)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRuleList)
|
|
tmp->TraverseInner(cb);
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StyleSheet)
|
|
tmp->DropMedia();
|
|
tmp->UnlinkInner();
|
|
tmp->DropRuleList();
|
|
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);
|
|
}
|
|
|
|
void
|
|
StyleSheet::SetComplete()
|
|
{
|
|
MOZ_ASSERT(!HasForcedUniqueInner(),
|
|
"Can't complete a sheet that's already been forced unique.");
|
|
MOZ_ASSERT(!IsComplete(), "Already complete?");
|
|
mState |= State::Complete;
|
|
if (!Disabled()) {
|
|
ApplicableStateChanged(true);
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::ApplicableStateChanged(bool aApplicable)
|
|
{
|
|
if (!mDocumentOrShadowRoot) {
|
|
return;
|
|
}
|
|
|
|
nsINode& node = mDocumentOrShadowRoot->AsNode();
|
|
if (auto* shadow = ShadowRoot::FromNode(node)) {
|
|
shadow->StyleSheetApplicableStateChanged(*this, aApplicable);
|
|
} else {
|
|
node.AsDocument()->SetStyleSheetApplicableState(this, aApplicable);
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::SetDisabled(bool aDisabled)
|
|
{
|
|
if (aDisabled == Disabled()) {
|
|
return;
|
|
}
|
|
|
|
if (aDisabled) {
|
|
mState |= State::Disabled;
|
|
} else {
|
|
mState &= ~State::Disabled;
|
|
}
|
|
|
|
if (IsComplete()) {
|
|
ApplicableStateChanged(!aDisabled);
|
|
}
|
|
}
|
|
|
|
already_AddRefed<URLExtraData>
|
|
StyleSheet::CreateURLExtraData() const
|
|
{
|
|
RefPtr<URLExtraData> data = new URLExtraData(GetBaseURI(),
|
|
GetSheetURI(),
|
|
Principal(),
|
|
GetReferrerPolicy());
|
|
return data.forget();
|
|
}
|
|
StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
|
|
ReferrerPolicy aReferrerPolicy,
|
|
const SRIMetadata& aIntegrity,
|
|
css::SheetParsingMode aParsingMode)
|
|
: mPrincipal(NullPrincipal::CreateWithoutOriginAttributes())
|
|
, mCORSMode(aCORSMode)
|
|
, mReferrerPolicy(aReferrerPolicy)
|
|
, mIntegrity(aIntegrity)
|
|
, mContents(Servo_StyleSheet_Empty(aParsingMode).Consume())
|
|
, mURLData(URLExtraData::Dummy())
|
|
#ifdef DEBUG
|
|
, mPrincipalSet(false)
|
|
#endif
|
|
{
|
|
if (!mPrincipal) {
|
|
MOZ_CRASH("NullPrincipal::Init failed");
|
|
}
|
|
MOZ_COUNT_CTOR(StyleSheetInfo);
|
|
}
|
|
|
|
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)
|
|
, mFirstChild() // We don't rebuild the child because we're making a copy
|
|
// without children.
|
|
, mSourceMapURL(aCopy.mSourceMapURL)
|
|
, mSourceMapURLFromComment(aCopy.mSourceMapURLFromComment)
|
|
, mSourceURL(aCopy.mSourceURL)
|
|
, mContents(Servo_StyleSheet_Clone(aCopy.mContents.get(), aPrimarySheet).Consume())
|
|
, mURLData(aCopy.mURLData)
|
|
#ifdef DEBUG
|
|
, mPrincipalSet(aCopy.mPrincipalSet)
|
|
#endif
|
|
{
|
|
AddSheet(aPrimarySheet);
|
|
|
|
// Our child list is fixed up by our parent.
|
|
MOZ_COUNT_CTOR(StyleSheetInfo);
|
|
}
|
|
|
|
StyleSheetInfo::~StyleSheetInfo()
|
|
{
|
|
MOZ_COUNT_DTOR(StyleSheetInfo);
|
|
}
|
|
|
|
StyleSheetInfo*
|
|
StyleSheetInfo::CloneFor(StyleSheet* aPrimarySheet)
|
|
{
|
|
return new StyleSheetInfo(*this, aPrimarySheet);
|
|
}
|
|
|
|
MOZ_DEFINE_MALLOC_SIZE_OF(ServoStyleSheetMallocSizeOf)
|
|
MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoStyleSheetMallocEnclosingSizeOf)
|
|
|
|
size_t
|
|
StyleSheetInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
size_t n = aMallocSizeOf(this);
|
|
n += Servo_StyleSheet_SizeOfIncludingThis(
|
|
ServoStyleSheetMallocSizeOf,
|
|
ServoStyleSheetMallocEnclosingSizeOf,
|
|
mContents);
|
|
return n;
|
|
}
|
|
|
|
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->SetAssociatedDocumentOrShadowRoot(parent->mDocumentOrShadowRoot,
|
|
parent->mAssociationMode);
|
|
}
|
|
|
|
void
|
|
StyleSheet::ChildSheetListBuilder::ReparentChildList(StyleSheet* aPrimarySheet,
|
|
StyleSheet* aFirstChild)
|
|
{
|
|
for (StyleSheet *child = aFirstChild; child; child = child->mNext) {
|
|
child->mParent = aPrimarySheet;
|
|
child->SetAssociatedDocumentOrShadowRoot(aPrimarySheet->mDocumentOrShadowRoot,
|
|
aPrimarySheet->mAssociationMode);
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::GetType(nsAString& aType)
|
|
{
|
|
aType.AssignLiteral("text/css");
|
|
}
|
|
|
|
void
|
|
StyleSheet::GetHref(nsAString& aHref, ErrorResult& aRv)
|
|
{
|
|
if (nsIURI* sheetURI = Inner().mOriginalSheetURI) {
|
|
nsAutoCString str;
|
|
nsresult rv = sheetURI->GetSpec(str);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
return;
|
|
}
|
|
CopyUTF8toUTF16(str, aHref);
|
|
} else {
|
|
SetDOMStringToNull(aHref);
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::GetTitle(nsAString& aTitle)
|
|
{
|
|
// From https://drafts.csswg.org/cssom/#dom-stylesheet-title:
|
|
//
|
|
// The title attribute must return the title or null if title is the empty
|
|
// string.
|
|
//
|
|
if (!mTitle.IsEmpty()) {
|
|
aTitle.Assign(mTitle);
|
|
} else {
|
|
SetDOMStringToNull(aTitle);
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::WillDirty()
|
|
{
|
|
if (IsComplete()) {
|
|
EnsureUniqueInner();
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::AddStyleSet(ServoStyleSet* aStyleSet)
|
|
{
|
|
NS_ASSERTION(!mStyleSets.Contains(aStyleSet),
|
|
"style set already registered");
|
|
mStyleSets.AppendElement(aStyleSet);
|
|
}
|
|
|
|
void
|
|
StyleSheet::DropStyleSet(ServoStyleSet* 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");
|
|
mState |= State::ForcedUniqueInner;
|
|
|
|
if (HasUniqueInner()) {
|
|
// already unique
|
|
return;
|
|
}
|
|
|
|
StyleSheetInfo* clone = mInner->CloneFor(this);
|
|
MOZ_ASSERT(clone);
|
|
mInner->RemoveSheet(this);
|
|
mInner = clone;
|
|
|
|
// 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.
|
|
BuildChildListAfterInnerClone();
|
|
|
|
// let our containing style sets know that if we call
|
|
// nsPresContext::EnsureSafeToHandOutCSSRules we will need to restyle the
|
|
// document
|
|
for (ServoStyleSet* setHandle : mStyleSets) {
|
|
setHandle->SetNeedsRestyleAfterEnsureUniqueInner();
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::AppendAllChildSheets(nsTArray<StyleSheet*>& aArray)
|
|
{
|
|
for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) {
|
|
aArray.AppendElement(child);
|
|
}
|
|
}
|
|
|
|
// WebIDL CSSStyleSheet API
|
|
|
|
dom::CSSRuleList*
|
|
StyleSheet::GetCssRules(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv)
|
|
{
|
|
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
|
|
return nullptr;
|
|
}
|
|
return 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;
|
|
}
|
|
return InsertRuleInternal(aRule, aIndex, aRv);
|
|
}
|
|
|
|
void
|
|
StyleSheet::DeleteRule(uint32_t aIndex,
|
|
nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv)
|
|
{
|
|
if (!AreRulesAvailable(aSubjectPrincipal, aRv)) {
|
|
return;
|
|
}
|
|
return 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;
|
|
}
|
|
|
|
WillDirty();
|
|
|
|
nsresult result = aGroup->DeleteStyleRuleAt(aIndex);
|
|
NS_ENSURE_SUCCESS(result, result);
|
|
|
|
rule->DropReferences();
|
|
|
|
RuleRemoved(*rule);
|
|
return NS_OK;
|
|
}
|
|
|
|
dom::ShadowRoot*
|
|
StyleSheet::GetContainingShadow() const
|
|
{
|
|
if (!mOwningNode || !mOwningNode->IsContent()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return mOwningNode->AsContent()->GetContainingShadow();
|
|
}
|
|
|
|
#define NOTIFY(function_, args_) do { \
|
|
StyleSheet* current = this; \
|
|
do { \
|
|
for (ServoStyleSet* handle : current->mStyleSets) { \
|
|
handle->function_ args_; \
|
|
} \
|
|
if (auto* shadow = current->GetContainingShadow()) { \
|
|
shadow->function_ args_; \
|
|
} \
|
|
current = current->mParent; \
|
|
} while (current); \
|
|
} while (0)
|
|
|
|
void
|
|
StyleSheet::RuleAdded(css::Rule& aRule)
|
|
{
|
|
mState |= State::ModifiedRules;
|
|
NOTIFY(RuleAdded, (*this, aRule));
|
|
|
|
if (nsIDocument* doc = GetComposedDoc()) {
|
|
doc->StyleRuleAdded(this, &aRule);
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::RuleRemoved(css::Rule& aRule)
|
|
{
|
|
mState |= State::ModifiedRules;
|
|
NOTIFY(RuleRemoved, (*this, aRule));
|
|
|
|
if (nsIDocument* doc = GetComposedDoc()) {
|
|
doc->StyleRuleRemoved(this, &aRule);
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::RuleChanged(css::Rule* aRule)
|
|
{
|
|
mState |= State::ModifiedRules;
|
|
NOTIFY(RuleChanged, (*this, aRule));
|
|
|
|
if (nsIDocument* doc = GetComposedDoc()) {
|
|
doc->StyleRuleChanged(this, aRule);
|
|
}
|
|
}
|
|
|
|
#undef NOTIFY
|
|
|
|
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;
|
|
}
|
|
|
|
WillDirty();
|
|
|
|
nsresult result = InsertRuleIntoGroupInternal(aRule, aGroup, aIndex);
|
|
NS_ENSURE_SUCCESS(result, result);
|
|
RuleAdded(*aGroup->GetStyleRuleAt(aIndex));
|
|
return NS_OK;
|
|
}
|
|
|
|
uint64_t
|
|
StyleSheet::FindOwningWindowInnerID() const
|
|
{
|
|
uint64_t windowID = 0;
|
|
if (nsIDocument* doc = GetAssociatedDocument()) {
|
|
windowID = doc->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::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->mAssociationMode == NotOwnedByDocumentOrShadowRoot,
|
|
"How did we get to the destructor, exactly, if we're owned "
|
|
"by a document?");
|
|
child->mDocumentOrShadowRoot = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::SubjectSubsumesInnerPrincipal(nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv)
|
|
{
|
|
StyleSheetInfo& info = Inner();
|
|
|
|
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 (!IsComplete()) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
|
return;
|
|
}
|
|
|
|
WillDirty();
|
|
|
|
info.mPrincipal = &aSubjectPrincipal;
|
|
}
|
|
|
|
bool
|
|
StyleSheet::AreRulesAvailable(nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aRv)
|
|
{
|
|
// Rules are not available on incomplete sheets.
|
|
if (!IsComplete()) {
|
|
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 Inner().mFirstChild;
|
|
}
|
|
|
|
void
|
|
StyleSheet::SetAssociatedDocumentOrShadowRoot(DocumentOrShadowRoot* aDocOrShadowRoot,
|
|
AssociationMode aAssociationMode)
|
|
{
|
|
MOZ_ASSERT(aDocOrShadowRoot ||
|
|
aAssociationMode == NotOwnedByDocumentOrShadowRoot);
|
|
|
|
// not ref counted
|
|
mDocumentOrShadowRoot = aDocOrShadowRoot;
|
|
mAssociationMode = 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->SetAssociatedDocumentOrShadowRoot(aDocOrShadowRoot, aAssociationMode);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
StyleSheet::PrependStyleSheet(StyleSheet* aSheet)
|
|
{
|
|
WillDirty();
|
|
PrependStyleSheetSilently(aSheet);
|
|
}
|
|
|
|
void
|
|
StyleSheet::PrependStyleSheetSilently(StyleSheet* aSheet)
|
|
{
|
|
MOZ_ASSERT(aSheet);
|
|
|
|
aSheet->mNext = Inner().mFirstChild;
|
|
Inner().mFirstChild = aSheet;
|
|
|
|
// This is not reference counted. Our parent tells us when
|
|
// it's going away.
|
|
aSheet->mParent = this;
|
|
aSheet->SetAssociatedDocumentOrShadowRoot(mDocumentOrShadowRoot, mAssociationMode);
|
|
}
|
|
|
|
size_t
|
|
StyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
size_t n = 0;
|
|
const StyleSheet* s = this;
|
|
while (s) {
|
|
n += aMallocSizeOf(s);
|
|
|
|
// See the comment in CSSStyleSheet::SizeOfIncludingThis() for an
|
|
// explanation of this.
|
|
//
|
|
// FIXME(emilio): This comment is gone, someone should go find it.
|
|
if (s->Inner().mSheets.LastElement() == s) {
|
|
n += s->Inner().SizeOfIncludingThis(aMallocSizeOf);
|
|
}
|
|
|
|
// Measurement of the following members may be added later if DMD finds it
|
|
// is worthwhile:
|
|
// - s->mTitle
|
|
// - s->mMedia
|
|
// - s->mStyleSets
|
|
// - s->mRuleList
|
|
|
|
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)
|
|
{
|
|
if (aMedia) {
|
|
aMedia->SetStyleSheet(this);
|
|
}
|
|
mMedia = aMedia;
|
|
}
|
|
|
|
void
|
|
StyleSheet::SetReferrerPolicy(net::ReferrerPolicy aReferrerPolicy)
|
|
{
|
|
Inner().mReferrerPolicy = aReferrerPolicy;
|
|
}
|
|
|
|
void
|
|
StyleSheet::DropMedia()
|
|
{
|
|
if (mMedia) {
|
|
mMedia->SetStyleSheet(nullptr);
|
|
mMedia = nullptr;
|
|
}
|
|
}
|
|
|
|
dom::MediaList*
|
|
StyleSheet::Media()
|
|
{
|
|
if (!mMedia) {
|
|
mMedia = dom::MediaList::Create(nsString());
|
|
mMedia->SetStyleSheet(this);
|
|
}
|
|
|
|
return mMedia;
|
|
}
|
|
|
|
// nsWrapperCache
|
|
|
|
JSObject*
|
|
StyleSheet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return dom::CSSStyleSheet_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
/* static */ bool
|
|
StyleSheet::RuleHasPendingChildSheet(css::Rule* aRule)
|
|
{
|
|
MOZ_ASSERT(aRule->Type() == dom::CSSRule_Binding::IMPORT_RULE);
|
|
auto rule = static_cast<dom::CSSImportRule*>(aRule);
|
|
if (StyleSheet* childSheet = rule->GetStyleSheet()) {
|
|
return !childSheet->IsComplete();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
StyleSheet::BuildChildListAfterInnerClone()
|
|
{
|
|
MOZ_ASSERT(Inner().mSheets.Length() == 1, "Should've just cloned");
|
|
MOZ_ASSERT(Inner().mSheets[0] == this);
|
|
MOZ_ASSERT(!Inner().mFirstChild);
|
|
|
|
auto* contents = Inner().mContents.get();
|
|
RefPtr<ServoCssRules> rules =
|
|
Servo_StyleSheet_GetRules(contents).Consume();
|
|
|
|
uint32_t index = 0;
|
|
while (true) {
|
|
uint32_t line, column; // Actually unused.
|
|
RefPtr<RawServoImportRule> import =
|
|
Servo_CssRules_GetImportRuleAt(rules, index, &line, &column).Consume();
|
|
if (!import) {
|
|
// Note that only @charset rules come before @import rules, and @charset
|
|
// rules are parsed but skipped, so we can stop iterating as soon as we
|
|
// find something that isn't an @import rule.
|
|
break;
|
|
}
|
|
auto* sheet =
|
|
const_cast<StyleSheet*>(Servo_ImportRule_GetSheet(import));
|
|
MOZ_ASSERT(sheet);
|
|
PrependStyleSheetSilently(sheet);
|
|
index++;
|
|
}
|
|
}
|
|
|
|
already_AddRefed<StyleSheet>
|
|
StyleSheet::CreateEmptyChildSheet(
|
|
already_AddRefed<dom::MediaList> aMediaList) const
|
|
{
|
|
RefPtr<StyleSheet> child =
|
|
new StyleSheet(ParsingMode(),
|
|
CORSMode::CORS_NONE,
|
|
GetReferrerPolicy(),
|
|
SRIMetadata());
|
|
|
|
child->mMedia = aMediaList;
|
|
return child.forget();
|
|
}
|
|
|
|
// We disable parallel stylesheet parsing if any of the following three
|
|
// conditions hold:
|
|
//
|
|
// (1) The pref is off.
|
|
// (2) The browser is recording CSS errors (which parallel parsing can't handle).
|
|
// (3) The stylesheet is a chrome stylesheet, since those can use -moz-bool-pref,
|
|
// which needs to access the pref service, which is not threadsafe.
|
|
static bool
|
|
AllowParallelParse(css::Loader* aLoader, nsIURI* aSheetURI)
|
|
{
|
|
// Check the pref.
|
|
if (!StaticPrefs::layout_css_parsing_parallel()) {
|
|
return false;
|
|
}
|
|
|
|
// If the browser is recording CSS errors, we need to use the sequential path
|
|
// because the parallel path doesn't support that.
|
|
nsIDocument* doc = aLoader->GetDocument();
|
|
if (doc && css::ErrorReporter::ShouldReportErrors(*doc)) {
|
|
return false;
|
|
}
|
|
|
|
// If this is a chrome stylesheet, it might use -moz-bool-pref, which needs to
|
|
// access the pref service, which is not thread-safe. We could probably expose
|
|
// the relevant booleans as thread-safe var caches if we needed to, but
|
|
// parsing chrome stylesheets in parallel is unlikely to be a win anyway.
|
|
//
|
|
// Note that UA stylesheets can also use -moz-bool-pref, but those are always
|
|
// parsed sync.
|
|
if (dom::IsChromeURI(aSheetURI)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
RefPtr<StyleSheetParsePromise>
|
|
StyleSheet::ParseSheet(css::Loader* aLoader,
|
|
const nsACString& aBytes,
|
|
css::SheetLoadData* aLoadData)
|
|
{
|
|
MOZ_ASSERT(aLoader);
|
|
MOZ_ASSERT(aLoadData);
|
|
MOZ_ASSERT(mParsePromise.IsEmpty());
|
|
RefPtr<StyleSheetParsePromise> p = mParsePromise.Ensure(__func__);
|
|
Inner().mURLData = CreateURLExtraData(); // RefPtr
|
|
|
|
const StyleUseCounters* useCounters = aLoader->GetDocument()
|
|
? aLoader->GetDocument()->GetStyleUseCounters()
|
|
: nullptr;
|
|
|
|
if (!AllowParallelParse(aLoader, GetSheetURI())) {
|
|
RefPtr<RawServoStyleSheetContents> contents =
|
|
Servo_StyleSheet_FromUTF8Bytes(aLoader,
|
|
this,
|
|
aLoadData,
|
|
&aBytes,
|
|
mParsingMode,
|
|
Inner().mURLData,
|
|
aLoadData->mLineNumber,
|
|
aLoader->GetCompatibilityMode(),
|
|
/* reusable_sheets = */ nullptr,
|
|
useCounters)
|
|
.Consume();
|
|
FinishAsyncParse(contents.forget());
|
|
} else {
|
|
RefPtr<css::SheetLoadDataHolder> loadDataHolder =
|
|
new css::SheetLoadDataHolder(__func__, aLoadData);
|
|
Servo_StyleSheet_FromUTF8BytesAsync(loadDataHolder,
|
|
Inner().mURLData,
|
|
&aBytes,
|
|
mParsingMode,
|
|
aLoadData->mLineNumber,
|
|
aLoader->GetCompatibilityMode(),
|
|
/* should_record_counters = */ !!useCounters);
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
void
|
|
StyleSheet::FinishAsyncParse(already_AddRefed<RawServoStyleSheetContents> aSheetContents)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!mParsePromise.IsEmpty());
|
|
Inner().mContents = aSheetContents;
|
|
FinishParse();
|
|
mParsePromise.Resolve(true, __func__);
|
|
}
|
|
|
|
|
|
void
|
|
StyleSheet::ParseSheetSync(css::Loader* aLoader,
|
|
const nsACString& aBytes,
|
|
css::SheetLoadData* aLoadData,
|
|
uint32_t aLineNumber,
|
|
css::LoaderReusableStyleSheets* aReusableSheets)
|
|
{
|
|
nsCompatibility compatMode =
|
|
aLoader ? aLoader->GetCompatibilityMode() : eCompatibility_FullStandards;
|
|
|
|
const StyleUseCounters* useCounters = aLoader && aLoader->GetDocument()
|
|
? aLoader->GetDocument()->GetStyleUseCounters()
|
|
: nullptr;
|
|
|
|
Inner().mURLData = CreateURLExtraData(); // RefPtr
|
|
Inner().mContents = Servo_StyleSheet_FromUTF8Bytes(aLoader,
|
|
this,
|
|
aLoadData,
|
|
&aBytes,
|
|
mParsingMode,
|
|
Inner().mURLData,
|
|
aLineNumber,
|
|
compatMode,
|
|
aReusableSheets,
|
|
useCounters)
|
|
.Consume();
|
|
|
|
FinishParse();
|
|
}
|
|
|
|
void
|
|
StyleSheet::FinishParse()
|
|
{
|
|
nsString sourceMapURL;
|
|
Servo_StyleSheet_GetSourceMapURL(Inner().mContents, &sourceMapURL);
|
|
SetSourceMapURLFromComment(sourceMapURL);
|
|
|
|
nsString sourceURL;
|
|
Servo_StyleSheet_GetSourceURL(Inner().mContents, &sourceURL);
|
|
SetSourceURL(sourceURL);
|
|
}
|
|
|
|
nsresult
|
|
StyleSheet::ReparseSheet(const nsAString& aInput)
|
|
{
|
|
if (!IsComplete()) {
|
|
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
|
}
|
|
|
|
// Hold strong ref to the CSSLoader in case the document update
|
|
// kills the document
|
|
RefPtr<css::Loader> loader;
|
|
if (nsIDocument* doc = GetAssociatedDocument()) {
|
|
loader = doc->CSSLoader();
|
|
NS_ASSERTION(loader, "Document with no CSS loader!");
|
|
} else {
|
|
loader = new css::Loader;
|
|
}
|
|
|
|
WillDirty();
|
|
|
|
// cache child sheets to reuse
|
|
css::LoaderReusableStyleSheets reusableSheets;
|
|
for (StyleSheet* child = GetFirstChild(); child; child = child->mNext) {
|
|
if (child->GetOriginalURI()) {
|
|
reusableSheets.AddReusableSheet(child);
|
|
}
|
|
}
|
|
|
|
// clean up child sheets list
|
|
for (StyleSheet* child = GetFirstChild(); child; ) {
|
|
StyleSheet* next = child->mNext;
|
|
child->mParent = nullptr;
|
|
child->ClearAssociatedDocumentOrShadowRoot();
|
|
child->mNext = nullptr;
|
|
child = next;
|
|
}
|
|
Inner().mFirstChild = nullptr;
|
|
|
|
uint32_t lineNumber = 1;
|
|
if (mOwningNode) {
|
|
nsCOMPtr<nsIStyleSheetLinkingElement> link = do_QueryInterface(mOwningNode);
|
|
if (link) {
|
|
lineNumber = link->GetLineNumber();
|
|
}
|
|
}
|
|
|
|
// Notify to the stylesets about the old rules going away.
|
|
{
|
|
ServoCSSRuleList* ruleList = GetCssRulesInternal();
|
|
MOZ_ASSERT(ruleList);
|
|
|
|
uint32_t ruleCount = ruleList->Length();
|
|
for (uint32_t i = 0; i < ruleCount; ++i) {
|
|
css::Rule* rule = ruleList->GetRule(i);
|
|
MOZ_ASSERT(rule);
|
|
if (rule->Type() == dom::CSSRule_Binding::IMPORT_RULE &&
|
|
RuleHasPendingChildSheet(rule)) {
|
|
continue; // notify when loaded (see StyleSheetLoaded)
|
|
}
|
|
RuleRemoved(*rule);
|
|
}
|
|
}
|
|
|
|
DropRuleList();
|
|
|
|
ParseSheetSync(loader,
|
|
NS_ConvertUTF16toUTF8(aInput),
|
|
/* aLoadData = */ nullptr,
|
|
lineNumber,
|
|
&reusableSheets);
|
|
|
|
// Notify the stylesets about the new rules.
|
|
{
|
|
// Get the rule list (which will need to be regenerated after ParseSheet).
|
|
ServoCSSRuleList* ruleList = GetCssRulesInternal();
|
|
MOZ_ASSERT(ruleList);
|
|
|
|
uint32_t ruleCount = ruleList->Length();
|
|
for (uint32_t i = 0; i < ruleCount; ++i) {
|
|
css::Rule* rule = ruleList->GetRule(i);
|
|
MOZ_ASSERT(rule);
|
|
if (rule->Type() == CSSRule_Binding::IMPORT_RULE &&
|
|
RuleHasPendingChildSheet(rule)) {
|
|
continue; // notify when loaded (see StyleSheetLoaded)
|
|
}
|
|
|
|
RuleAdded(*rule);
|
|
}
|
|
}
|
|
|
|
// Our rules are no longer considered modified.
|
|
ClearModifiedRules();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsICSSLoaderObserver implementation
|
|
NS_IMETHODIMP
|
|
StyleSheet::StyleSheetLoaded(StyleSheet* aSheet,
|
|
bool aWasAlternate,
|
|
nsresult aStatus)
|
|
{
|
|
if (!aSheet->GetParentSheet()) {
|
|
return NS_OK; // ignore if sheet has been detached already
|
|
}
|
|
NS_ASSERTION(this == aSheet->GetParentSheet(),
|
|
"We are being notified of a sheet load for a sheet that is not our child!");
|
|
|
|
if (NS_SUCCEEDED(aStatus)) {
|
|
RuleAdded(*aSheet->GetOwnerRule());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
StyleSheet::DropRuleList()
|
|
{
|
|
if (mRuleList) {
|
|
mRuleList->DropReferences();
|
|
mRuleList = nullptr;
|
|
}
|
|
}
|
|
|
|
already_AddRefed<StyleSheet>
|
|
StyleSheet::Clone(StyleSheet* aCloneParent,
|
|
dom::CSSImportRule* aCloneOwnerRule,
|
|
dom::DocumentOrShadowRoot* aCloneDocumentOrShadowRoot,
|
|
nsINode* aCloneOwningNode) const
|
|
{
|
|
RefPtr<StyleSheet> clone =
|
|
new StyleSheet(*this,
|
|
aCloneParent,
|
|
aCloneOwnerRule,
|
|
aCloneDocumentOrShadowRoot,
|
|
aCloneOwningNode);
|
|
return clone.forget();
|
|
}
|
|
|
|
ServoCSSRuleList*
|
|
StyleSheet::GetCssRulesInternal()
|
|
{
|
|
if (!mRuleList) {
|
|
EnsureUniqueInner();
|
|
|
|
RefPtr<ServoCssRules> rawRules =
|
|
Servo_StyleSheet_GetRules(Inner().mContents).Consume();
|
|
MOZ_ASSERT(rawRules);
|
|
mRuleList = new ServoCSSRuleList(rawRules.forget(), this, nullptr);
|
|
}
|
|
return mRuleList;
|
|
}
|
|
|
|
uint32_t
|
|
StyleSheet::InsertRuleInternal(const nsAString& aRule,
|
|
uint32_t aIndex,
|
|
ErrorResult& aRv)
|
|
{
|
|
// Ensure mRuleList is constructed.
|
|
GetCssRulesInternal();
|
|
|
|
aRv = mRuleList->InsertRule(aRule, aIndex);
|
|
if (aRv.Failed()) {
|
|
return 0;
|
|
}
|
|
|
|
// XXX We may not want to get the rule when stylesheet change event
|
|
// is not enabled.
|
|
css::Rule* rule = mRuleList->GetRule(aIndex);
|
|
if (rule->Type() != CSSRule_Binding::IMPORT_RULE ||
|
|
!RuleHasPendingChildSheet(rule)) {
|
|
RuleAdded(*rule);
|
|
}
|
|
|
|
return aIndex;
|
|
}
|
|
|
|
void
|
|
StyleSheet::DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv)
|
|
{
|
|
// Ensure mRuleList is constructed.
|
|
GetCssRulesInternal();
|
|
if (aIndex >= mRuleList->Length()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
|
return;
|
|
}
|
|
|
|
// Hold a strong ref to the rule so it doesn't die when we remove it
|
|
// from the list. XXX We may not want to hold it if stylesheet change
|
|
// event is not enabled.
|
|
RefPtr<css::Rule> rule = mRuleList->GetRule(aIndex);
|
|
aRv = mRuleList->DeleteRule(aIndex);
|
|
MOZ_ASSERT(!aRv.ErrorCodeIs(NS_ERROR_DOM_INDEX_SIZE_ERR),
|
|
"IndexSizeError should have been handled earlier");
|
|
if (!aRv.Failed()) {
|
|
RuleRemoved(*rule);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
StyleSheet::InsertRuleIntoGroupInternal(const nsAString& aRule,
|
|
css::GroupRule* aGroup,
|
|
uint32_t aIndex)
|
|
{
|
|
auto rules = static_cast<ServoCSSRuleList*>(aGroup->CssRules());
|
|
MOZ_ASSERT(rules->GetParentRule() == aGroup);
|
|
return rules->InsertRule(aRule, aIndex);
|
|
}
|
|
|
|
OriginFlags
|
|
StyleSheet::GetOrigin()
|
|
{
|
|
return static_cast<OriginFlags>(
|
|
Servo_StyleSheet_GetOrigin(Inner().mContents));
|
|
}
|
|
|
|
} // namespace mozilla
|