/* -*- 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/. */ /* rules in a CSS stylesheet other than style rules (e.g., @import rules) */ #include "nsCSSRules.h" #include "nsCSSFontFaceRule.h" #include "mozilla/Attributes.h" #include "nsCSSValue.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/MemoryReporting.h" #include "mozilla/css/ImportRule.h" #include "mozilla/css/NameSpaceRule.h" #include "nsString.h" #include "nsAtom.h" #include "nsCSSProps.h" #include "nsCOMPtr.h" #include "nsIDOMCSSStyleSheet.h" #include "nsMediaList.h" #include "mozilla/dom/CSSRuleList.h" #include "nsIDocument.h" #include "nsPresContext.h" #include "nsContentUtils.h" #include "nsError.h" #include "nsStyleUtil.h" #include "mozilla/DeclarationBlockInlines.h" #include "nsCSSParser.h" #include "nsDOMClassInfoID.h" #include "mozilla/dom/CSSStyleDeclarationBinding.h" #include "mozilla/dom/CSSFontFeatureValuesRuleBinding.h" #include "StyleRule.h" #include "nsFont.h" #include "nsIURI.h" #include "nsCCUncollectableMarker.h" #include "nsWrapperCacheInlines.h" using namespace mozilla; using namespace mozilla::dom; // base class for all rule types in a CSS style sheet namespace mozilla { namespace css { NS_IMPL_CYCLE_COLLECTING_ADDREF(Rule) NS_IMPL_CYCLE_COLLECTING_RELEASE(Rule) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Rule) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Rule) bool Rule::IsCCLeaf() const { return !PreservingWrapper(); } bool Rule::IsKnownLive() const { if (HasKnownLiveWrapper()) { return true; } StyleSheet* sheet = GetStyleSheet(); if (!sheet) { return false; } if (!sheet->IsOwnedByDocument()) { return false; } return nsCCUncollectableMarker::InGeneration( sheet->GetAssociatedDocument()->GetMarkedCCGeneration()); } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Rule) return tmp->IsCCLeaf() || tmp->IsKnownLive(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Rule) // Please see documentation for nsCycleCollectionParticipant::CanSkip* for why // we need to check HasNothingToTrace here but not in the other two CanSkip // methods. return tmp->IsCCLeaf() || (tmp->IsKnownLive() && tmp->HasNothingToTrace(tmp)); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Rule) return tmp->IsCCLeaf() || tmp->IsKnownLive(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END /* virtual */ void Rule::SetStyleSheet(StyleSheet* aSheet) { // We don't reference count this up reference. The style sheet // will tell us when it's going away or when we're detached from // it. mSheet = aSheet; } NS_IMETHODIMP Rule::GetParentRule(nsIDOMCSSRule** aParentRule) { NS_IF_ADDREF(*aParentRule = mParentRule); return NS_OK; } NS_IMETHODIMP Rule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet) { NS_ENSURE_ARG_POINTER(aSheet); NS_IF_ADDREF(*aSheet = GetStyleSheet()); return NS_OK; } /* virtual */ css::Rule* Rule::GetCSSRule() { return this; } NS_IMETHODIMP Rule::GetType(uint16_t* aType) { *aType = Type(); return NS_OK; } NS_IMETHODIMP Rule::SetCssText(const nsAString& aCssText) { // We used to throw for some rule types, but not all. Specifically, we did // not throw for StyleRule. Let's just always not throw. return NS_OK; } NS_IMETHODIMP Rule::GetCssText(nsAString& aCssText) { GetCssTextImpl(aCssText); return NS_OK; } Rule* Rule::GetParentRule() const { return mParentRule; } // ------------------------------------------- // ImportRule // ImportRule::ImportRule(nsMediaList* aMedia, const nsString& aURLSpec, uint32_t aLineNumber, uint32_t aColumnNumber) : CSSImportRule(aLineNumber, aColumnNumber) , mURLSpec(aURLSpec) , mMedia(aMedia) { MOZ_ASSERT(aMedia); // XXXbz This is really silly.... the mMedia here will be replaced // with itself if we manage to load a sheet. Which should really // never fail nowadays, in sane cases. } ImportRule::ImportRule(const ImportRule& aCopy) : CSSImportRule(aCopy), mURLSpec(aCopy.mURLSpec) { // Whether or not an @import rule has a null sheet is a permanent // property of that @import rule, since it is null only if the target // sheet failed security checks. if (aCopy.mChildSheet) { RefPtr sheet = aCopy.mChildSheet->Clone(nullptr, this, nullptr, nullptr); SetSheet(static_cast(sheet.get())); // SetSheet sets mMedia appropriately } else { // We better just copy mMedia from aCopy, since we have nowhere else to get // one. mMedia = aCopy.mMedia; } } ImportRule::~ImportRule() { if (mChildSheet) { mChildSheet->SetOwnerRule(nullptr); } } NS_IMPL_ADDREF_INHERITED(ImportRule, CSSImportRule) NS_IMPL_RELEASE_INHERITED(ImportRule, CSSImportRule) NS_IMPL_CYCLE_COLLECTION_INHERITED(ImportRule, CSSImportRule, mMedia, mChildSheet) // QueryInterface implementation for ImportRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImportRule) NS_INTERFACE_MAP_END_INHERITING(CSSImportRule) #ifdef DEBUG /* virtual */ void ImportRule::List(FILE* out, int32_t aIndent) const { nsAutoCString str; // Indent for (int32_t indent = aIndent; --indent >= 0; ) { str.AppendLiteral(" "); } str.AppendLiteral("@import \""); AppendUTF16toUTF8(mURLSpec, str); str.AppendLiteral("\" "); nsAutoString mediaText; mMedia->GetText(mediaText); AppendUTF16toUTF8(mediaText, str); str.AppendLiteral("\n"); fprintf_stderr(out, "%s", str.get()); } #endif /* virtual */ already_AddRefed ImportRule::Clone() const { RefPtr clone = new ImportRule(*this); return clone.forget(); } void ImportRule::SetSheet(CSSStyleSheet* aSheet) { NS_PRECONDITION(aSheet, "null arg"); // set the new sheet mChildSheet = aSheet; aSheet->SetOwnerRule(this); // set our medialist to be the same as the sheet's medialist mMedia = static_cast(mChildSheet->Media()); } void ImportRule::GetCssTextImpl(nsAString& aCssText) const { aCssText.AssignLiteral("@import url("); nsStyleUtil::AppendEscapedCSSString(mURLSpec, aCssText); aCssText.Append(')'); if (mMedia) { nsAutoString mediaText; mMedia->GetText(mediaText); if (!mediaText.IsEmpty()) { aCssText.Append(' '); aCssText.Append(mediaText); } } aCssText.Append(';'); } MediaList* ImportRule::GetMedia() const { return mMedia; } StyleSheet* ImportRule::GetStyleSheet() const { return mChildSheet; } NS_IMETHODIMP ImportRule::GetHref(nsAString & aHref) { aHref = mURLSpec; return NS_OK; } /* virtual */ size_t ImportRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this); // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - mURLSpec // // The following members are not measured: // - mMedia, because it is measured via CSSStyleSheet::mMedia // - mChildSheet, because it is measured via CSSStyleSheetInner::mSheets } // ------------------------------------------- // nsICSSMediaRule // MediaRule::MediaRule(uint32_t aLineNumber, uint32_t aColumnNumber) : CSSMediaRule(aLineNumber, aColumnNumber) { } MediaRule::MediaRule(const MediaRule& aCopy) : CSSMediaRule(aCopy) { if (aCopy.mMedia) { mMedia = aCopy.mMedia->Clone().downcast(); // XXXldb This doesn't really make sense. mMedia->SetStyleSheet(aCopy.GetStyleSheet()); } } MediaRule::~MediaRule() { if (mMedia) { mMedia->SetStyleSheet(nullptr); } } NS_IMPL_ADDREF_INHERITED(MediaRule, CSSMediaRule) NS_IMPL_RELEASE_INHERITED(MediaRule, CSSMediaRule) // QueryInterface implementation for MediaRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaRule) NS_INTERFACE_MAP_END_INHERITING(CSSMediaRule) NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaRule, CSSMediaRule, mMedia) /* virtual */ void MediaRule::SetStyleSheet(StyleSheet* aSheet) { if (mMedia) { // Set to null so it knows it's leaving one sheet and joining another. mMedia->SetStyleSheet(nullptr); if (aSheet) { mMedia->SetStyleSheet(aSheet->AsGecko()); } } GroupRule::SetStyleSheet(aSheet); } #ifdef DEBUG /* virtual */ void MediaRule::List(FILE* out, int32_t aIndent) const { nsAutoCString indentStr; for (int32_t indent = aIndent; --indent >= 0; ) { indentStr.AppendLiteral(" "); } nsAutoCString str(indentStr); str.AppendLiteral("@media "); if (mMedia) { nsAutoString mediaText; mMedia->GetText(mediaText); AppendUTF16toUTF8(mediaText, str); } str.AppendLiteral(" {\n"); fprintf_stderr(out, "%s", str.get()); GroupRule::List(out, aIndent); fprintf_stderr(out, "%s}\n", indentStr.get()); } #endif /* virtual */ already_AddRefed MediaRule::Clone() const { RefPtr clone = new MediaRule(*this); return clone.forget(); } nsresult MediaRule::SetMedia(nsMediaList* aMedia) { mMedia = aMedia; if (aMedia) mMedia->SetStyleSheet(GetStyleSheet()); return NS_OK; } MediaList* MediaRule::Media() { // In practice, if we end up being parsed at all, we have non-null mMedia. So // it's OK to claim we don't return null here. return mMedia; } void MediaRule::GetCssTextImpl(nsAString& aCssText) const { aCssText.AssignLiteral("@media "); AppendConditionText(aCssText); GroupRule::AppendRulesToCssText(aCssText); } // nsIDOMCSSConditionRule methods NS_IMETHODIMP MediaRule::GetConditionText(nsAString& aConditionText) { aConditionText.Truncate(0); AppendConditionText(aConditionText); return NS_OK; } NS_IMETHODIMP MediaRule::SetConditionText(const nsAString& aConditionText) { if (!mMedia) { RefPtr media = new nsMediaList(); media->SetStyleSheet(GetStyleSheet()); nsresult rv = media->SetMediaText(aConditionText); if (NS_SUCCEEDED(rv)) { mMedia = media; } return rv; } return mMedia->SetMediaText(aConditionText); } // GroupRule interface /* virtual */ bool MediaRule::UseForPresentation(nsPresContext* aPresContext, nsMediaQueryResultCacheKey& aKey) { if (mMedia) { MOZ_ASSERT(aPresContext); return mMedia->Matches(aPresContext, &aKey); } return true; } /* virtual */ size_t MediaRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); n += GroupRule::SizeOfExcludingThis(aMallocSizeOf); // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - mMedia return n; } void MediaRule::AppendConditionText(nsAString& aOutput) const { if (mMedia) { nsAutoString mediaText; mMedia->GetText(mediaText); aOutput.Append(mediaText); } } DocumentRule::DocumentRule(uint32_t aLineNumber, uint32_t aColumnNumber) : dom::CSSMozDocumentRule(aLineNumber, aColumnNumber) { } DocumentRule::DocumentRule(const DocumentRule& aCopy) : dom::CSSMozDocumentRule(aCopy) , mURLs(new URL(*aCopy.mURLs)) { } DocumentRule::~DocumentRule() { } NS_IMPL_ADDREF_INHERITED(DocumentRule, ConditionRule) NS_IMPL_RELEASE_INHERITED(DocumentRule, ConditionRule) // QueryInterface implementation for DocumentRule NS_INTERFACE_MAP_BEGIN(DocumentRule) NS_INTERFACE_MAP_END_INHERITING(dom::CSSMozDocumentRule) #ifdef DEBUG /* virtual */ void DocumentRule::List(FILE* out, int32_t aIndent) const { nsAutoCString indentStr; for (int32_t indent = aIndent; --indent >= 0; ) { indentStr.AppendLiteral(" "); } nsAutoCString str; str.AppendLiteral("@-moz-document "); for (URL *url = mURLs; url; url = url->next) { switch (url->func) { case URLMatchingFunction::eURL: str.AppendLiteral("url(\""); break; case URLMatchingFunction::eURLPrefix: str.AppendLiteral("url-prefix(\""); break; case URLMatchingFunction::eDomain: str.AppendLiteral("domain(\""); break; case URLMatchingFunction::eRegExp: str.AppendLiteral("regexp(\""); break; } nsAutoCString escapedURL(url->url); escapedURL.ReplaceSubstring("\"", "\\\""); // escape quotes str.Append(escapedURL); str.AppendLiteral("\"), "); } str.Cut(str.Length() - 2, 1); // remove last , fprintf_stderr(out, "%s%s {\n", indentStr.get(), str.get()); GroupRule::List(out, aIndent); fprintf_stderr(out, "%s}\n", indentStr.get()); } #endif /* virtual */ already_AddRefed DocumentRule::Clone() const { RefPtr clone = new DocumentRule(*this); return clone.forget(); } void DocumentRule::GetCssTextImpl(nsAString& aCssText) const { aCssText.AssignLiteral("@-moz-document "); AppendConditionText(aCssText); GroupRule::AppendRulesToCssText(aCssText); } // nsIDOMCSSConditionRule methods NS_IMETHODIMP DocumentRule::GetConditionText(nsAString& aConditionText) { aConditionText.Truncate(0); AppendConditionText(aConditionText); return NS_OK; } NS_IMETHODIMP DocumentRule::SetConditionText(const nsAString& aConditionText) { return NS_ERROR_NOT_IMPLEMENTED; } // GroupRule interface /* virtual */ bool DocumentRule::UseForPresentation(nsPresContext* aPresContext, nsMediaQueryResultCacheKey& aKey) { return UseForPresentation(aPresContext); } bool DocumentRule::UseForPresentation(nsPresContext* aPresContext) { nsIDocument *doc = aPresContext->Document(); nsIURI *docURI = doc->GetDocumentURI(); nsAutoCString docURISpec; if (docURI) { // If GetSpec fails (due to OOM) just skip these URI-specific CSS rules. nsresult rv = docURI->GetSpec(docURISpec); NS_ENSURE_SUCCESS(rv, false); } for (URL *url = mURLs; url; url = url->next) { if (Match(doc, docURI, docURISpec, url->url, url->func)) { return true; } } return false; } DocumentRule::URL::~URL() { NS_CSS_DELETE_LIST_MEMBER(DocumentRule::URL, this, next); } /* virtual */ size_t DocumentRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); n += GroupRule::SizeOfExcludingThis(aMallocSizeOf); // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - mURLs return n; } void DocumentRule::AppendConditionText(nsAString& aCssText) const { for (URL *url = mURLs; url; url = url->next) { switch (url->func) { case URLMatchingFunction::eURL: aCssText.AppendLiteral("url("); break; case URLMatchingFunction::eURLPrefix: aCssText.AppendLiteral("url-prefix("); break; case URLMatchingFunction::eDomain: aCssText.AppendLiteral("domain("); break; case URLMatchingFunction::eRegExp: aCssText.AppendLiteral("regexp("); break; } nsStyleUtil::AppendEscapedCSSString(NS_ConvertUTF8toUTF16(url->url), aCssText); aCssText.AppendLiteral("), "); } aCssText.Truncate(aCssText.Length() - 2); // remove last ", " } // ------------------------------------------- // NameSpaceRule // NameSpaceRule::NameSpaceRule(nsAtom* aPrefix, const nsString& aURLSpec, uint32_t aLineNumber, uint32_t aColumnNumber) : CSSNamespaceRule(aLineNumber, aColumnNumber), mPrefix(aPrefix), mURLSpec(aURLSpec) { } NameSpaceRule::NameSpaceRule(const NameSpaceRule& aCopy) : CSSNamespaceRule(aCopy), mPrefix(aCopy.mPrefix), mURLSpec(aCopy.mURLSpec) { } NameSpaceRule::~NameSpaceRule() { } NS_IMPL_ADDREF_INHERITED(NameSpaceRule, CSSNamespaceRule) NS_IMPL_RELEASE_INHERITED(NameSpaceRule, CSSNamespaceRule) // QueryInterface implementation for NameSpaceRule // If this ever gets its own cycle-collection bits, reevaluate our IsCCLeaf // implementation. NS_INTERFACE_MAP_BEGIN(NameSpaceRule) if (aIID.Equals(NS_GET_IID(css::NameSpaceRule))) { *aInstancePtr = this; NS_ADDREF_THIS(); return NS_OK; } else NS_INTERFACE_MAP_END_INHERITING(CSSNamespaceRule) #ifdef DEBUG /* virtual */ void NameSpaceRule::List(FILE* out, int32_t aIndent) const { nsAutoCString str; for (int32_t indent = aIndent; --indent >= 0; ) { str.AppendLiteral(" "); } nsAutoString buffer; str.AppendLiteral("@namespace "); if (mPrefix) { mPrefix->ToString(buffer); AppendUTF16toUTF8(buffer, str); str.Append(' '); } str.AppendLiteral("url(\""); AppendUTF16toUTF8(mURLSpec, str); str.AppendLiteral("\")\n"); fprintf_stderr(out, "%s", str.get()); } #endif /* virtual */ already_AddRefed NameSpaceRule::Clone() const { RefPtr clone = new NameSpaceRule(*this); return clone.forget(); } void NameSpaceRule::GetCssTextImpl(nsAString& aCssText) const { aCssText.AssignLiteral("@namespace "); if (mPrefix) { aCssText.Append(nsDependentAtomString(mPrefix) + NS_LITERAL_STRING(" ")); } aCssText.AppendLiteral("url("); nsStyleUtil::AppendEscapedCSSString(mURLSpec, aCssText); aCssText.AppendLiteral(");"); } /* virtual */ size_t NameSpaceRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this); // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - mPrefix // - mURLSpec } } // namespace css } // namespace mozilla // ----------------------------------- // nsCSSFontFeatureValuesRule // /* virtual */ already_AddRefed nsCSSFontFeatureValuesRule::Clone() const { RefPtr clone = new nsCSSFontFeatureValuesRule(*this); return clone.forget(); } NS_IMPL_ADDREF_INHERITED(nsCSSFontFeatureValuesRule, dom::CSSFontFeatureValuesRule) NS_IMPL_RELEASE_INHERITED(nsCSSFontFeatureValuesRule, dom::CSSFontFeatureValuesRule) // QueryInterface implementation for nsCSSFontFeatureValuesRule // If this ever gets its own cycle-collection bits, reevaluate our IsCCLeaf // implementation. NS_INTERFACE_MAP_BEGIN(nsCSSFontFeatureValuesRule) NS_INTERFACE_MAP_END_INHERITING(dom::CSSFontFeatureValuesRule) static void FeatureValuesToString( const nsTArray& aFeatureValues, nsAString& aOutStr) { uint32_t i, n; // append values n = aFeatureValues.Length(); for (i = 0; i < n; i++) { const gfxFontFeatureValueSet::FeatureValues& fv = aFeatureValues[i]; // @alternate aOutStr.AppendLiteral(" @"); nsAutoString functAlt; nsStyleUtil::GetFunctionalAlternatesName(fv.alternate, functAlt); aOutStr.Append(functAlt); aOutStr.AppendLiteral(" {"); // for each ident-values tuple uint32_t j, numValues = fv.valuelist.Length(); for (j = 0; j < numValues; j++) { aOutStr.Append(' '); const gfxFontFeatureValueSet::ValueList& vlist = fv.valuelist[j]; nsStyleUtil::AppendEscapedCSSIdent(vlist.name, aOutStr); aOutStr.Append(':'); uint32_t k, numSelectors = vlist.featureSelectors.Length(); for (k = 0; k < numSelectors; k++) { aOutStr.Append(' '); aOutStr.AppendInt(vlist.featureSelectors[k]); } aOutStr.Append(';'); } aOutStr.AppendLiteral(" }\n"); } } static void FontFeatureValuesRuleToString( mozilla::SharedFontList* aFamilyList, const nsTArray& aFeatureValues, nsAString& aOutStr) { aOutStr.AssignLiteral("@font-feature-values "); nsAutoString familyListStr, valueTextStr; nsStyleUtil::AppendEscapedCSSFontFamilyList(aFamilyList, familyListStr); aOutStr.Append(familyListStr); aOutStr.AppendLiteral(" {\n"); FeatureValuesToString(aFeatureValues, valueTextStr); aOutStr.Append(valueTextStr); aOutStr.Append('}'); } #ifdef DEBUG void nsCSSFontFeatureValuesRule::List(FILE* out, int32_t aIndent) const { nsAutoString text; FontFeatureValuesRuleToString(mFamilyList, mFeatureValues, text); NS_ConvertUTF16toUTF8 utf8(text); // replace newlines with newlines plus indent spaces char* indent = new char[(aIndent + 1) * 2]; int32_t i; for (i = 1; i < (aIndent + 1) * 2 - 1; i++) { indent[i] = 0x20; } indent[0] = 0xa; indent[aIndent * 2 + 1] = 0; utf8.ReplaceSubstring("\n", indent); delete [] indent; nsAutoCString indentStr; for (i = aIndent; --i >= 0; ) { indentStr.AppendLiteral(" "); } fprintf_stderr(out, "%s%s\n", indentStr.get(), utf8.get()); } #endif NS_IMETHODIMP nsCSSFontFeatureValuesRule::GetFontFamily(nsAString& aFamilyListStr) { nsStyleUtil::AppendEscapedCSSFontFamilyList(mFamilyList, aFamilyListStr); return NS_OK; } NS_IMETHODIMP nsCSSFontFeatureValuesRule::GetValueText(nsAString& aValueText) { FeatureValuesToString(mFeatureValues, aValueText); return NS_OK; } NS_IMETHODIMP nsCSSFontFeatureValuesRule::SetFontFamily(const nsAString& aFontFamily) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsCSSFontFeatureValuesRule::SetValueText(const nsAString& aValueText) { return NS_ERROR_NOT_IMPLEMENTED; } void nsCSSFontFeatureValuesRule::GetCssTextImpl(nsAString& aCssText) const { FontFeatureValuesRuleToString(mFamilyList, mFeatureValues, aCssText); } struct MakeFamilyArray { explicit MakeFamilyArray(nsTArray& aFamilyArray) : familyArray(aFamilyArray), hasGeneric(false) {} static bool AddFamily(const nsString& aFamily, bool aGeneric, void* aData) { MakeFamilyArray *familyArr = reinterpret_cast (aData); if (!aGeneric && !aFamily.IsEmpty()) { familyArr->familyArray.AppendElement(aFamily); } if (aGeneric) { familyArr->hasGeneric = true; } return true; } nsTArray& familyArray; bool hasGeneric; }; void nsCSSFontFeatureValuesRule::AddValueList(int32_t aVariantAlternate, nsTArray& aValueList) { uint32_t i, len = mFeatureValues.Length(); bool foundAlternate = false; // add to an existing list for a given property value for (i = 0; i < len; i++) { gfxFontFeatureValueSet::FeatureValues& f = mFeatureValues.ElementAt(i); if (f.alternate == uint32_t(aVariantAlternate)) { f.valuelist.AppendElements(aValueList); foundAlternate = true; break; } } // create a new list for a given property value if (!foundAlternate) { gfxFontFeatureValueSet::FeatureValues &f = *mFeatureValues.AppendElement(); f.alternate = aVariantAlternate; f.valuelist.AppendElements(aValueList); } } size_t nsCSSFontFeatureValuesRule::SizeOfIncludingThis( MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this); } // ------------------------------------------- // nsCSSKeyframeStyleDeclaration // nsCSSKeyframeStyleDeclaration::nsCSSKeyframeStyleDeclaration(nsCSSKeyframeRule *aRule) : mRule(aRule) { } nsCSSKeyframeStyleDeclaration::~nsCSSKeyframeStyleDeclaration() { NS_ASSERTION(!mRule, "DropReference not called."); } NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSKeyframeStyleDeclaration) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSKeyframeStyleDeclaration) NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsCSSKeyframeStyleDeclaration) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSKeyframeStyleDeclaration) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) DeclarationBlock* nsCSSKeyframeStyleDeclaration::GetCSSDeclaration(Operation aOperation) { if (mRule) { return mRule->Declaration(); } else { return nullptr; } } void nsCSSKeyframeStyleDeclaration::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv, nsIPrincipal* aSubjectPrincipal) { GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv); } nsDOMCSSDeclaration::ServoCSSParsingEnvironment nsCSSKeyframeStyleDeclaration::GetServoCSSParsingEnvironment( nsIPrincipal* aSubjectPrincipal) const { MOZ_CRASH("GetURLData shouldn't be calling on a Gecko rule"); } NS_IMETHODIMP nsCSSKeyframeStyleDeclaration::GetParentRule(nsIDOMCSSRule **aParent) { NS_ENSURE_ARG_POINTER(aParent); NS_IF_ADDREF(*aParent = mRule); return NS_OK; } nsresult nsCSSKeyframeStyleDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl) { MOZ_ASSERT(aDecl, "must be non-null"); mRule->ChangeDeclaration(aDecl->AsGecko()); return NS_OK; } nsIDocument* nsCSSKeyframeStyleDeclaration::DocToUpdate() { return nullptr; } nsINode* nsCSSKeyframeStyleDeclaration::GetParentObject() { return mRule ? mRule->GetDocument() : nullptr; } // ------------------------------------------- // nsCSSKeyframeRule // nsCSSKeyframeRule::nsCSSKeyframeRule(const nsCSSKeyframeRule& aCopy) // copy everything except our reference count and mDOMDeclaration : dom::CSSKeyframeRule(aCopy) , mKeys(aCopy.mKeys) , mDeclaration(new css::Declaration(*aCopy.mDeclaration)) { mDeclaration->SetOwningRule(this); } nsCSSKeyframeRule::~nsCSSKeyframeRule() { mDeclaration->SetOwningRule(nullptr); if (mDOMDeclaration) { mDOMDeclaration->DropReference(); } } /* virtual */ already_AddRefed nsCSSKeyframeRule::Clone() const { RefPtr clone = new nsCSSKeyframeRule(*this); return clone.forget(); } NS_IMPL_ADDREF_INHERITED(nsCSSKeyframeRule, dom::CSSKeyframeRule) NS_IMPL_RELEASE_INHERITED(nsCSSKeyframeRule, dom::CSSKeyframeRule) NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSKeyframeRule) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsCSSKeyframeRule, dom::CSSKeyframeRule) if (tmp->mDOMDeclaration) { tmp->mDOMDeclaration->DropReference(); tmp->mDOMDeclaration = nullptr; } NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsCSSKeyframeRule, dom::CSSKeyframeRule) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMDeclaration) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END bool nsCSSKeyframeRule::IsCCLeaf() const { // Let's not worry about figuring out whether we're a leaf or not. return false; } // QueryInterface implementation for nsCSSKeyframeRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSKeyframeRule) NS_INTERFACE_MAP_END_INHERITING(dom::CSSKeyframeRule) #ifdef DEBUG void nsCSSKeyframeRule::List(FILE* out, int32_t aIndent) const { nsAutoCString str; for (int32_t index = aIndent; --index >= 0; ) { str.AppendLiteral(" "); } nsAutoString tmp; DoGetKeyText(tmp); AppendUTF16toUTF8(tmp, str); str.AppendLiteral(" { "); mDeclaration->ToString(tmp); AppendUTF16toUTF8(tmp, str); str.AppendLiteral("}\n"); fprintf_stderr(out, "%s", str.get()); } #endif void nsCSSKeyframeRule::GetCssTextImpl(nsAString& aCssText) const { DoGetKeyText(aCssText); aCssText.AppendLiteral(" { "); nsAutoString tmp; mDeclaration->ToString(tmp); aCssText.Append(tmp); aCssText.AppendLiteral(" }"); } NS_IMETHODIMP nsCSSKeyframeRule::GetKeyText(nsAString& aKeyText) { DoGetKeyText(aKeyText); return NS_OK; } void nsCSSKeyframeRule::DoGetKeyText(nsAString& aKeyText) const { aKeyText.Truncate(); uint32_t i = 0, i_end = mKeys.Length(); MOZ_ASSERT(i_end != 0, "must have some keys"); for (;;) { aKeyText.AppendFloat(mKeys[i] * 100.0f); aKeyText.Append(char16_t('%')); if (++i == i_end) { break; } aKeyText.AppendLiteral(", "); } } NS_IMETHODIMP nsCSSKeyframeRule::SetKeyText(const nsAString& aKeyText) { nsCSSParser parser; InfallibleTArray newSelectors; // FIXME: pass filename and line number if (!parser.ParseKeyframeSelectorString(aKeyText, nullptr, 0, newSelectors)) { // for now, we don't do anything if the parse fails return NS_OK; } nsIDocument* doc = GetDocument(); MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true); newSelectors.SwapElements(mKeys); if (StyleSheet* sheet = GetStyleSheet()) { sheet->AsGecko()->SetModifiedByChildRule(); if (doc) { doc->StyleRuleChanged(sheet, this); } } return NS_OK; } nsICSSDeclaration* nsCSSKeyframeRule::Style() { if (!mDOMDeclaration) { mDOMDeclaration = new nsCSSKeyframeStyleDeclaration(this); } return mDOMDeclaration; } void nsCSSKeyframeRule::ChangeDeclaration(css::Declaration* aDeclaration) { // Our caller already did a BeginUpdate/EndUpdate, but with // UPDATE_CONTENT, and we need UPDATE_STYLE to trigger work in // PresShell::EndUpdate. nsIDocument* doc = GetDocument(); MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true); if (aDeclaration != mDeclaration) { mDeclaration->SetOwningRule(nullptr); mDeclaration = aDeclaration; mDeclaration->SetOwningRule(this); } if (StyleSheet* sheet = GetStyleSheet()) { sheet->AsGecko()->SetModifiedByChildRule(); if (doc) { doc->StyleRuleChanged(sheet, this); } } } /* virtual */ size_t nsCSSKeyframeRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this); // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - mKeys // - mDeclaration // - mDOMDeclaration } // ------------------------------------------- // nsCSSKeyframesRule // nsCSSKeyframesRule::nsCSSKeyframesRule(const nsCSSKeyframesRule& aCopy) // copy everything except our reference count. GroupRule's copy // constructor also doesn't copy the lazily-constructed // mRuleCollection. : dom::CSSKeyframesRule(aCopy), mName(aCopy.mName) { } nsCSSKeyframesRule::~nsCSSKeyframesRule() { } /* virtual */ already_AddRefed nsCSSKeyframesRule::Clone() const { RefPtr clone = new nsCSSKeyframesRule(*this); return clone.forget(); } NS_IMPL_ADDREF_INHERITED(nsCSSKeyframesRule, dom::CSSKeyframesRule) NS_IMPL_RELEASE_INHERITED(nsCSSKeyframesRule, dom::CSSKeyframesRule) // QueryInterface implementation for nsCSSKeyframesRule NS_INTERFACE_MAP_BEGIN(nsCSSKeyframesRule) NS_INTERFACE_MAP_END_INHERITING(dom::CSSKeyframesRule) #ifdef DEBUG void nsCSSKeyframesRule::List(FILE* out, int32_t aIndent) const { nsAutoCString indentStr; for (int32_t indent = aIndent; --indent >= 0; ) { indentStr.AppendLiteral(" "); } fprintf_stderr(out, "%s@keyframes %s {\n", indentStr.get(), nsAtomCString(mName).get()); GroupRule::List(out, aIndent); fprintf_stderr(out, "%s}\n", indentStr.get()); } #endif void nsCSSKeyframesRule::GetCssTextImpl(nsAString& aCssText) const { aCssText.AssignLiteral("@keyframes "); aCssText.Append(nsDependentAtomString(mName)); aCssText.AppendLiteral(" {\n"); nsAutoString tmp; for (const Rule* rule : GeckoRules()) { static_cast(rule)->GetCssText(tmp); aCssText.Append(tmp); aCssText.Append('\n'); } aCssText.Append('}'); } NS_IMETHODIMP nsCSSKeyframesRule::GetName(nsAString& aName) { mName->ToString(aName); return NS_OK; } NS_IMETHODIMP nsCSSKeyframesRule::SetName(const nsAString& aName) { if (mName->Equals(aName)) { return NS_OK; } nsIDocument* doc = GetDocument(); MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true); mName = NS_Atomize(aName); if (StyleSheet* sheet = GetStyleSheet()) { sheet->AsGecko()->SetModifiedByChildRule(); if (doc) { doc->StyleRuleChanged(sheet, this); } } return NS_OK; } NS_IMETHODIMP nsCSSKeyframesRule::AppendRule(const nsAString& aRule) { // The spec is confusing, and I think we should just append the rule, // which also turns out to match WebKit: // http://lists.w3.org/Archives/Public/www-style/2011Apr/0034.html nsCSSParser parser; // FIXME: pass filename and line number RefPtr rule = parser.ParseKeyframeRule(aRule, nullptr, 0); if (rule) { nsIDocument* doc = GetDocument(); MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true); AppendStyleRule(rule); if (StyleSheet* sheet = GetStyleSheet()) { sheet->AsGecko()->SetModifiedByChildRule(); if (doc) { doc->StyleRuleChanged(sheet, this); } } } return NS_OK; } static const uint32_t RULE_NOT_FOUND = uint32_t(-1); uint32_t nsCSSKeyframesRule::FindRuleIndexForKey(const nsAString& aKey) { nsCSSParser parser; InfallibleTArray keys; // FIXME: pass filename and line number if (parser.ParseKeyframeSelectorString(aKey, nullptr, 0, keys)) { IncrementalClearCOMRuleArray& rules = GeckoRules(); // The spec isn't clear, but we'll match on the key list, which // mostly matches what WebKit does, except we'll do last-match // instead of first-match, and handling parsing differences better. // http://lists.w3.org/Archives/Public/www-style/2011Apr/0036.html // http://lists.w3.org/Archives/Public/www-style/2011Apr/0037.html for (uint32_t i = rules.Count(); i-- != 0; ) { if (static_cast(rules[i])->GetKeys() == keys) { return i; } } } return RULE_NOT_FOUND; } NS_IMETHODIMP nsCSSKeyframesRule::DeleteRule(const nsAString& aKey) { uint32_t index = FindRuleIndexForKey(aKey); if (index != RULE_NOT_FOUND) { nsIDocument* doc = GetDocument(); MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true); DeleteStyleRuleAt(index); if (StyleSheet* sheet = GetStyleSheet()) { sheet->AsGecko()->SetModifiedByChildRule(); if (doc) { doc->StyleRuleChanged(sheet, this); } } } return NS_OK; } nsCSSKeyframeRule* nsCSSKeyframesRule::FindRule(const nsAString& aKey) { uint32_t index = FindRuleIndexForKey(aKey); if (index == RULE_NOT_FOUND) { return nullptr; } return static_cast(GeckoRules()[index]); } /* virtual */ size_t nsCSSKeyframesRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); n += GroupRule::SizeOfExcludingThis(aMallocSizeOf); // Measurement of the following members may be added later if DMD finds it is // worthwhile: // - mName return n; } // ------------------------------------------- // nsCSSPageStyleDeclaration // nsCSSPageStyleDeclaration::nsCSSPageStyleDeclaration(nsCSSPageRule* aRule) : mRule(aRule) { } nsCSSPageStyleDeclaration::~nsCSSPageStyleDeclaration() { NS_ASSERTION(!mRule, "DropReference not called."); } NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSPageStyleDeclaration) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSPageStyleDeclaration) NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsCSSPageStyleDeclaration) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSPageStyleDeclaration) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) DeclarationBlock* nsCSSPageStyleDeclaration::GetCSSDeclaration(Operation aOperation) { if (mRule) { return mRule->Declaration(); } else { return nullptr; } } void nsCSSPageStyleDeclaration::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv, nsIPrincipal* aSubjectPrincipal) { GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv); } nsDOMCSSDeclaration::ServoCSSParsingEnvironment nsCSSPageStyleDeclaration::GetServoCSSParsingEnvironment( nsIPrincipal* aSubjectPrincipal) const { MOZ_CRASH("GetURLData shouldn't be calling on a Gecko rule"); } NS_IMETHODIMP nsCSSPageStyleDeclaration::GetParentRule(nsIDOMCSSRule** aParent) { NS_ENSURE_ARG_POINTER(aParent); NS_IF_ADDREF(*aParent = mRule); return NS_OK; } nsresult nsCSSPageStyleDeclaration::SetCSSDeclaration(DeclarationBlock* aDecl) { MOZ_ASSERT(aDecl, "must be non-null"); mRule->ChangeDeclaration(aDecl->AsGecko()); return NS_OK; } nsIDocument* nsCSSPageStyleDeclaration::DocToUpdate() { return nullptr; } nsINode* nsCSSPageStyleDeclaration::GetParentObject() { return mRule ? mRule->GetDocument() : nullptr; } // ------------------------------------------- // nsCSSPageRule // nsCSSPageRule::nsCSSPageRule(const nsCSSPageRule& aCopy) // copy everything except our reference count and mDOMDeclaration : dom::CSSPageRule(aCopy) , mDeclaration(new css::Declaration(*aCopy.mDeclaration)) { mDeclaration->SetOwningRule(this); } nsCSSPageRule::~nsCSSPageRule() { mDeclaration->SetOwningRule(nullptr); if (mDOMDeclaration) { mDOMDeclaration->DropReference(); } } /* virtual */ already_AddRefed nsCSSPageRule::Clone() const { RefPtr clone = new nsCSSPageRule(*this); return clone.forget(); } NS_IMPL_ADDREF_INHERITED(nsCSSPageRule, dom::CSSPageRule) NS_IMPL_RELEASE_INHERITED(nsCSSPageRule, dom::CSSPageRule) NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSPageRule) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsCSSPageRule, dom::CSSPageRule) if (tmp->mDOMDeclaration) { tmp->mDOMDeclaration->DropReference(); tmp->mDOMDeclaration = nullptr; } NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsCSSPageRule, dom::CSSPageRule) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMDeclaration) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END bool nsCSSPageRule::IsCCLeaf() const { // Let's not worry about figuring out whether we're a leaf or not. return false; } // QueryInterface implementation for nsCSSPageRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSPageRule) NS_INTERFACE_MAP_END_INHERITING(dom::CSSPageRule) #ifdef DEBUG void nsCSSPageRule::List(FILE* out, int32_t aIndent) const { nsAutoCString str; for (int32_t indent = aIndent; --indent >= 0; ) { str.AppendLiteral(" "); } str.AppendLiteral("@page { "); nsAutoString tmp; mDeclaration->ToString(tmp); AppendUTF16toUTF8(tmp, str); str.AppendLiteral("}\n"); fprintf_stderr(out, "%s", str.get()); } #endif void nsCSSPageRule::GetCssTextImpl(nsAString& aCssText) const { aCssText.AppendLiteral("@page { "); nsAutoString tmp; mDeclaration->ToString(tmp); aCssText.Append(tmp); aCssText.AppendLiteral(" }"); } nsICSSDeclaration* nsCSSPageRule::Style() { if (!mDOMDeclaration) { mDOMDeclaration = new nsCSSPageStyleDeclaration(this); } return mDOMDeclaration; } void nsCSSPageRule::ChangeDeclaration(css::Declaration* aDeclaration) { if (aDeclaration != mDeclaration) { mDeclaration->SetOwningRule(nullptr); mDeclaration = aDeclaration; mDeclaration->SetOwningRule(this); } if (StyleSheet* sheet = GetStyleSheet()) { sheet->AsGecko()->SetModifiedByChildRule(); } } /* virtual */ size_t nsCSSPageRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this); } namespace mozilla { CSSSupportsRule::CSSSupportsRule(bool aConditionMet, const nsString& aCondition, uint32_t aLineNumber, uint32_t aColumnNumber) : dom::CSSSupportsRule(aLineNumber, aColumnNumber) , mUseGroup(aConditionMet) , mCondition(aCondition) { } CSSSupportsRule::~CSSSupportsRule() { } CSSSupportsRule::CSSSupportsRule(const CSSSupportsRule& aCopy) : dom::CSSSupportsRule(aCopy), mUseGroup(aCopy.mUseGroup), mCondition(aCopy.mCondition) { } #ifdef DEBUG /* virtual */ void CSSSupportsRule::List(FILE* out, int32_t aIndent) const { nsAutoCString indentStr; for (int32_t indent = aIndent; --indent >= 0; ) { indentStr.AppendLiteral(" "); } fprintf_stderr(out, "%s@supports %s {\n", indentStr.get(), NS_ConvertUTF16toUTF8(mCondition).get()); css::GroupRule::List(out, aIndent); fprintf_stderr(out, "%s}\n", indentStr.get()); } #endif /* virtual */ already_AddRefed CSSSupportsRule::Clone() const { RefPtr clone = new CSSSupportsRule(*this); return clone.forget(); } /* virtual */ bool CSSSupportsRule::UseForPresentation(nsPresContext* aPresContext, nsMediaQueryResultCacheKey& aKey) { return mUseGroup; } NS_IMPL_ADDREF_INHERITED(mozilla::CSSSupportsRule, dom::CSSSupportsRule) NS_IMPL_RELEASE_INHERITED(mozilla::CSSSupportsRule, dom::CSSSupportsRule) // QueryInterface implementation for CSSSupportsRule NS_INTERFACE_MAP_BEGIN(CSSSupportsRule) NS_INTERFACE_MAP_END_INHERITING(dom::CSSSupportsRule) void CSSSupportsRule::GetCssTextImpl(nsAString& aCssText) const { aCssText.AssignLiteral("@supports "); aCssText.Append(mCondition); css::GroupRule::AppendRulesToCssText(aCssText); } // nsIDOMCSSConditionRule methods NS_IMETHODIMP CSSSupportsRule::GetConditionText(nsAString& aConditionText) { aConditionText.Assign(mCondition); return NS_OK; } NS_IMETHODIMP CSSSupportsRule::SetConditionText(const nsAString& aConditionText) { return NS_ERROR_NOT_IMPLEMENTED; } /* virtual */ size_t CSSSupportsRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); n += css::GroupRule::SizeOfExcludingThis(aMallocSizeOf); n += mCondition.SizeOfExcludingThisIfUnshared(aMallocSizeOf); return n; } } // namespace mozilla