/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set tw=78 expandtab softtabstop=2 ts=2 sw=2: */ /* 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/. */ /* DOM object returned from element.getComputedStyle() */ #include "nsComputedDOMStyle.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Preferences.h" #include "nsError.h" #include "nsDOMString.h" #include "nsIDOMCSSPrimitiveValue.h" #include "nsIFrame.h" #include "nsIFrameInlines.h" #include "nsStyleContext.h" #include "nsIScrollableFrame.h" #include "nsContentUtils.h" #include "nsIContent.h" #include "nsDOMCSSRect.h" #include "nsDOMCSSRGBColor.h" #include "nsDOMCSSValueList.h" #include "nsFlexContainerFrame.h" #include "nsGridContainerFrame.h" #include "nsGkAtoms.h" #include "nsHTMLReflowState.h" #include "nsStyleUtil.h" #include "nsStyleStructInlines.h" #include "nsROCSSPrimitiveValue.h" #include "nsPresContext.h" #include "nsIDocument.h" #include "nsCSSPseudoElements.h" #include "nsStyleSet.h" #include "imgIRequest.h" #include "nsLayoutUtils.h" #include "nsCSSKeywords.h" #include "nsStyleCoord.h" #include "nsDisplayList.h" #include "nsDOMCSSDeclaration.h" #include "nsStyleTransformMatrix.h" #include "mozilla/dom/Element.h" #include "prtime.h" #include "nsWrapperCacheInlines.h" #include "mozilla/AppUnits.h" #include using namespace mozilla; using namespace mozilla::dom; #if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon) #define DEBUG_ComputedDOMStyle #endif /* * This is the implementation of the readonly CSSStyleDeclaration that is * returned by the getComputedStyle() function. */ already_AddRefed NS_NewComputedDOMStyle(dom::Element* aElement, const nsAString& aPseudoElt, nsIPresShell* aPresShell, nsComputedDOMStyle::StyleType aStyleType) { RefPtr computedStyle; computedStyle = new nsComputedDOMStyle(aElement, aPseudoElt, aPresShell, aStyleType); return computedStyle.forget(); } /** * An object that represents the ordered set of properties that are exposed on * an nsComputedDOMStyle object and how their computed values can be obtained. */ struct nsComputedStyleMap { friend class nsComputedDOMStyle; struct Entry { // Create a pointer-to-member-function type. typedef already_AddRefed (nsComputedDOMStyle::*ComputeMethod)(); nsCSSProperty mProperty; ComputeMethod mGetter; bool IsLayoutFlushNeeded() const { return nsCSSProps::PropHasFlags(mProperty, CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH); } bool IsEnabled() const { return nsCSSProps::IsEnabled(mProperty, nsCSSProps::eEnabledForAllContent); } }; // We define this enum just to count the total number of properties that can // be exposed on an nsComputedDOMStyle, including properties that may be // disabled. enum { #define COMPUTED_STYLE_PROP(prop_, method_) \ eComputedStyleProperty_##prop_, #include "nsComputedDOMStylePropertyList.h" #undef COMPUTED_STYLE_PROP eComputedStyleProperty_COUNT }; /** * Returns the number of properties that should be exposed on an * nsComputedDOMStyle, ecxluding any disabled properties. */ uint32_t Length() { Update(); return mExposedPropertyCount; } /** * Returns the property at the given index in the list of properties * that should be exposed on an nsComputedDOMStyle, excluding any * disabled properties. */ nsCSSProperty PropertyAt(uint32_t aIndex) { Update(); return kEntries[EntryIndex(aIndex)].mProperty; } /** * Searches for and returns the computed style map entry for the given * property, or nullptr if the property is not exposed on nsComputedDOMStyle * or is currently disabled. */ const Entry* FindEntryForProperty(nsCSSProperty aPropID) { Update(); for (uint32_t i = 0; i < mExposedPropertyCount; i++) { const Entry* entry = &kEntries[EntryIndex(i)]; if (entry->mProperty == aPropID) { return entry; } } return nullptr; } /** * Records that mIndexMap needs updating, due to prefs changing that could * affect the set of properties exposed on an nsComputedDOMStyle. */ void MarkDirty() { mExposedPropertyCount = 0; } // The member variables are public so that we can use an initializer in // nsComputedDOMStyle::GetComputedStyleMap. Use the member functions // above to get information from this object. /** * An entry for each property that can be exposed on an nsComputedDOMStyle. */ const Entry kEntries[eComputedStyleProperty_COUNT]; /** * The number of properties that should be exposed on an nsComputedDOMStyle. * This will be less than eComputedStyleProperty_COUNT if some property * prefs are disabled. A value of 0 indicates that it and mIndexMap are out * of date. */ uint32_t mExposedPropertyCount; /** * A map of indexes on the nsComputedDOMStyle object to indexes into kEntries. */ uint32_t mIndexMap[eComputedStyleProperty_COUNT]; private: /** * Returns whether mExposedPropertyCount and mIndexMap are out of date. */ bool IsDirty() { return mExposedPropertyCount == 0; } /** * Updates mExposedPropertyCount and mIndexMap to take into account properties * whose prefs are currently disabled. */ void Update(); /** * Maps an nsComputedDOMStyle indexed getter index to an index into kEntries. */ uint32_t EntryIndex(uint32_t aIndex) const { MOZ_ASSERT(aIndex < mExposedPropertyCount); return mIndexMap[aIndex]; } }; void nsComputedStyleMap::Update() { if (!IsDirty()) { return; } uint32_t index = 0; for (uint32_t i = 0; i < eComputedStyleProperty_COUNT; i++) { if (kEntries[i].IsEnabled()) { mIndexMap[index++] = i; } } mExposedPropertyCount = index; } nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement, const nsAString& aPseudoElt, nsIPresShell* aPresShell, StyleType aStyleType) : mDocumentWeak(nullptr), mOuterFrame(nullptr), mInnerFrame(nullptr), mPresShell(nullptr), mStyleType(aStyleType), mStyleContextGeneration(0), mExposeVisitedStyle(false), mResolvedStyleContext(false) { MOZ_ASSERT(aElement && aPresShell); mDocumentWeak = do_GetWeakReference(aPresShell->GetDocument()); mContent = aElement; if (!DOMStringIsNull(aPseudoElt) && !aPseudoElt.IsEmpty() && aPseudoElt.First() == char16_t(':')) { // deal with two-colon forms of aPseudoElt nsAString::const_iterator start, end; aPseudoElt.BeginReading(start); aPseudoElt.EndReading(end); NS_ASSERTION(start != end, "aPseudoElt is not empty!"); ++start; bool haveTwoColons = true; if (start == end || *start != char16_t(':')) { --start; haveTwoColons = false; } mPseudo = do_GetAtom(Substring(start, end)); MOZ_ASSERT(mPseudo); // There aren't any non-CSS2 pseudo-elements with a single ':' if (!haveTwoColons && (!nsCSSPseudoElements::IsPseudoElement(mPseudo) || !nsCSSPseudoElements::IsCSS2PseudoElement(mPseudo))) { // XXXbz I'd really rather we threw an exception or something, but // the DOM spec sucks. mPseudo = nullptr; } } MOZ_ASSERT(aPresShell->GetPresContext()); } nsComputedDOMStyle::~nsComputedDOMStyle() { ClearStyleContext(); } NS_IMPL_CYCLE_COLLECTION_CLASS(nsComputedDOMStyle) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle) tmp->ClearStyleContext(); // remove observer before clearing mContent NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsComputedDOMStyle) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsComputedDOMStyle) NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsComputedDOMStyle) return tmp->IsBlack(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsComputedDOMStyle) return tmp->IsBlack(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsComputedDOMStyle) return tmp->IsBlack(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END // QueryInterface implementation for nsComputedDOMStyle NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsComputedDOMStyle) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsComputedDOMStyle) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsComputedDOMStyle) NS_IMETHODIMP nsComputedDOMStyle::GetPropertyValue(const nsCSSProperty aPropID, nsAString& aValue) { // This is mostly to avoid code duplication with GetPropertyCSSValue(); if // perf ever becomes an issue here (doubtful), we can look into changing // this. return GetPropertyValue( NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(aPropID)), aValue); } NS_IMETHODIMP nsComputedDOMStyle::SetPropertyValue(const nsCSSProperty aPropID, const nsAString& aValue) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHODIMP nsComputedDOMStyle::GetCssText(nsAString& aCssText) { aCssText.Truncate(); return NS_OK; } NS_IMETHODIMP nsComputedDOMStyle::SetCssText(const nsAString& aCssText) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHODIMP nsComputedDOMStyle::GetLength(uint32_t* aLength) { NS_PRECONDITION(aLength, "Null aLength! Prepare to die!"); uint32_t length = GetComputedStyleMap()->Length(); // Make sure we have up to date style so that we can include custom // properties. UpdateCurrentStyleSources(false); if (mStyleContext) { length += StyleVariables()->mVariables.Count(); } *aLength = length; ClearCurrentStyleSources(); return NS_OK; } NS_IMETHODIMP nsComputedDOMStyle::GetParentRule(nsIDOMCSSRule** aParentRule) { *aParentRule = nullptr; return NS_OK; } NS_IMETHODIMP nsComputedDOMStyle::GetPropertyValue(const nsAString& aPropertyName, nsAString& aReturn) { aReturn.Truncate(); ErrorResult error; RefPtr val = GetPropertyCSSValue(aPropertyName, error); if (error.Failed()) { return error.StealNSResult(); } if (val) { nsString text; val->GetCssText(text, error); aReturn.Assign(text); return error.StealNSResult(); } return NS_OK; } NS_IMETHODIMP nsComputedDOMStyle::GetAuthoredPropertyValue(const nsAString& aPropertyName, nsAString& aReturn) { // Authored style doesn't make sense to return from computed DOM style, // so just return whatever GetPropertyValue() returns. return GetPropertyValue(aPropertyName, aReturn); } /* static */ already_AddRefed nsComputedDOMStyle::GetStyleContextForElement(Element* aElement, nsIAtom* aPseudo, nsIPresShell* aPresShell, StyleType aStyleType) { // If the content has a pres shell, we must use it. Otherwise we'd // potentially mix rule trees by using the wrong pres shell's style // set. Using the pres shell from the content also means that any // content that's actually *in* a document will get the style from the // correct document. nsCOMPtr presShell = GetPresShellForContent(aElement); if (!presShell) { presShell = aPresShell; if (!presShell) return nullptr; } presShell->FlushPendingNotifications(Flush_Style); return GetStyleContextForElementNoFlush(aElement, aPseudo, presShell, aStyleType); } /* static */ already_AddRefed nsComputedDOMStyle::GetStyleContextForElementNoFlush(Element* aElement, nsIAtom* aPseudo, nsIPresShell* aPresShell, StyleType aStyleType) { MOZ_ASSERT(aElement, "NULL element"); // If the content has a pres shell, we must use it. Otherwise we'd // potentially mix rule trees by using the wrong pres shell's style // set. Using the pres shell from the content also means that any // content that's actually *in* a document will get the style from the // correct document. nsIPresShell *presShell = GetPresShellForContent(aElement); if (!presShell) { presShell = aPresShell; if (!presShell) return nullptr; } // XXX the !aElement->IsHTMLElement(nsGkAtoms::area) // check is needed due to bug 135040 (to avoid using // mPrimaryFrame). Remove it once that's fixed. if (!aPseudo && aStyleType == eAll && !aElement->IsHTMLElement(nsGkAtoms::area)) { nsIFrame* frame = nsLayoutUtils::GetStyleFrame(aElement); if (frame) { nsStyleContext* result = frame->StyleContext(); // Don't use the style context if it was influenced by // pseudo-elements, since then it's not the primary style // for this element. if (!result->HasPseudoElementData()) { // this function returns an addrefed style context RefPtr ret = result; return ret.forget(); } } } // No frame has been created, or we have a pseudo, or we're looking // for the default style, so resolve the style ourselves. RefPtr parentContext; nsIContent* parent = aPseudo ? aElement : aElement->GetParent(); // Don't resolve parent context for document fragments. if (parent && parent->IsElement()) parentContext = GetStyleContextForElementNoFlush(parent->AsElement(), nullptr, presShell, aStyleType); nsPresContext *presContext = presShell->GetPresContext(); if (!presContext) return nullptr; nsStyleSet *styleSet = presShell->StyleSet(); RefPtr sc; if (aPseudo) { nsCSSPseudoElements::Type type = nsCSSPseudoElements::GetPseudoType(aPseudo); if (type >= nsCSSPseudoElements::ePseudo_PseudoElementCount) { return nullptr; } nsIFrame* frame = nsLayoutUtils::GetStyleFrame(aElement); Element* pseudoElement = frame ? frame->GetPseudoElement(type) : nullptr; sc = styleSet->ResolvePseudoElementStyle(aElement, type, parentContext, pseudoElement); } else { sc = styleSet->ResolveStyleFor(aElement, parentContext); } if (aStyleType == eDefaultOnly) { // We really only want the user and UA rules. Filter out the other ones. nsTArray< nsCOMPtr > rules; for (nsRuleNode* ruleNode = sc->RuleNode(); !ruleNode->IsRoot(); ruleNode = ruleNode->GetParent()) { if (ruleNode->GetLevel() == SheetType::Agent || ruleNode->GetLevel() == SheetType::User) { rules.AppendElement(ruleNode->GetRule()); } } // We want to build a list of user/ua rules that is in order from least to // most important, so we have to reverse the list. // Integer division to get "stop" is purposeful here: if length is odd, we // don't have to do anything with the middle element of the array. for (uint32_t i = 0, length = rules.Length(), stop = length / 2; i < stop; ++i) { rules[i].swap(rules[length - i - 1]); } sc = styleSet->ResolveStyleForRules(parentContext, rules); } return sc.forget(); } nsMargin nsComputedDOMStyle::GetAdjustedValuesForBoxSizing() { // We want the width/height of whatever parts 'width' or 'height' controls, // which can be different depending on the value of the 'box-sizing' property. const nsStylePosition* stylePos = StylePosition(); nsMargin adjustment; switch(stylePos->mBoxSizing) { case StyleBoxSizing::Border: adjustment += mInnerFrame->GetUsedBorder(); MOZ_FALLTHROUGH; case StyleBoxSizing::Padding: adjustment += mInnerFrame->GetUsedPadding(); MOZ_FALLTHROUGH; case StyleBoxSizing::Content: // nothing break; } return adjustment; } /* static */ nsIPresShell* nsComputedDOMStyle::GetPresShellForContent(nsIContent* aContent) { nsIDocument* composedDoc = aContent->GetComposedDoc(); if (!composedDoc) return nullptr; return composedDoc->GetShell(); } // nsDOMCSSDeclaration abstract methods which should never be called // on a nsComputedDOMStyle object, but must be defined to avoid // compile errors. css::Declaration* nsComputedDOMStyle::GetCSSDeclaration(Operation) { NS_RUNTIMEABORT("called nsComputedDOMStyle::GetCSSDeclaration"); return nullptr; } nsresult nsComputedDOMStyle::SetCSSDeclaration(css::Declaration*) { NS_RUNTIMEABORT("called nsComputedDOMStyle::SetCSSDeclaration"); return NS_ERROR_FAILURE; } nsIDocument* nsComputedDOMStyle::DocToUpdate() { NS_RUNTIMEABORT("called nsComputedDOMStyle::DocToUpdate"); return nullptr; } void nsComputedDOMStyle::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) { NS_RUNTIMEABORT("called nsComputedDOMStyle::GetCSSParsingEnvironment"); // Just in case NS_RUNTIMEABORT ever stops killing us for some reason aCSSParseEnv.mPrincipal = nullptr; } void nsComputedDOMStyle::ClearStyleContext() { if (mResolvedStyleContext) { mResolvedStyleContext = false; mContent->RemoveMutationObserver(this); } mStyleContext = nullptr; } void nsComputedDOMStyle::SetResolvedStyleContext(RefPtr&& aContext) { if (!mResolvedStyleContext) { mResolvedStyleContext = true; mContent->AddMutationObserver(this); } mStyleContext = aContext; } void nsComputedDOMStyle::SetFrameStyleContext(nsStyleContext* aContext) { ClearStyleContext(); mStyleContext = aContext; } void nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) { nsCOMPtr document = do_QueryReferent(mDocumentWeak); if (!document) { ClearStyleContext(); return; } document->FlushPendingLinkUpdates(); // Flush _before_ getting the presshell, since that could create a new // presshell. Also note that we want to flush the style on the document // we're computing style in, not on the document mContent is in -- the two // may be different. document->FlushPendingNotifications( aNeedsLayoutFlush ? Flush_Layout : Flush_Style); #ifdef DEBUG mFlushedPendingReflows = aNeedsLayoutFlush; #endif mPresShell = document->GetShell(); if (!mPresShell || !mPresShell->GetPresContext()) { ClearStyleContext(); return; } uint64_t currentGeneration = mPresShell->GetPresContext()->GetRestyleGeneration(); if (mStyleContext) { if (mStyleContextGeneration == currentGeneration) { // Our cached style context is still valid. return; } // We've processed some restyles, so the cached style context might // be out of date. mStyleContext = nullptr; } // XXX the !mContent->IsHTMLElement(nsGkAtoms::area) // check is needed due to bug 135040 (to avoid using // mPrimaryFrame). Remove it once that's fixed. if (!mPseudo && mStyleType == eAll && !mContent->IsHTMLElement(nsGkAtoms::area)) { mOuterFrame = mContent->GetPrimaryFrame(); mInnerFrame = mOuterFrame; if (mOuterFrame) { nsIAtom* type = mOuterFrame->GetType(); if (type == nsGkAtoms::tableOuterFrame) { // If the frame is an outer table frame then we should get the style // from the inner table frame. mInnerFrame = mOuterFrame->GetFirstPrincipalChild(); NS_ASSERTION(mInnerFrame, "Outer table must have an inner"); NS_ASSERTION(!mInnerFrame->GetNextSibling(), "Outer table frames should have just one child, " "the inner table"); } SetFrameStyleContext(mInnerFrame->StyleContext()); NS_ASSERTION(mStyleContext, "Frame without style context?"); } } if (!mStyleContext || mStyleContext->HasPseudoElementData()) { #ifdef DEBUG if (mStyleContext) { // We want to check that going through this path because of // HasPseudoElementData is rare, because it slows us down a good // bit. So check that we're really inside something associated // with a pseudo-element that contains elements. nsStyleContext* topWithPseudoElementData = mStyleContext; while (topWithPseudoElementData->GetParent()->HasPseudoElementData()) { topWithPseudoElementData = topWithPseudoElementData->GetParent(); } nsCSSPseudoElements::Type pseudo = topWithPseudoElementData->GetPseudoType(); nsIAtom* pseudoAtom = nsCSSPseudoElements::GetPseudoAtom(pseudo); nsAutoString assertMsg( NS_LITERAL_STRING("we should be in a pseudo-element that is expected to contain elements (")); assertMsg.Append(nsDependentString(pseudoAtom->GetUTF16String())); assertMsg.Append(')'); NS_ASSERTION(nsCSSPseudoElements::PseudoElementContainsElements(pseudo), NS_LossyConvertUTF16toASCII(assertMsg).get()); } #endif // Need to resolve a style context RefPtr resolvedStyleContext = nsComputedDOMStyle::GetStyleContextForElement(mContent->AsElement(), mPseudo, mPresShell, mStyleType); if (!resolvedStyleContext) { ClearStyleContext(); return; } // No need to re-get the generation, even though GetStyleContextForElement // will flush, since we flushed style at the top of this function. NS_ASSERTION(mPresShell && currentGeneration == mPresShell->GetPresContext()->GetRestyleGeneration(), "why should we have flushed style again?"); SetResolvedStyleContext(Move(resolvedStyleContext)); NS_ASSERTION(mPseudo || !mStyleContext->HasPseudoElementData(), "should not have pseudo-element data"); } // mExposeVisitedStyle is set to true only by testing APIs that // require chrome privilege. MOZ_ASSERT(!mExposeVisitedStyle || nsContentUtils::IsCallerChrome(), "mExposeVisitedStyle set incorrectly"); if (mExposeVisitedStyle && mStyleContext->RelevantLinkVisited()) { nsStyleContext *styleIfVisited = mStyleContext->GetStyleIfVisited(); if (styleIfVisited) { mStyleContext = styleIfVisited; } } } void nsComputedDOMStyle::ClearCurrentStyleSources() { mOuterFrame = nullptr; mInnerFrame = nullptr; mPresShell = nullptr; // Release the current style context if we got it off the frame. // For a style context we resolved, keep it around so that we // can re-use it next time this object is queried. if (!mResolvedStyleContext) { mStyleContext = nullptr; } } already_AddRefed nsComputedDOMStyle::GetPropertyCSSValue(const nsAString& aPropertyName, ErrorResult& aRv) { nsCSSProperty prop = nsCSSProps::LookupProperty(aPropertyName, nsCSSProps::eEnabledForAllContent); bool needsLayoutFlush; nsComputedStyleMap::Entry::ComputeMethod getter; if (prop == eCSSPropertyExtra_variable) { needsLayoutFlush = false; getter = nullptr; } else { // We don't (for now, anyway, though it may make sense to change it // for all aliases, including those in nsCSSPropAliasList) want // aliases to be enumerable (via GetLength and IndexedGetter), so // handle them here rather than adding entries to // GetQueryablePropertyMap. if (prop != eCSSProperty_UNKNOWN && nsCSSProps::PropHasFlags(prop, CSS_PROPERTY_IS_ALIAS)) { const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(prop); MOZ_ASSERT(subprops[1] == eCSSProperty_UNKNOWN, "must have list of length 1"); prop = subprops[0]; } const nsComputedStyleMap::Entry* propEntry = GetComputedStyleMap()->FindEntryForProperty(prop); if (!propEntry) { #ifdef DEBUG_ComputedDOMStyle NS_WARNING(PromiseFlatCString(NS_ConvertUTF16toUTF8(aPropertyName) + NS_LITERAL_CSTRING(" is not queryable!")).get()); #endif // NOTE: For branches, we should flush here for compatibility! return nullptr; } needsLayoutFlush = propEntry->IsLayoutFlushNeeded(); getter = propEntry->mGetter; } UpdateCurrentStyleSources(needsLayoutFlush); if (!mStyleContext) { aRv.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } RefPtr val; if (prop == eCSSPropertyExtra_variable) { val = DoGetCustomProperty(aPropertyName); } else { // Call our pointer-to-member-function. val = (this->*getter)(); } ClearCurrentStyleSources(); return val.forget(); } NS_IMETHODIMP nsComputedDOMStyle::RemoveProperty(const nsAString& aPropertyName, nsAString& aReturn) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHODIMP nsComputedDOMStyle::GetPropertyPriority(const nsAString& aPropertyName, nsAString& aReturn) { aReturn.Truncate(); return NS_OK; } NS_IMETHODIMP nsComputedDOMStyle::SetProperty(const nsAString& aPropertyName, const nsAString& aValue, const nsAString& aPriority) { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } NS_IMETHODIMP nsComputedDOMStyle::Item(uint32_t aIndex, nsAString& aReturn) { return nsDOMCSSDeclaration::Item(aIndex, aReturn); } void nsComputedDOMStyle::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aPropName) { nsComputedStyleMap* map = GetComputedStyleMap(); uint32_t length = map->Length(); if (aIndex < length) { aFound = true; CopyASCIItoUTF16(nsCSSProps::GetStringValue(map->PropertyAt(aIndex)), aPropName); return; } // Custom properties are exposed with indexed properties just after all // of the built-in properties. UpdateCurrentStyleSources(false); if (!mStyleContext) { aFound = false; return; } const nsStyleVariables* variables = StyleVariables(); if (aIndex - length < variables->mVariables.Count()) { aFound = true; nsString varName; variables->mVariables.GetVariableAt(aIndex - length, varName); aPropName.AssignLiteral("--"); aPropName.Append(varName); } else { aFound = false; } ClearCurrentStyleSources(); } // Property getters... already_AddRefed nsComputedDOMStyle::DoGetBinding() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleDisplay* display = StyleDisplay(); if (display->mBinding) { val->SetURI(display->mBinding->GetURI()); } else { val->SetIdent(eCSSKeyword_none); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetClear() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mBreakType, nsCSSProps::kClearKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFloat() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mFloats, nsCSSProps::kFloatKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBottom() { return GetOffsetWidthFor(NS_SIDE_BOTTOM); } already_AddRefed nsComputedDOMStyle::DoGetStackSizing() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(StyleXUL()->mStretchStack ? eCSSKeyword_stretch_to_fit : eCSSKeyword_ignore); return val.forget(); } void nsComputedDOMStyle::SetToRGBAColor(nsROCSSPrimitiveValue* aValue, nscolor aColor) { if (NS_GET_A(aColor) == 0) { aValue->SetIdent(eCSSKeyword_transparent); return; } nsROCSSPrimitiveValue *red = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *green = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *blue = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *alpha = new nsROCSSPrimitiveValue; uint8_t a = NS_GET_A(aColor); nsDOMCSSRGBColor *rgbColor = new nsDOMCSSRGBColor(red, green, blue, alpha, a < 255); red->SetNumber(NS_GET_R(aColor)); green->SetNumber(NS_GET_G(aColor)); blue->SetNumber(NS_GET_B(aColor)); alpha->SetNumber(nsStyleUtil::ColorComponentToFloat(a)); aValue->SetColor(rgbColor); } already_AddRefed nsComputedDOMStyle::DoGetColor() { RefPtr val = new nsROCSSPrimitiveValue; SetToRGBAColor(val, StyleColor()->mColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOpacity() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleDisplay()->mOpacity); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColumnCount() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleColumn* column = StyleColumn(); if (column->mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO) { val->SetIdent(eCSSKeyword_auto); } else { val->SetNumber(column->mColumnCount); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColumnWidth() { RefPtr val = new nsROCSSPrimitiveValue; // XXX fix the auto case. When we actually have a column frame, I think // we should return the computed column width. SetValueToCoord(val, StyleColumn()->mColumnWidth, true); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColumnGap() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleColumn* column = StyleColumn(); if (column->mColumnGap.GetUnit() == eStyleUnit_Normal) { val->SetAppUnits(StyleFont()->mFont.size); } else { SetValueToCoord(val, StyleColumn()->mColumnGap, true); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColumnFill() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleColumn()->mColumnFill, nsCSSProps::kColumnFillKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColumnRuleWidth() { RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(StyleColumn()->GetComputedColumnRuleWidth()); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColumnRuleStyle() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleColumn()->mColumnRuleStyle, nsCSSProps::kBorderStyleKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColumnRuleColor() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleColumn* column = StyleColumn(); nscolor ruleColor; if (column->mColumnRuleColorIsForeground) { ruleColor = StyleColor()->mColor; } else { ruleColor = column->mColumnRuleColor; } SetToRGBAColor(val, ruleColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetContent() { const nsStyleContent *content = StyleContent(); if (content->ContentCount() == 0) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } if (content->ContentCount() == 1 && content->ContentAt(0).mType == eStyleContentType_AltContent) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword__moz_alt_content); return val.forget(); } RefPtr valueList = GetROCSSValueList(false); for (uint32_t i = 0, i_end = content->ContentCount(); i < i_end; ++i) { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleContentData &data = content->ContentAt(i); switch (data.mType) { case eStyleContentType_String: { nsString str; nsStyleUtil::AppendEscapedCSSString( nsDependentString(data.mContent.mString), str); val->SetString(str); } break; case eStyleContentType_Image: { nsCOMPtr uri; if (data.mContent.mImage) { data.mContent.mImage->GetURI(getter_AddRefs(uri)); } val->SetURI(uri); } break; case eStyleContentType_Attr: { nsAutoString str; nsStyleUtil::AppendEscapedCSSIdent( nsDependentString(data.mContent.mString), str); val->SetString(str, nsIDOMCSSPrimitiveValue::CSS_ATTR); } break; case eStyleContentType_Counter: case eStyleContentType_Counters: { /* FIXME: counters should really use an object */ nsAutoString str; if (data.mType == eStyleContentType_Counter) { str.AppendLiteral("counter("); } else { str.AppendLiteral("counters("); } // WRITE ME nsCSSValue::Array *a = data.mContent.mCounters; nsStyleUtil::AppendEscapedCSSIdent( nsDependentString(a->Item(0).GetStringBufferValue()), str); int32_t typeItem = 1; if (data.mType == eStyleContentType_Counters) { typeItem = 2; str.AppendLiteral(", "); nsStyleUtil::AppendEscapedCSSString( nsDependentString(a->Item(1).GetStringBufferValue()), str); } MOZ_ASSERT(eCSSUnit_None != a->Item(typeItem).GetUnit(), "'none' should be handled as identifier value"); nsString type; a->Item(typeItem).AppendToString(eCSSProperty_list_style_type, type, nsCSSValue::eNormalized); if (!type.LowerCaseEqualsLiteral("decimal")) { str.AppendLiteral(", "); str.Append(type); } str.Append(char16_t(')')); val->SetString(str, nsIDOMCSSPrimitiveValue::CSS_COUNTER); } break; case eStyleContentType_OpenQuote: val->SetIdent(eCSSKeyword_open_quote); break; case eStyleContentType_CloseQuote: val->SetIdent(eCSSKeyword_close_quote); break; case eStyleContentType_NoOpenQuote: val->SetIdent(eCSSKeyword_no_open_quote); break; case eStyleContentType_NoCloseQuote: val->SetIdent(eCSSKeyword_no_close_quote); break; case eStyleContentType_AltContent: default: NS_NOTREACHED("unexpected type"); break; } valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetCounterIncrement() { const nsStyleContent *content = StyleContent(); if (content->CounterIncrementCount() == 0) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } RefPtr valueList = GetROCSSValueList(false); for (uint32_t i = 0, i_end = content->CounterIncrementCount(); i < i_end; ++i) { RefPtr name = new nsROCSSPrimitiveValue; RefPtr value = new nsROCSSPrimitiveValue; const nsStyleCounterData *data = content->GetCounterIncrementAt(i); nsAutoString escaped; nsStyleUtil::AppendEscapedCSSIdent(data->mCounter, escaped); name->SetString(escaped); value->SetNumber(data->mValue); // XXX This should really be integer valueList->AppendCSSValue(name.forget()); valueList->AppendCSSValue(value.forget()); } return valueList.forget(); } /* Convert the stored representation into a list of two values and then hand * it back. */ already_AddRefed nsComputedDOMStyle::DoGetTransformOrigin() { /* We need to build up a list of two values. We'll call them * width and height. */ /* Store things as a value list */ RefPtr valueList = GetROCSSValueList(false); /* Now, get the values. */ const nsStyleDisplay* display = StyleDisplay(); RefPtr width = new nsROCSSPrimitiveValue; SetValueToCoord(width, display->mTransformOrigin[0], false, &nsComputedDOMStyle::GetFrameBoundsWidthForTransform); valueList->AppendCSSValue(width.forget()); RefPtr height = new nsROCSSPrimitiveValue; SetValueToCoord(height, display->mTransformOrigin[1], false, &nsComputedDOMStyle::GetFrameBoundsHeightForTransform); valueList->AppendCSSValue(height.forget()); if (display->mTransformOrigin[2].GetUnit() != eStyleUnit_Coord || display->mTransformOrigin[2].GetCoordValue() != 0) { RefPtr depth = new nsROCSSPrimitiveValue; SetValueToCoord(depth, display->mTransformOrigin[2], false, nullptr); valueList->AppendCSSValue(depth.forget()); } return valueList.forget(); } /* Convert the stored representation into a list of two values and then hand * it back. */ already_AddRefed nsComputedDOMStyle::DoGetPerspectiveOrigin() { /* We need to build up a list of two values. We'll call them * width and height. */ /* Store things as a value list */ RefPtr valueList = GetROCSSValueList(false); /* Now, get the values. */ const nsStyleDisplay* display = StyleDisplay(); RefPtr width = new nsROCSSPrimitiveValue; SetValueToCoord(width, display->mPerspectiveOrigin[0], false, &nsComputedDOMStyle::GetFrameBoundsWidthForTransform); valueList->AppendCSSValue(width.forget()); RefPtr height = new nsROCSSPrimitiveValue; SetValueToCoord(height, display->mPerspectiveOrigin[1], false, &nsComputedDOMStyle::GetFrameBoundsHeightForTransform); valueList->AppendCSSValue(height.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPerspective() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleDisplay()->mChildPerspective, false); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBackfaceVisibility() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mBackfaceVisibility, nsCSSProps::kBackfaceVisibilityKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTransformStyle() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mTransformStyle, nsCSSProps::kTransformStyleKTable)); return val.forget(); } /* If the property is "none", hand back "none" wrapped in a value. * Otherwise, compute the aggregate transform matrix and hands it back in a * "matrix" wrapper. */ already_AddRefed nsComputedDOMStyle::DoGetTransform() { /* First, get the display data. We'll need it. */ const nsStyleDisplay* display = StyleDisplay(); /* If there are no transforms, then we should construct a single-element * entry and hand it back. */ if (!display->mSpecifiedTransform) { RefPtr val = new nsROCSSPrimitiveValue; /* Set it to "none." */ val->SetIdent(eCSSKeyword_none); return val.forget(); } /* Otherwise, we need to compute the current value of the transform matrix, * store it in a string, and hand it back to the caller. */ /* Use the inner frame for the reference box. If we don't have an inner * frame we use empty dimensions to allow us to continue (and percentage * values in the transform will simply give broken results). * TODO: There is no good way for us to represent the case where there's no * frame, which is problematic. The reason is that when we have percentage * transforms, there are a total of four stored matrix entries that influence * the transform based on the size of the element. However, this poses a * problem, because only two of these values can be explicitly referenced * using the named transforms. Until a real solution is found, we'll just * use this approach. */ nsStyleTransformMatrix::TransformReferenceBox refBox(mInnerFrame, nsSize(0, 0)); RuleNodeCacheConditions dummy; gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform->mHead, mStyleContext, mStyleContext->PresContext(), dummy, refBox, float(mozilla::AppUnitsPerCSSPixel())); return MatrixToCSSValue(matrix); } already_AddRefed nsComputedDOMStyle::DoGetTransformBox() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mTransformBox, nsCSSProps::kTransformBoxKTable)); return val.forget(); } /* static */ already_AddRefed nsComputedDOMStyle::MatrixToCSSValue(const mozilla::gfx::Matrix4x4& matrix) { bool is3D = !matrix.Is2D(); nsAutoString resultString(NS_LITERAL_STRING("matrix")); if (is3D) { resultString.AppendLiteral("3d"); } resultString.Append('('); resultString.AppendFloat(matrix._11); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._12); resultString.AppendLiteral(", "); if (is3D) { resultString.AppendFloat(matrix._13); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._14); resultString.AppendLiteral(", "); } resultString.AppendFloat(matrix._21); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._22); resultString.AppendLiteral(", "); if (is3D) { resultString.AppendFloat(matrix._23); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._24); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._31); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._32); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._33); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._34); resultString.AppendLiteral(", "); } resultString.AppendFloat(matrix._41); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._42); if (is3D) { resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._43); resultString.AppendLiteral(", "); resultString.AppendFloat(matrix._44); } resultString.Append(')'); /* Create a value to hold our result. */ RefPtr val = new nsROCSSPrimitiveValue; val->SetString(resultString); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetCounterReset() { const nsStyleContent *content = StyleContent(); if (content->CounterResetCount() == 0) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } RefPtr valueList = GetROCSSValueList(false); for (uint32_t i = 0, i_end = content->CounterResetCount(); i < i_end; ++i) { RefPtr name = new nsROCSSPrimitiveValue; RefPtr value = new nsROCSSPrimitiveValue; const nsStyleCounterData *data = content->GetCounterResetAt(i); nsAutoString escaped; nsStyleUtil::AppendEscapedCSSIdent(data->mCounter, escaped); name->SetString(escaped); value->SetNumber(data->mValue); // XXX This should really be integer valueList->AppendCSSValue(name.forget()); valueList->AppendCSSValue(value.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetQuotes() { const nsStyleQuotes *quotes = StyleQuotes(); if (quotes->QuotesCount() == 0) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } RefPtr valueList = GetROCSSValueList(false); for (uint32_t i = 0, i_end = quotes->QuotesCount(); i < i_end; ++i) { RefPtr openVal = new nsROCSSPrimitiveValue; RefPtr closeVal = new nsROCSSPrimitiveValue; nsString s; nsStyleUtil::AppendEscapedCSSString(*quotes->OpenQuoteAt(i), s); openVal->SetString(s); s.Truncate(); nsStyleUtil::AppendEscapedCSSString(*quotes->CloseQuoteAt(i), s); closeVal->SetString(s); valueList->AppendCSSValue(openVal.forget()); valueList->AppendCSSValue(closeVal.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontFamily() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleFont* font = StyleFont(); nsAutoString fontlistStr; nsStyleUtil::AppendEscapedCSSFontFamilyList(font->mFont.fontlist, fontlistStr); val->SetString(fontlistStr); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontSize() { RefPtr val = new nsROCSSPrimitiveValue; // Note: StyleFont()->mSize is the 'computed size'; // StyleFont()->mFont.size is the 'actual size' val->SetAppUnits(StyleFont()->mSize); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontSizeAdjust() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleFont *font = StyleFont(); if (font->mFont.sizeAdjust >= 0.0f) { val->SetNumber(font->mFont.sizeAdjust); } else { val->SetIdent(eCSSKeyword_none); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOsxFontSmoothing() { if (nsContentUtils::ShouldResistFingerprinting( mPresShell->GetPresContext()->GetDocShell())) return nullptr; RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleFont()->mFont.smoothing, nsCSSProps::kFontSmoothingKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontStretch() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleFont()->mFont.stretch, nsCSSProps::kFontStretchKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontStyle() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleFont()->mFont.style, nsCSSProps::kFontStyleKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontWeight() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleFont* font = StyleFont(); uint16_t weight = font->mFont.weight; NS_ASSERTION(weight % 100 == 0, "unexpected value of font-weight"); val->SetNumber(weight); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontFeatureSettings() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleFont* font = StyleFont(); if (font->mFont.fontFeatureSettings.IsEmpty()) { val->SetIdent(eCSSKeyword_normal); } else { nsAutoString result; nsStyleUtil::AppendFontFeatureSettings(font->mFont.fontFeatureSettings, result); val->SetString(result); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontKerning() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleFont()->mFont.kerning, nsCSSProps::kFontKerningKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontLanguageOverride() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleFont* font = StyleFont(); if (font->mFont.languageOverride.IsEmpty()) { val->SetIdent(eCSSKeyword_normal); } else { nsString str; nsStyleUtil::AppendEscapedCSSString(font->mFont.languageOverride, str); val->SetString(str); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontSynthesis() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleFont()->mFont.synthesis; if (0 == intValue) { val->SetIdent(eCSSKeyword_none); } else { nsAutoString valueStr; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_synthesis, intValue, NS_FONT_SYNTHESIS_WEIGHT, NS_FONT_SYNTHESIS_STYLE, valueStr); val->SetString(valueStr); } return val.forget(); } // return a value *only* for valid longhand values from CSS 2.1, either // normal or small-caps only already_AddRefed nsComputedDOMStyle::DoGetFontVariant() { const nsFont& f = StyleFont()->mFont; // if any of the other font-variant subproperties other than // font-variant-caps are not normal then can't calculate a computed value if (f.variantAlternates || f.variantEastAsian || f.variantLigatures || f.variantNumeric || f.variantPosition) { return nullptr; } nsCSSKeyword keyword; switch (f.variantCaps) { case 0: keyword = eCSSKeyword_normal; break; case NS_FONT_VARIANT_CAPS_SMALLCAPS: keyword = eCSSKeyword_small_caps; break; default: return nullptr; } RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(keyword); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontVariantAlternates() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleFont()->mFont.variantAlternates; if (0 == intValue) { val->SetIdent(eCSSKeyword_normal); return val.forget(); } // first, include enumerated values nsAutoString valueStr; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_variant_alternates, intValue & NS_FONT_VARIANT_ALTERNATES_ENUMERATED_MASK, NS_FONT_VARIANT_ALTERNATES_HISTORICAL, NS_FONT_VARIANT_ALTERNATES_HISTORICAL, valueStr); // next, include functional values if present if (intValue & NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK) { nsStyleUtil::SerializeFunctionalAlternates(StyleFont()->mFont.alternateValues, valueStr); } val->SetString(valueStr); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontVariantCaps() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleFont()->mFont.variantCaps; if (0 == intValue) { val->SetIdent(eCSSKeyword_normal); } else { val->SetIdent( nsCSSProps::ValueToKeywordEnum(intValue, nsCSSProps::kFontVariantCapsKTable)); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontVariantEastAsian() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleFont()->mFont.variantEastAsian; if (0 == intValue) { val->SetIdent(eCSSKeyword_normal); } else { nsAutoString valueStr; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_variant_east_asian, intValue, NS_FONT_VARIANT_EAST_ASIAN_JIS78, NS_FONT_VARIANT_EAST_ASIAN_RUBY, valueStr); val->SetString(valueStr); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontVariantLigatures() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleFont()->mFont.variantLigatures; if (0 == intValue) { val->SetIdent(eCSSKeyword_normal); } else if (NS_FONT_VARIANT_LIGATURES_NONE == intValue) { val->SetIdent(eCSSKeyword_none); } else { nsAutoString valueStr; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_variant_ligatures, intValue, NS_FONT_VARIANT_LIGATURES_NONE, NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL, valueStr); val->SetString(valueStr); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontVariantNumeric() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleFont()->mFont.variantNumeric; if (0 == intValue) { val->SetIdent(eCSSKeyword_normal); } else { nsAutoString valueStr; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_variant_numeric, intValue, NS_FONT_VARIANT_NUMERIC_LINING, NS_FONT_VARIANT_NUMERIC_ORDINAL, valueStr); val->SetString(valueStr); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFontVariantPosition() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleFont()->mFont.variantPosition; if (0 == intValue) { val->SetIdent(eCSSKeyword_normal); } else { val->SetIdent( nsCSSProps::ValueToKeywordEnum(intValue, nsCSSProps::kFontVariantPositionKTable)); } return val.forget(); } already_AddRefed nsComputedDOMStyle::GetBackgroundList(uint8_t nsStyleBackground::Layer::* aMember, uint32_t nsStyleBackground::* aCount, const KTableEntry aTable[]) { const nsStyleBackground* bg = StyleBackground(); RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = bg->*aCount; i < i_end; ++i) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(bg->mLayers[i].*aMember, aTable)); valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundAttachment() { return GetBackgroundList(&nsStyleBackground::Layer::mAttachment, &nsStyleBackground::mAttachmentCount, nsCSSProps::kBackgroundAttachmentKTable); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundClip() { return GetBackgroundList(&nsStyleBackground::Layer::mClip, &nsStyleBackground::mClipCount, nsCSSProps::kBackgroundOriginKTable); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundColor() { RefPtr val = new nsROCSSPrimitiveValue; SetToRGBAColor(val, StyleBackground()->mBackgroundColor); return val.forget(); } static void SetValueToCalc(const nsStyleCoord::CalcValue *aCalc, nsROCSSPrimitiveValue *aValue) { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString tmp, result; result.AppendLiteral("calc("); val->SetAppUnits(aCalc->mLength); val->GetCssText(tmp); result.Append(tmp); if (aCalc->mHasPercent) { result.AppendLiteral(" + "); val->SetPercent(aCalc->mPercent); val->GetCssText(tmp); result.Append(tmp); } result.Append(')'); aValue->SetString(result); // not really SetString } static void AppendCSSGradientLength(const nsStyleCoord& aValue, nsROCSSPrimitiveValue* aPrimitive, nsAString& aString) { nsAutoString tokenString; if (aValue.IsCalcUnit()) SetValueToCalc(aValue.GetCalcValue(), aPrimitive); else if (aValue.GetUnit() == eStyleUnit_Coord) aPrimitive->SetAppUnits(aValue.GetCoordValue()); else aPrimitive->SetPercent(aValue.GetPercentValue()); aPrimitive->GetCssText(tokenString); aString.Append(tokenString); } static void AppendCSSGradientToBoxPosition(const nsStyleGradient* aGradient, nsAString& aString, bool& aNeedSep) { float xValue = aGradient->mBgPosX.GetPercentValue(); float yValue = aGradient->mBgPosY.GetPercentValue(); if (yValue == 1.0f && xValue == 0.5f) { // omit "to bottom" return; } NS_ASSERTION(yValue != 0.5f || xValue != 0.5f, "invalid box position"); aString.AppendLiteral("to"); if (yValue == 0.0f) { aString.AppendLiteral(" top"); } else if (yValue == 1.0f) { aString.AppendLiteral(" bottom"); } else if (yValue != 0.5f) { // do not write "center" keyword NS_NOTREACHED("invalid box position"); } if (xValue == 0.0f) { aString.AppendLiteral(" left"); } else if (xValue == 1.0f) { aString.AppendLiteral(" right"); } else if (xValue != 0.5f) { // do not write "center" keyword NS_NOTREACHED("invalid box position"); } aNeedSep = true; } void nsComputedDOMStyle::GetCSSGradientString(const nsStyleGradient* aGradient, nsAString& aString) { if (!aGradient->mLegacySyntax) { aString.Truncate(); } else { aString.AssignLiteral("-moz-"); } if (aGradient->mRepeating) { aString.AppendLiteral("repeating-"); } bool isRadial = aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR; if (isRadial) { aString.AppendLiteral("radial-gradient("); } else { aString.AppendLiteral("linear-gradient("); } bool needSep = false; nsAutoString tokenString; RefPtr tmpVal = new nsROCSSPrimitiveValue; if (isRadial && !aGradient->mLegacySyntax) { if (aGradient->mSize != NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE) { if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) { aString.AppendLiteral("circle"); needSep = true; } if (aGradient->mSize != NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER) { if (needSep) { aString.Append(' '); } AppendASCIItoUTF16(nsCSSProps:: ValueToKeyword(aGradient->mSize, nsCSSProps::kRadialGradientSizeKTable), aString); needSep = true; } } else { AppendCSSGradientLength(aGradient->mRadiusX, tmpVal, aString); if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_CIRCULAR) { aString.Append(' '); AppendCSSGradientLength(aGradient->mRadiusY, tmpVal, aString); } needSep = true; } } if (aGradient->mBgPosX.GetUnit() != eStyleUnit_None) { MOZ_ASSERT(aGradient->mBgPosY.GetUnit() != eStyleUnit_None); if (!isRadial && !aGradient->mLegacySyntax) { AppendCSSGradientToBoxPosition(aGradient, aString, needSep); } else if (aGradient->mBgPosX.GetUnit() != eStyleUnit_Percent || aGradient->mBgPosX.GetPercentValue() != 0.5f || aGradient->mBgPosY.GetUnit() != eStyleUnit_Percent || aGradient->mBgPosY.GetPercentValue() != (isRadial ? 0.5f : 1.0f)) { if (isRadial && !aGradient->mLegacySyntax) { if (needSep) { aString.Append(' '); } aString.AppendLiteral("at "); needSep = false; } AppendCSSGradientLength(aGradient->mBgPosX, tmpVal, aString); if (aGradient->mBgPosY.GetUnit() != eStyleUnit_None) { aString.Append(' '); AppendCSSGradientLength(aGradient->mBgPosY, tmpVal, aString); } needSep = true; } } if (aGradient->mAngle.GetUnit() != eStyleUnit_None) { MOZ_ASSERT(!isRadial || aGradient->mLegacySyntax); if (needSep) { aString.Append(' '); } nsStyleUtil::AppendAngleValue(aGradient->mAngle, aString); needSep = true; } if (isRadial && aGradient->mLegacySyntax && (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR || aGradient->mSize != NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER)) { MOZ_ASSERT(aGradient->mSize != NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE); if (needSep) { aString.AppendLiteral(", "); needSep = false; } if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) { aString.AppendLiteral("circle"); needSep = true; } if (aGradient->mSize != NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER) { if (needSep) { aString.Append(' '); } AppendASCIItoUTF16(nsCSSProps:: ValueToKeyword(aGradient->mSize, nsCSSProps::kRadialGradientSizeKTable), aString); } needSep = true; } // color stops for (uint32_t i = 0; i < aGradient->mStops.Length(); ++i) { if (needSep) { aString.AppendLiteral(", "); } const auto& stop = aGradient->mStops[i]; if (!stop.mIsInterpolationHint) { SetToRGBAColor(tmpVal, stop.mColor); tmpVal->GetCssText(tokenString); aString.Append(tokenString); } if (stop.mLocation.GetUnit() != eStyleUnit_None) { if (!stop.mIsInterpolationHint) { aString.Append(' '); } AppendCSSGradientLength(stop.mLocation, tmpVal, aString); } needSep = true; } aString.Append(')'); } // -moz-image-rect(, , , , ) void nsComputedDOMStyle::GetImageRectString(nsIURI* aURI, const nsStyleSides& aCropRect, nsString& aString) { RefPtr valueList = GetROCSSValueList(true); // RefPtr valURI = new nsROCSSPrimitiveValue; valURI->SetURI(aURI); valueList->AppendCSSValue(valURI.forget()); // , , , NS_FOR_CSS_SIDES(side) { RefPtr valSide = new nsROCSSPrimitiveValue; SetValueToCoord(valSide, aCropRect.Get(side), false); valueList->AppendCSSValue(valSide.forget()); } nsAutoString argumentString; valueList->GetCssText(argumentString); aString = NS_LITERAL_STRING("-moz-image-rect(") + argumentString + NS_LITERAL_STRING(")"); } void nsComputedDOMStyle::SetValueToStyleImage(const nsStyleImage& aStyleImage, nsROCSSPrimitiveValue* aValue) { switch (aStyleImage.GetType()) { case eStyleImageType_Image: { imgIRequest *req = aStyleImage.GetImageData(); nsCOMPtr uri; req->GetURI(getter_AddRefs(uri)); const nsStyleSides* cropRect = aStyleImage.GetCropRect(); if (cropRect) { nsAutoString imageRectString; GetImageRectString(uri, *cropRect, imageRectString); aValue->SetString(imageRectString); } else { aValue->SetURI(uri); } break; } case eStyleImageType_Gradient: { nsAutoString gradientString; GetCSSGradientString(aStyleImage.GetGradientData(), gradientString); aValue->SetString(gradientString); break; } case eStyleImageType_Element: { nsAutoString elementId; nsStyleUtil::AppendEscapedCSSIdent( nsDependentString(aStyleImage.GetElementId()), elementId); nsAutoString elementString = NS_LITERAL_STRING("-moz-element(#") + elementId + NS_LITERAL_STRING(")"); aValue->SetString(elementString); break; } case eStyleImageType_Null: aValue->SetIdent(eCSSKeyword_none); break; default: NS_NOTREACHED("unexpected image type"); break; } } already_AddRefed nsComputedDOMStyle::DoGetBackgroundImage() { const nsStyleBackground* bg = StyleBackground(); RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = bg->mImageCount; i < i_end; ++i) { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleImage& image = bg->mLayers[i].mImage; SetValueToStyleImage(image, val); valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundBlendMode() { return GetBackgroundList(&nsStyleBackground::Layer::mBlendMode, &nsStyleBackground::mBlendModeCount, nsCSSProps::kBlendModeKTable); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundOrigin() { return GetBackgroundList(&nsStyleBackground::Layer::mOrigin, &nsStyleBackground::mOriginCount, nsCSSProps::kBackgroundOriginKTable); } void nsComputedDOMStyle::SetValueToPositionCoord( const nsStyleBackground::Position::PositionCoord& aCoord, nsROCSSPrimitiveValue* aValue) { if (!aCoord.mHasPercent) { MOZ_ASSERT(aCoord.mPercent == 0.0f, "Shouldn't have mPercent!"); aValue->SetAppUnits(aCoord.mLength); } else if (aCoord.mLength == 0) { aValue->SetPercent(aCoord.mPercent); } else { SetValueToCalc(&aCoord, aValue); } } void nsComputedDOMStyle::SetValueToPosition( const nsStyleBackground::Position& aPosition, nsDOMCSSValueList* aValueList) { RefPtr valX = new nsROCSSPrimitiveValue; SetValueToPositionCoord(aPosition.mXPosition, valX); aValueList->AppendCSSValue(valX.forget()); RefPtr valY = new nsROCSSPrimitiveValue; SetValueToPositionCoord(aPosition.mYPosition, valY); aValueList->AppendCSSValue(valY.forget()); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundPosition() { const nsStyleBackground* bg = StyleBackground(); RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = bg->mPositionCount; i < i_end; ++i) { RefPtr itemList = GetROCSSValueList(false); SetValueToPosition(bg->mLayers[i].mPosition, itemList); valueList->AppendCSSValue(itemList.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundRepeat() { const nsStyleBackground* bg = StyleBackground(); RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = bg->mRepeatCount; i < i_end; ++i) { RefPtr itemList = GetROCSSValueList(false); RefPtr valX = new nsROCSSPrimitiveValue; const uint8_t& xRepeat = bg->mLayers[i].mRepeat.mXRepeat; const uint8_t& yRepeat = bg->mLayers[i].mRepeat.mYRepeat; bool hasContraction = true; unsigned contraction; if (xRepeat == yRepeat) { contraction = xRepeat; } else if (xRepeat == NS_STYLE_BG_REPEAT_REPEAT && yRepeat == NS_STYLE_BG_REPEAT_NO_REPEAT) { contraction = NS_STYLE_BG_REPEAT_REPEAT_X; } else if (xRepeat == NS_STYLE_BG_REPEAT_NO_REPEAT && yRepeat == NS_STYLE_BG_REPEAT_REPEAT) { contraction = NS_STYLE_BG_REPEAT_REPEAT_Y; } else { hasContraction = false; } RefPtr valY; if (hasContraction) { valX->SetIdent(nsCSSProps::ValueToKeywordEnum(contraction, nsCSSProps::kBackgroundRepeatKTable)); } else { valY = new nsROCSSPrimitiveValue; valX->SetIdent(nsCSSProps::ValueToKeywordEnum(xRepeat, nsCSSProps::kBackgroundRepeatKTable)); valY->SetIdent(nsCSSProps::ValueToKeywordEnum(yRepeat, nsCSSProps::kBackgroundRepeatKTable)); } itemList->AppendCSSValue(valX.forget()); if (valY) { itemList->AppendCSSValue(valY.forget()); } valueList->AppendCSSValue(itemList.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundSize() { const nsStyleBackground* bg = StyleBackground(); RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = bg->mSizeCount; i < i_end; ++i) { const nsStyleBackground::Size &size = bg->mLayers[i].mSize; switch (size.mWidthType) { case nsStyleBackground::Size::eContain: case nsStyleBackground::Size::eCover: { MOZ_ASSERT(size.mWidthType == size.mHeightType, "unsynced types"); nsCSSKeyword keyword = size.mWidthType == nsStyleBackground::Size::eContain ? eCSSKeyword_contain : eCSSKeyword_cover; RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(keyword); valueList->AppendCSSValue(val.forget()); break; } default: { RefPtr itemList = GetROCSSValueList(false); RefPtr valX = new nsROCSSPrimitiveValue; RefPtr valY = new nsROCSSPrimitiveValue; if (size.mWidthType == nsStyleBackground::Size::eAuto) { valX->SetIdent(eCSSKeyword_auto); } else { MOZ_ASSERT(size.mWidthType == nsStyleBackground::Size::eLengthPercentage, "bad mWidthType"); if (!size.mWidth.mHasPercent && // negative values must have come from calc() size.mWidth.mLength >= 0) { MOZ_ASSERT(size.mWidth.mPercent == 0.0f, "Shouldn't have mPercent"); valX->SetAppUnits(size.mWidth.mLength); } else if (size.mWidth.mLength == 0 && // negative values must have come from calc() size.mWidth.mPercent >= 0.0f) { valX->SetPercent(size.mWidth.mPercent); } else { SetValueToCalc(&size.mWidth, valX); } } if (size.mHeightType == nsStyleBackground::Size::eAuto) { valY->SetIdent(eCSSKeyword_auto); } else { MOZ_ASSERT(size.mHeightType == nsStyleBackground::Size::eLengthPercentage, "bad mHeightType"); if (!size.mHeight.mHasPercent && // negative values must have come from calc() size.mHeight.mLength >= 0) { MOZ_ASSERT(size.mHeight.mPercent == 0.0f, "Shouldn't have mPercent"); valY->SetAppUnits(size.mHeight.mLength); } else if (size.mHeight.mLength == 0 && // negative values must have come from calc() size.mHeight.mPercent >= 0.0f) { valY->SetPercent(size.mHeight.mPercent); } else { SetValueToCalc(&size.mHeight, valY); } } itemList->AppendCSSValue(valX.forget()); itemList->AppendCSSValue(valY.forget()); valueList->AppendCSSValue(itemList.forget()); break; } } } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetGridTemplateAreas() { const css::GridTemplateAreasValue* areas = StylePosition()->mGridTemplateAreas; if (!areas) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } MOZ_ASSERT(!areas->mTemplates.IsEmpty(), "Unexpected empty array in GridTemplateAreasValue"); RefPtr valueList = GetROCSSValueList(false); for (uint32_t i = 0; i < areas->mTemplates.Length(); i++) { nsAutoString str; nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[i], str); RefPtr val = new nsROCSSPrimitiveValue; val->SetString(str); valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } void nsComputedDOMStyle::AppendGridLineNames(nsString& aResult, const nsTArray& aLineNames) { MOZ_ASSERT(!aLineNames.IsEmpty(), "expected some line names"); uint32_t numLines = aLineNames.Length(); for (uint32_t i = 0;;) { nsStyleUtil::AppendEscapedCSSIdent(aLineNames[i], aResult); if (++i == numLines) { break; } aResult.Append(' '); } } void nsComputedDOMStyle::AppendGridLineNames(nsDOMCSSValueList* aValueList, const nsTArray& aLineNames) { if (aLineNames.IsEmpty()) { return; } RefPtr val = new nsROCSSPrimitiveValue; nsAutoString lineNamesString; lineNamesString.Assign('['); AppendGridLineNames(lineNamesString, aLineNames); lineNamesString.Append(']'); val->SetString(lineNamesString); aValueList->AppendCSSValue(val.forget()); } void nsComputedDOMStyle::AppendGridLineNames(nsDOMCSSValueList* aValueList, const nsTArray& aLineNames1, const nsTArray& aLineNames2) { if (aLineNames1.IsEmpty() && aLineNames2.IsEmpty()) { return; } RefPtr val = new nsROCSSPrimitiveValue; nsAutoString lineNamesString; lineNamesString.Assign('['); if (!aLineNames1.IsEmpty()) { AppendGridLineNames(lineNamesString, aLineNames1); } if (!aLineNames2.IsEmpty()) { if (!aLineNames1.IsEmpty()) { lineNamesString.Append(' '); } AppendGridLineNames(lineNamesString, aLineNames2); } lineNamesString.Append(']'); val->SetString(lineNamesString); aValueList->AppendCSSValue(val.forget()); } already_AddRefed nsComputedDOMStyle::GetGridTrackSize(const nsStyleCoord& aMinValue, const nsStyleCoord& aMaxValue) { if (aMinValue == aMaxValue) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, aMinValue, true, nullptr, nsCSSProps::kGridTrackBreadthKTable); return val.forget(); } RefPtr val = new nsROCSSPrimitiveValue; nsAutoString argumentStr, minmaxStr; minmaxStr.AppendLiteral("minmax("); SetValueToCoord(val, aMinValue, true, nullptr, nsCSSProps::kGridTrackBreadthKTable); val->GetCssText(argumentStr); minmaxStr.Append(argumentStr); minmaxStr.AppendLiteral(", "); SetValueToCoord(val, aMaxValue, true, nullptr, nsCSSProps::kGridTrackBreadthKTable); val->GetCssText(argumentStr); minmaxStr.Append(argumentStr); minmaxStr.Append(char16_t(')')); val->SetString(minmaxStr); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetGridTemplateColumnsRows(const nsStyleGridTemplate& aTrackList, const nsTArray* aTrackSizes) { if (aTrackList.mIsSubgrid) { // XXX TODO: add support for repeat(auto-fill) for 'subgrid' (bug 1234311) NS_ASSERTION(aTrackList.mMinTrackSizingFunctions.IsEmpty() && aTrackList.mMaxTrackSizingFunctions.IsEmpty(), "Unexpected sizing functions with subgrid"); RefPtr valueList = GetROCSSValueList(false); RefPtr subgridKeyword = new nsROCSSPrimitiveValue; subgridKeyword->SetIdent(eCSSKeyword_subgrid); valueList->AppendCSSValue(subgridKeyword.forget()); for (uint32_t i = 0; i < aTrackList.mLineNameLists.Length(); i++) { if (MOZ_UNLIKELY(aTrackList.IsRepeatAutoIndex(i))) { MOZ_ASSERT(aTrackList.mIsAutoFill, "subgrid can only have 'auto-fill'"); MOZ_ASSERT(!aTrackList.mRepeatAutoLineNameListBefore.IsEmpty(), "The syntax is + so this shouldn't be empty"); MOZ_ASSERT(aTrackList.mRepeatAutoLineNameListAfter.IsEmpty(), "mRepeatAutoLineNameListAfter isn't used for subgrid"); RefPtr start = new nsROCSSPrimitiveValue; start->SetString(NS_LITERAL_STRING("repeat(auto-fill,")); valueList->AppendCSSValue(start.forget()); AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListBefore); RefPtr end = new nsROCSSPrimitiveValue; end->SetString(NS_LITERAL_STRING(")")); valueList->AppendCSSValue(end.forget()); } AppendGridLineNames(valueList, aTrackList.mLineNameLists[i]); } return valueList.forget(); } uint32_t numSizes = aTrackList.mMinTrackSizingFunctions.Length(); MOZ_ASSERT(aTrackList.mMaxTrackSizingFunctions.Length() == numSizes, "Different number of min and max track sizing functions"); if (aTrackSizes) { numSizes = aTrackSizes->Length(); MOZ_ASSERT(numSizes > 0 || (aTrackList.HasRepeatAuto() && !aTrackList.mIsAutoFill), "only 'auto-fit' can result in zero tracks"); } // An empty is represented as "none" in syntax. if (numSizes == 0) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } // Delimiting N (specified) tracks requires N+1 lines: // one before each track, plus one at the very end. MOZ_ASSERT(aTrackList.mLineNameLists.Length() == aTrackList.mMinTrackSizingFunctions.Length() + 1, "Unexpected number of line name lists"); RefPtr valueList = GetROCSSValueList(false); if (aTrackSizes) { // We've done layout on the grid and have resolved the sizes of its tracks, // so we'll return those sizes here. The grid spec says we MAY use // repeat(, Npx) here for consecutive tracks with the same // size, but that doesn't seem worth doing since even for repeat(auto-*) // the resolved size might differ for the repeated tracks. int32_t endOfRepeat = 0; // first index after any repeat() tracks int32_t offsetToLastRepeat = 0; if (aTrackList.HasRepeatAuto()) { // offsetToLastRepeat is -1 if all repeat(auto-fit) tracks are empty offsetToLastRepeat = numSizes + 1 - aTrackList.mLineNameLists.Length(); endOfRepeat = aTrackList.mRepeatAutoIndex + offsetToLastRepeat + 1; } MOZ_ASSERT(numSizes > 0); for (int32_t i = 0;; i++) { if (aTrackList.HasRepeatAuto()) { if (i == aTrackList.mRepeatAutoIndex) { const nsTArray& lineNames = aTrackList.mLineNameLists[i]; if (i == endOfRepeat) { // all auto-fit tracks are empty, so "[a] repeat(...) [b]" // becomes "[a b]" AppendGridLineNames(valueList, lineNames, aTrackList.mLineNameLists[i + 1]); } else { AppendGridLineNames(valueList, lineNames, aTrackList.mRepeatAutoLineNameListBefore); } } else if (i == endOfRepeat) { const nsTArray& lineNames = aTrackList.mLineNameLists[aTrackList.mRepeatAutoIndex + 1]; AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListAfter, lineNames); } else if (i > aTrackList.mRepeatAutoIndex && i < endOfRepeat) { AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListAfter, aTrackList.mRepeatAutoLineNameListBefore); } else { uint32_t j = i > endOfRepeat ? i - offsetToLastRepeat : i; const nsTArray& lineNames = aTrackList.mLineNameLists[j]; AppendGridLineNames(valueList, lineNames); } } else { const nsTArray& lineNames = aTrackList.mLineNameLists[i]; AppendGridLineNames(valueList, lineNames); } if (uint32_t(i) == numSizes) { break; } RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits((*aTrackSizes)[i]); valueList->AppendCSSValue(val.forget()); } } else { // We don't have a frame. So, we'll just return a serialization of // the tracks from the style (without resolved sizes). for (uint32_t i = 0;; i++) { const nsTArray& lineNames = aTrackList.mLineNameLists[i]; if (!lineNames.IsEmpty()) { AppendGridLineNames(valueList, lineNames); } if (i == numSizes) { break; } if (MOZ_UNLIKELY(aTrackList.IsRepeatAutoIndex(i))) { RefPtr start = new nsROCSSPrimitiveValue; start->SetString(aTrackList.mIsAutoFill ? NS_LITERAL_STRING("repeat(auto-fill,") : NS_LITERAL_STRING("repeat(auto-fit,")); valueList->AppendCSSValue(start.forget()); if (!aTrackList.mRepeatAutoLineNameListBefore.IsEmpty()) { AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListBefore); } valueList->AppendCSSValue( GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i], aTrackList.mMaxTrackSizingFunctions[i])); if (!aTrackList.mRepeatAutoLineNameListAfter.IsEmpty()) { AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListAfter); } RefPtr end = new nsROCSSPrimitiveValue; end->SetString(NS_LITERAL_STRING(")")); valueList->AppendCSSValue(end.forget()); } else { valueList->AppendCSSValue( GetGridTrackSize(aTrackList.mMinTrackSizingFunctions[i], aTrackList.mMaxTrackSizingFunctions[i])); } } } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetGridAutoFlow() { nsAutoString str; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_grid_auto_flow, StylePosition()->mGridAutoFlow, NS_STYLE_GRID_AUTO_FLOW_ROW, NS_STYLE_GRID_AUTO_FLOW_DENSE, str); RefPtr val = new nsROCSSPrimitiveValue; val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetGridAutoColumns() { return GetGridTrackSize(StylePosition()->mGridAutoColumnsMin, StylePosition()->mGridAutoColumnsMax); } already_AddRefed nsComputedDOMStyle::DoGetGridAutoRows() { return GetGridTrackSize(StylePosition()->mGridAutoRowsMin, StylePosition()->mGridAutoRowsMax); } already_AddRefed nsComputedDOMStyle::DoGetGridTemplateColumns() { const nsTArray* trackSizes = nullptr; if (mInnerFrame) { nsIFrame* gridContainerCandidate = mInnerFrame->GetContentInsertionFrame(); if (gridContainerCandidate && gridContainerCandidate->GetType() == nsGkAtoms::gridContainerFrame) { auto gridContainer = static_cast(gridContainerCandidate); trackSizes = gridContainer->GetComputedTemplateColumns(); } } return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns, trackSizes); } already_AddRefed nsComputedDOMStyle::DoGetGridTemplateRows() { const nsTArray* trackSizes = nullptr; if (mInnerFrame) { nsIFrame* gridContainerCandidate = mInnerFrame->GetContentInsertionFrame(); if (gridContainerCandidate && gridContainerCandidate->GetType() == nsGkAtoms::gridContainerFrame) { auto gridContainer = static_cast(gridContainerCandidate); trackSizes = gridContainer->GetComputedTemplateRows(); } } return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, trackSizes); } already_AddRefed nsComputedDOMStyle::GetGridLine(const nsStyleGridLine& aGridLine) { if (aGridLine.IsAuto()) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_auto); return val.forget(); } RefPtr valueList = GetROCSSValueList(false); if (aGridLine.mHasSpan) { RefPtr span = new nsROCSSPrimitiveValue; span->SetIdent(eCSSKeyword_span); valueList->AppendCSSValue(span.forget()); } if (aGridLine.mInteger != 0) { RefPtr integer = new nsROCSSPrimitiveValue; integer->SetNumber(aGridLine.mInteger); valueList->AppendCSSValue(integer.forget()); } if (!aGridLine.mLineName.IsEmpty()) { RefPtr lineName = new nsROCSSPrimitiveValue; nsString escapedLineName; nsStyleUtil::AppendEscapedCSSIdent(aGridLine.mLineName, escapedLineName); lineName->SetString(escapedLineName); valueList->AppendCSSValue(lineName.forget()); } NS_ASSERTION(valueList->Length() > 0, "Should have appended at least one value"); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetGridColumnStart() { return GetGridLine(StylePosition()->mGridColumnStart); } already_AddRefed nsComputedDOMStyle::DoGetGridColumnEnd() { return GetGridLine(StylePosition()->mGridColumnEnd); } already_AddRefed nsComputedDOMStyle::DoGetGridRowStart() { return GetGridLine(StylePosition()->mGridRowStart); } already_AddRefed nsComputedDOMStyle::DoGetGridRowEnd() { return GetGridLine(StylePosition()->mGridRowEnd); } already_AddRefed nsComputedDOMStyle::DoGetGridColumnGap() { RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(StylePosition()->mGridColumnGap); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetGridRowGap() { RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(StylePosition()->mGridRowGap); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPaddingTop() { return GetPaddingWidthFor(NS_SIDE_TOP); } already_AddRefed nsComputedDOMStyle::DoGetPaddingBottom() { return GetPaddingWidthFor(NS_SIDE_BOTTOM); } already_AddRefed nsComputedDOMStyle::DoGetPaddingLeft() { return GetPaddingWidthFor(NS_SIDE_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetPaddingRight() { return GetPaddingWidthFor(NS_SIDE_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetBorderCollapse() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleTableBorder()->mBorderCollapse, nsCSSProps::kBorderCollapseKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBorderSpacing() { RefPtr valueList = GetROCSSValueList(false); RefPtr xSpacing = new nsROCSSPrimitiveValue; RefPtr ySpacing = new nsROCSSPrimitiveValue; const nsStyleTableBorder *border = StyleTableBorder(); xSpacing->SetAppUnits(border->mBorderSpacingCol); ySpacing->SetAppUnits(border->mBorderSpacingRow); valueList->AppendCSSValue(xSpacing.forget()); valueList->AppendCSSValue(ySpacing.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetCaptionSide() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleTableBorder()->mCaptionSide, nsCSSProps::kCaptionSideKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetEmptyCells() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleTableBorder()->mEmptyCells, nsCSSProps::kEmptyCellsKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTableLayout() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleTable()->mLayoutStrategy, nsCSSProps::kTableLayoutKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBorderTopStyle() { return GetBorderStyleFor(NS_SIDE_TOP); } already_AddRefed nsComputedDOMStyle::DoGetBorderBottomStyle() { return GetBorderStyleFor(NS_SIDE_BOTTOM); } already_AddRefed nsComputedDOMStyle::DoGetBorderLeftStyle() { return GetBorderStyleFor(NS_SIDE_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetBorderRightStyle() { return GetBorderStyleFor(NS_SIDE_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetBorderBottomColors() { return GetBorderColorsFor(NS_SIDE_BOTTOM); } already_AddRefed nsComputedDOMStyle::DoGetBorderLeftColors() { return GetBorderColorsFor(NS_SIDE_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetBorderRightColors() { return GetBorderColorsFor(NS_SIDE_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetBorderTopColors() { return GetBorderColorsFor(NS_SIDE_TOP); } already_AddRefed nsComputedDOMStyle::DoGetBorderBottomLeftRadius() { return GetEllipseRadii(StyleBorder()->mBorderRadius, NS_CORNER_BOTTOM_LEFT, true); } already_AddRefed nsComputedDOMStyle::DoGetBorderBottomRightRadius() { return GetEllipseRadii(StyleBorder()->mBorderRadius, NS_CORNER_BOTTOM_RIGHT, true); } already_AddRefed nsComputedDOMStyle::DoGetBorderTopLeftRadius() { return GetEllipseRadii(StyleBorder()->mBorderRadius, NS_CORNER_TOP_LEFT, true); } already_AddRefed nsComputedDOMStyle::DoGetBorderTopRightRadius() { return GetEllipseRadii(StyleBorder()->mBorderRadius, NS_CORNER_TOP_RIGHT, true); } already_AddRefed nsComputedDOMStyle::DoGetBorderTopWidth() { return GetBorderWidthFor(NS_SIDE_TOP); } already_AddRefed nsComputedDOMStyle::DoGetBorderBottomWidth() { return GetBorderWidthFor(NS_SIDE_BOTTOM); } already_AddRefed nsComputedDOMStyle::DoGetBorderLeftWidth() { return GetBorderWidthFor(NS_SIDE_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetBorderRightWidth() { return GetBorderWidthFor(NS_SIDE_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetBorderTopColor() { return GetBorderColorFor(NS_SIDE_TOP); } already_AddRefed nsComputedDOMStyle::DoGetBorderBottomColor() { return GetBorderColorFor(NS_SIDE_BOTTOM); } already_AddRefed nsComputedDOMStyle::DoGetBorderLeftColor() { return GetBorderColorFor(NS_SIDE_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetBorderRightColor() { return GetBorderColorFor(NS_SIDE_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetMarginTopWidth() { return GetMarginWidthFor(NS_SIDE_TOP); } already_AddRefed nsComputedDOMStyle::DoGetMarginBottomWidth() { return GetMarginWidthFor(NS_SIDE_BOTTOM); } already_AddRefed nsComputedDOMStyle::DoGetMarginLeftWidth() { return GetMarginWidthFor(NS_SIDE_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetMarginRightWidth() { return GetMarginWidthFor(NS_SIDE_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetMarkerOffset() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleContent()->mMarkerOffset, false); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOrient() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOrient, nsCSSProps::kOrientKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetScrollBehavior() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mScrollBehavior, nsCSSProps::kScrollBehaviorKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetScrollSnapType() { const nsStyleDisplay* display = StyleDisplay(); if (display->mScrollSnapTypeX != display->mScrollSnapTypeY) { // No value to return. We can't express this combination of // values as a shorthand. return nullptr; } RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mScrollSnapTypeX, nsCSSProps::kScrollSnapTypeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetScrollSnapTypeX() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mScrollSnapTypeX, nsCSSProps::kScrollSnapTypeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetScrollSnapTypeY() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mScrollSnapTypeY, nsCSSProps::kScrollSnapTypeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetScrollSnapPoints(const nsStyleCoord& aCoord) { RefPtr val = new nsROCSSPrimitiveValue; if (aCoord.GetUnit() == eStyleUnit_None) { val->SetIdent(eCSSKeyword_none); } else { nsAutoString argumentString; SetCssTextToCoord(argumentString, aCoord); nsAutoString tmp; tmp.AppendLiteral("repeat("); tmp.Append(argumentString); tmp.Append(')'); val->SetString(tmp); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetScrollSnapPointsX() { return GetScrollSnapPoints(StyleDisplay()->mScrollSnapPointsX); } already_AddRefed nsComputedDOMStyle::DoGetScrollSnapPointsY() { return GetScrollSnapPoints(StyleDisplay()->mScrollSnapPointsY); } already_AddRefed nsComputedDOMStyle::DoGetScrollSnapDestination() { RefPtr valueList = GetROCSSValueList(false); SetValueToPosition(StyleDisplay()->mScrollSnapDestination, valueList); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetScrollSnapCoordinate() { const nsStyleDisplay* sd = StyleDisplay(); if (sd->mScrollSnapCoordinate.IsEmpty()) { // Having no snap coordinates is interpreted as "none" RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } else { RefPtr valueList = GetROCSSValueList(true); for (size_t i = 0, i_end = sd->mScrollSnapCoordinate.Length(); i < i_end; ++i) { RefPtr itemList = GetROCSSValueList(false); SetValueToPosition(sd->mScrollSnapCoordinate[i], itemList); valueList->AppendCSSValue(itemList.forget()); } return valueList.forget(); } } already_AddRefed nsComputedDOMStyle::DoGetOutlineWidth() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleOutline* outline = StyleOutline(); nscoord width; if (outline->GetOutlineStyle() == NS_STYLE_BORDER_STYLE_NONE) { NS_ASSERTION(outline->GetOutlineWidth(width) && width == 0, "unexpected width"); width = 0; } else { #ifdef DEBUG bool res = #endif outline->GetOutlineWidth(width); NS_ASSERTION(res, "percent outline doesn't exist"); } val->SetAppUnits(width); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOutlineStyle() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleOutline()->GetOutlineStyle(), nsCSSProps::kOutlineStyleKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOutlineOffset() { RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(StyleOutline()->mOutlineOffset); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOutlineRadiusBottomLeft() { return GetEllipseRadii(StyleOutline()->mOutlineRadius, NS_CORNER_BOTTOM_LEFT, false); } already_AddRefed nsComputedDOMStyle::DoGetOutlineRadiusBottomRight() { return GetEllipseRadii(StyleOutline()->mOutlineRadius, NS_CORNER_BOTTOM_RIGHT, false); } already_AddRefed nsComputedDOMStyle::DoGetOutlineRadiusTopLeft() { return GetEllipseRadii(StyleOutline()->mOutlineRadius, NS_CORNER_TOP_LEFT, false); } already_AddRefed nsComputedDOMStyle::DoGetOutlineRadiusTopRight() { return GetEllipseRadii(StyleOutline()->mOutlineRadius, NS_CORNER_TOP_RIGHT, false); } already_AddRefed nsComputedDOMStyle::DoGetOutlineColor() { RefPtr val = new nsROCSSPrimitiveValue; nscolor color; if (!StyleOutline()->GetOutlineColor(color)) color = StyleColor()->mColor; SetToRGBAColor(val, color); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetEllipseRadii(const nsStyleCorners& aRadius, uint8_t aFullCorner, bool aIsBorder) // else outline { nsStyleCoord radiusX, radiusY; if (mInnerFrame && aIsBorder) { nscoord radii[8]; mInnerFrame->GetBorderRadii(radii); radiusX.SetCoordValue(radii[NS_FULL_TO_HALF_CORNER(aFullCorner, false)]); radiusY.SetCoordValue(radii[NS_FULL_TO_HALF_CORNER(aFullCorner, true)]); } else { radiusX = aRadius.Get(NS_FULL_TO_HALF_CORNER(aFullCorner, false)); radiusY = aRadius.Get(NS_FULL_TO_HALF_CORNER(aFullCorner, true)); if (mInnerFrame) { // We need to convert to absolute coordinates before doing the // equality check below. nscoord v; v = StyleCoordToNSCoord(radiusX, &nsComputedDOMStyle::GetFrameBorderRectWidth, 0, true); radiusX.SetCoordValue(v); v = StyleCoordToNSCoord(radiusY, &nsComputedDOMStyle::GetFrameBorderRectHeight, 0, true); radiusY.SetCoordValue(v); } } // for compatibility, return a single value if X and Y are equal if (radiusX == radiusY) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, radiusX, true); return val.forget(); } RefPtr valueList = GetROCSSValueList(false); RefPtr valX = new nsROCSSPrimitiveValue; RefPtr valY = new nsROCSSPrimitiveValue; SetValueToCoord(valX, radiusX, true); SetValueToCoord(valY, radiusY, true); valueList->AppendCSSValue(valX.forget()); valueList->AppendCSSValue(valY.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::GetCSSShadowArray(nsCSSShadowArray* aArray, const nscolor& aDefaultColor, bool aIsBoxShadow) { if (!aArray) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } static nscoord nsCSSShadowItem::* const shadowValuesNoSpread[] = { &nsCSSShadowItem::mXOffset, &nsCSSShadowItem::mYOffset, &nsCSSShadowItem::mRadius }; static nscoord nsCSSShadowItem::* const shadowValuesWithSpread[] = { &nsCSSShadowItem::mXOffset, &nsCSSShadowItem::mYOffset, &nsCSSShadowItem::mRadius, &nsCSSShadowItem::mSpread }; nscoord nsCSSShadowItem::* const * shadowValues; uint32_t shadowValuesLength; if (aIsBoxShadow) { shadowValues = shadowValuesWithSpread; shadowValuesLength = ArrayLength(shadowValuesWithSpread); } else { shadowValues = shadowValuesNoSpread; shadowValuesLength = ArrayLength(shadowValuesNoSpread); } RefPtr valueList = GetROCSSValueList(true); for (nsCSSShadowItem *item = aArray->ShadowAt(0), *item_end = item + aArray->Length(); item < item_end; ++item) { RefPtr itemList = GetROCSSValueList(false); // Color is either the specified shadow color or the foreground color RefPtr val = new nsROCSSPrimitiveValue; nscolor shadowColor; if (item->mHasColor) { shadowColor = item->mColor; } else { shadowColor = aDefaultColor; } SetToRGBAColor(val, shadowColor); itemList->AppendCSSValue(val.forget()); // Set the offsets, blur radius, and spread if available for (uint32_t i = 0; i < shadowValuesLength; ++i) { val = new nsROCSSPrimitiveValue; val->SetAppUnits(item->*(shadowValues[i])); itemList->AppendCSSValue(val.forget()); } if (item->mInset && aIsBoxShadow) { // This is an inset box-shadow val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(NS_STYLE_BOX_SHADOW_INSET, nsCSSProps::kBoxShadowTypeKTable)); itemList->AppendCSSValue(val.forget()); } valueList->AppendCSSValue(itemList.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxDecorationBreak() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleBorder()->mBoxDecorationBreak, nsCSSProps::kBoxDecorationBreakKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxShadow() { return GetCSSShadowArray(StyleBorder()->mBoxShadow, StyleColor()->mColor, true); } already_AddRefed nsComputedDOMStyle::DoGetZIndex() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StylePosition()->mZIndex, false); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetListStyleImage() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleList* list = StyleList(); if (!list->GetListStyleImage()) { val->SetIdent(eCSSKeyword_none); } else { nsCOMPtr uri; if (list->GetListStyleImage()) { list->GetListStyleImage()->GetURI(getter_AddRefs(uri)); } val->SetURI(uri); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetListStylePosition() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleList()->mListStylePosition, nsCSSProps::kListStylePositionKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetListStyleType() { RefPtr val = new nsROCSSPrimitiveValue; CounterStyle* style = StyleList()->GetCounterStyle(); AnonymousCounterStyle* anonymous = style->AsAnonymous(); nsString tmp; if (!anonymous) { // want SetIdent nsString type; StyleList()->GetListStyleType(type); nsStyleUtil::AppendEscapedCSSIdent(type, tmp); } else if (anonymous->IsSingleString()) { const nsTArray& symbols = anonymous->GetSymbols(); MOZ_ASSERT(symbols.Length() == 1); nsStyleUtil::AppendEscapedCSSString(symbols[0], tmp); } else { tmp.AppendLiteral("symbols("); uint8_t system = anonymous->GetSystem(); NS_ASSERTION(system == NS_STYLE_COUNTER_SYSTEM_CYCLIC || system == NS_STYLE_COUNTER_SYSTEM_NUMERIC || system == NS_STYLE_COUNTER_SYSTEM_ALPHABETIC || system == NS_STYLE_COUNTER_SYSTEM_SYMBOLIC || system == NS_STYLE_COUNTER_SYSTEM_FIXED, "Invalid system for anonymous counter style."); if (system != NS_STYLE_COUNTER_SYSTEM_SYMBOLIC) { AppendASCIItoUTF16(nsCSSProps::ValueToKeyword( system, nsCSSProps::kCounterSystemKTable), tmp); tmp.Append(' '); } const nsTArray& symbols = anonymous->GetSymbols(); NS_ASSERTION(symbols.Length() > 0, "No symbols in the anonymous counter style"); for (size_t i = 0, iend = symbols.Length(); i < iend; i++) { nsStyleUtil::AppendEscapedCSSString(symbols[i], tmp); tmp.Append(' '); } tmp.Replace(tmp.Length() - 1, 1, char16_t(')')); } val->SetString(tmp); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetImageRegion() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleList* list = StyleList(); if (list->mImageRegion.width <= 0 || list->mImageRegion.height <= 0) { val->SetIdent(eCSSKeyword_auto); } else { // create the cssvalues for the sides, stick them in the rect object nsROCSSPrimitiveValue *topVal = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *rightVal = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *bottomVal = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *leftVal = new nsROCSSPrimitiveValue; nsDOMCSSRect * domRect = new nsDOMCSSRect(topVal, rightVal, bottomVal, leftVal); topVal->SetAppUnits(list->mImageRegion.y); rightVal->SetAppUnits(list->mImageRegion.width + list->mImageRegion.x); bottomVal->SetAppUnits(list->mImageRegion.height + list->mImageRegion.y); leftVal->SetAppUnits(list->mImageRegion.x); val->SetRect(domRect); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetLineHeight() { RefPtr val = new nsROCSSPrimitiveValue; nscoord lineHeight; if (GetLineHeightCoord(lineHeight)) { val->SetAppUnits(lineHeight); } else { SetValueToCoord(val, StyleText()->mLineHeight, true, nullptr, nsCSSProps::kLineHeightKTable); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetRubyAlign() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum( StyleText()->mRubyAlign, nsCSSProps::kRubyAlignKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetRubyPosition() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum( StyleText()->mRubyPosition, nsCSSProps::kRubyPositionKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetVerticalAlign() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleTextReset()->mVerticalAlign, false, &nsComputedDOMStyle::GetLineHeightCoord, nsCSSProps::kVerticalAlignKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::CreateTextAlignValue(uint8_t aAlign, bool aAlignTrue, const KTableEntry aTable[]) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(aAlign, aTable)); if (!aAlignTrue) { return val.forget(); } RefPtr first = new nsROCSSPrimitiveValue; first->SetIdent(eCSSKeyword_true); RefPtr valueList = GetROCSSValueList(false); valueList->AppendCSSValue(first.forget()); valueList->AppendCSSValue(val.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextAlign() { const nsStyleText* style = StyleText(); return CreateTextAlignValue(style->mTextAlign, style->mTextAlignTrue, nsCSSProps::kTextAlignKTable); } already_AddRefed nsComputedDOMStyle::DoGetTextAlignLast() { const nsStyleText* style = StyleText(); return CreateTextAlignValue(style->mTextAlignLast, style->mTextAlignLastTrue, nsCSSProps::kTextAlignLastKTable); } already_AddRefed nsComputedDOMStyle::DoGetTextCombineUpright() { RefPtr val = new nsROCSSPrimitiveValue; uint8_t tch = StyleText()->mTextCombineUpright; if (tch <= NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) { val->SetIdent( nsCSSProps::ValueToKeywordEnum(tch, nsCSSProps::kTextCombineUprightKTable)); } else if (tch <= NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_2) { val->SetString(NS_LITERAL_STRING("digits 2")); } else if (tch <= NS_STYLE_TEXT_COMBINE_UPRIGHT_DIGITS_3) { val->SetString(NS_LITERAL_STRING("digits 3")); } else { val->SetString(NS_LITERAL_STRING("digits 4")); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextDecoration() { const nsStyleTextReset* textReset = StyleTextReset(); bool isInitialStyle = textReset->GetDecorationStyle() == NS_STYLE_TEXT_DECORATION_STYLE_SOLID; nscolor color; bool isForegroundColor; textReset->GetDecorationColor(color, isForegroundColor); if (isInitialStyle && isForegroundColor) { return DoGetTextDecorationLine(); } RefPtr valueList = GetROCSSValueList(false); valueList->AppendCSSValue(DoGetTextDecorationLine()); if (!isInitialStyle) { valueList->AppendCSSValue(DoGetTextDecorationStyle()); } if (!isForegroundColor) { valueList->AppendCSSValue(DoGetTextDecorationColor()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextDecorationColor() { RefPtr val = new nsROCSSPrimitiveValue; nscolor color; bool isForeground; StyleTextReset()->GetDecorationColor(color, isForeground); if (isForeground) { color = StyleColor()->mColor; } SetToRGBAColor(val, color); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextDecorationLine() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleTextReset()->mTextDecorationLine; if (NS_STYLE_TEXT_DECORATION_LINE_NONE == intValue) { val->SetIdent(eCSSKeyword_none); } else { nsAutoString decorationLineString; // Clear the -moz-anchor-decoration bit and the OVERRIDE_ALL bits -- we // don't want these to appear in the computed style. intValue &= ~(NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS | NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL); nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_text_decoration_line, intValue, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, NS_STYLE_TEXT_DECORATION_LINE_BLINK, decorationLineString); val->SetString(decorationLineString); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextDecorationStyle() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleTextReset()->GetDecorationStyle(), nsCSSProps::kTextDecorationStyleKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextEmphasisColor() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleText* text = StyleText(); nscolor color = text->mTextEmphasisColorForeground ? StyleColor()->mColor : text->mTextEmphasisColor; SetToRGBAColor(val, color); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextEmphasisPosition() { auto position = StyleText()->mTextEmphasisPosition; MOZ_ASSERT(!(position & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER) != !(position & NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER)); RefPtr first = new nsROCSSPrimitiveValue; first->SetIdent((position & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER) ? eCSSKeyword_over : eCSSKeyword_under); MOZ_ASSERT(!(position & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT) != !(position & NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT)); RefPtr second = new nsROCSSPrimitiveValue; second->SetIdent((position & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT) ? eCSSKeyword_left : eCSSKeyword_right); RefPtr valueList = GetROCSSValueList(false); valueList->AppendCSSValue(first.forget()); valueList->AppendCSSValue(second.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextEmphasisStyle() { auto style = StyleText()->mTextEmphasisStyle; if (style == NS_STYLE_TEXT_EMPHASIS_STYLE_NONE) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } if (style == NS_STYLE_TEXT_EMPHASIS_STYLE_STRING) { RefPtr val = new nsROCSSPrimitiveValue; nsString tmp; nsStyleUtil::AppendEscapedCSSString( StyleText()->mTextEmphasisStyleString, tmp); val->SetString(tmp); return val.forget(); } RefPtr fillVal = new nsROCSSPrimitiveValue; if ((style & NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK) == NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED) { fillVal->SetIdent(eCSSKeyword_filled); } else { MOZ_ASSERT((style & NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK) == NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN); fillVal->SetIdent(eCSSKeyword_open); } RefPtr shapeVal = new nsROCSSPrimitiveValue; shapeVal->SetIdent(nsCSSProps::ValueToKeywordEnum( style & NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK, nsCSSProps::kTextEmphasisStyleShapeKTable)); RefPtr valueList = GetROCSSValueList(false); valueList->AppendCSSValue(fillVal.forget()); valueList->AppendCSSValue(shapeVal.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextIndent() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleText()->mTextIndent, false, &nsComputedDOMStyle::GetCBContentWidth); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextOrientation() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mTextOrientation, nsCSSProps::kTextOrientationKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextOverflow() { const nsStyleTextReset *style = StyleTextReset(); RefPtr first = new nsROCSSPrimitiveValue; const nsStyleTextOverflowSide *side = style->mTextOverflow.GetFirstValue(); if (side->mType == NS_STYLE_TEXT_OVERFLOW_STRING) { nsString str; nsStyleUtil::AppendEscapedCSSString(side->mString, str); first->SetString(str); } else { first->SetIdent( nsCSSProps::ValueToKeywordEnum(side->mType, nsCSSProps::kTextOverflowKTable)); } side = style->mTextOverflow.GetSecondValue(); if (!side) { return first.forget(); } RefPtr second = new nsROCSSPrimitiveValue; if (side->mType == NS_STYLE_TEXT_OVERFLOW_STRING) { nsString str; nsStyleUtil::AppendEscapedCSSString(side->mString, str); second->SetString(str); } else { second->SetIdent( nsCSSProps::ValueToKeywordEnum(side->mType, nsCSSProps::kTextOverflowKTable)); } RefPtr valueList = GetROCSSValueList(false); valueList->AppendCSSValue(first.forget()); valueList->AppendCSSValue(second.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextShadow() { return GetCSSShadowArray(StyleText()->mTextShadow, StyleColor()->mColor, false); } already_AddRefed nsComputedDOMStyle::DoGetTextTransform() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleText()->mTextTransform, nsCSSProps::kTextTransformKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTabSize() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleText()->mTabSize); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetLetterSpacing() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleText()->mLetterSpacing, false); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWordSpacing() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleText()->mWordSpacing, false); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWhiteSpace() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleText()->mWhiteSpace, nsCSSProps::kWhitespaceKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWindowDragging() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUserInterface()->mWindowDragging, nsCSSProps::kWindowDraggingKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWindowShadow() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUIReset()->mWindowShadow, nsCSSProps::kWindowShadowKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWordBreak() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleText()->mWordBreak, nsCSSProps::kWordBreakKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWordWrap() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleText()->mWordWrap, nsCSSProps::kWordWrapKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetHyphens() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleText()->mHyphens, nsCSSProps::kHyphensKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextSizeAdjust() { RefPtr val = new nsROCSSPrimitiveValue; switch (StyleText()->mTextSizeAdjust) { default: NS_NOTREACHED("unexpected value"); MOZ_FALLTHROUGH; case NS_STYLE_TEXT_SIZE_ADJUST_AUTO: val->SetIdent(eCSSKeyword_auto); break; case NS_STYLE_TEXT_SIZE_ADJUST_NONE: val->SetIdent(eCSSKeyword_none); break; } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPointerEvents() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mPointerEvents, nsCSSProps::kPointerEventsKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetVisibility() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mVisible, nsCSSProps::kVisibilityKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWritingMode() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mWritingMode, nsCSSProps::kWritingModeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetDirection() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mDirection, nsCSSProps::kDirectionKTable)); return val.forget(); } static_assert(NS_STYLE_UNICODE_BIDI_NORMAL == 0, "unicode-bidi style constants not as expected"); already_AddRefed nsComputedDOMStyle::DoGetUnicodeBidi() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleTextReset()->mUnicodeBidi, nsCSSProps::kUnicodeBidiKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetCursor() { RefPtr valueList = GetROCSSValueList(true); const nsStyleUserInterface *ui = StyleUserInterface(); for (nsCursorImage *item = ui->mCursorArray, *item_end = ui->mCursorArray + ui->mCursorArrayLength; item < item_end; ++item) { RefPtr itemList = GetROCSSValueList(false); nsCOMPtr uri; item->GetImage()->GetURI(getter_AddRefs(uri)); RefPtr val = new nsROCSSPrimitiveValue; val->SetURI(uri); itemList->AppendCSSValue(val.forget()); if (item->mHaveHotspot) { RefPtr valX = new nsROCSSPrimitiveValue; RefPtr valY = new nsROCSSPrimitiveValue; valX->SetNumber(item->mHotspotX); valY->SetNumber(item->mHotspotY); itemList->AppendCSSValue(valX.forget()); itemList->AppendCSSValue(valY.forget()); } valueList->AppendCSSValue(itemList.forget()); } RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(ui->mCursor, nsCSSProps::kCursorKTable)); valueList->AppendCSSValue(val.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAppearance() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mAppearance, nsCSSProps::kAppearanceKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxAlign() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleXUL()->mBoxAlign, nsCSSProps::kBoxAlignKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxDirection() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleXUL()->mBoxDirection, nsCSSProps::kBoxDirectionKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxFlex() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleXUL()->mBoxFlex); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxOrdinalGroup() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleXUL()->mBoxOrdinal); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxOrient() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleXUL()->mBoxOrient, nsCSSProps::kBoxOrientKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxPack() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleXUL()->mBoxPack, nsCSSProps::kBoxPackKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBoxSizing() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(uint8_t(StylePosition()->mBoxSizing), nsCSSProps::kBoxSizingKTable)); return val.forget(); } /* Border image properties */ already_AddRefed nsComputedDOMStyle::DoGetBorderImageSource() { const nsStyleBorder* border = StyleBorder(); RefPtr val = new nsROCSSPrimitiveValue; const nsStyleImage& image = border->mBorderImageSource; SetValueToStyleImage(image, val); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBorderImageSlice() { RefPtr valueList = GetROCSSValueList(false); const nsStyleBorder* border = StyleBorder(); // Four slice numbers. NS_FOR_CSS_SIDES (side) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, border->mBorderImageSlice.Get(side), true, nullptr); valueList->AppendCSSValue(val.forget()); } // Fill keyword. if (NS_STYLE_BORDER_IMAGE_SLICE_FILL == border->mBorderImageFill) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_fill); valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBorderImageWidth() { const nsStyleBorder* border = StyleBorder(); RefPtr valueList = GetROCSSValueList(false); NS_FOR_CSS_SIDES (side) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, border->mBorderImageWidth.Get(side), true, nullptr); valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBorderImageOutset() { RefPtr valueList = GetROCSSValueList(false); const nsStyleBorder* border = StyleBorder(); // four slice numbers NS_FOR_CSS_SIDES (side) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, border->mBorderImageOutset.Get(side), true, nullptr); valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetBorderImageRepeat() { RefPtr valueList = GetROCSSValueList(false); const nsStyleBorder* border = StyleBorder(); // horizontal repeat RefPtr valX = new nsROCSSPrimitiveValue; valX->SetIdent( nsCSSProps::ValueToKeywordEnum(border->mBorderImageRepeatH, nsCSSProps::kBorderImageRepeatKTable)); valueList->AppendCSSValue(valX.forget()); // vertical repeat RefPtr valY = new nsROCSSPrimitiveValue; valY->SetIdent( nsCSSProps::ValueToKeywordEnum(border->mBorderImageRepeatV, nsCSSProps::kBorderImageRepeatKTable)); valueList->AppendCSSValue(valY.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFlexBasis() { RefPtr val = new nsROCSSPrimitiveValue; // XXXdholbert We could make this more automagic and resolve percentages // if we wanted, by passing in a PercentageBaseGetter instead of nullptr // below. Logic would go like this: // if (i'm a flex item) { // if (my flex container is horizontal) { // percentageBaseGetter = &nsComputedDOMStyle::GetCBContentWidth; // } else { // percentageBaseGetter = &nsComputedDOMStyle::GetCBContentHeight; // } // } SetValueToCoord(val, StylePosition()->mFlexBasis, true, nullptr, nsCSSProps::kWidthKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFlexDirection() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StylePosition()->mFlexDirection, nsCSSProps::kFlexDirectionKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFlexGrow() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StylePosition()->mFlexGrow); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFlexShrink() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StylePosition()->mFlexShrink); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFlexWrap() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StylePosition()->mFlexWrap, nsCSSProps::kFlexWrapKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOrder() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StylePosition()->mOrder); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAlignContent() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString str; auto align = StylePosition()->ComputedAlignContent(); nsCSSValue::AppendAlignJustifyValueToString(align & NS_STYLE_ALIGN_ALL_BITS, str); auto fallback = align >> NS_STYLE_ALIGN_ALL_SHIFT; if (fallback) { str.Append(' '); nsCSSValue::AppendAlignJustifyValueToString(fallback, str); } val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAlignItems() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString str; auto align = StylePosition()->ComputedAlignItems(); nsCSSValue::AppendAlignJustifyValueToString(align, str); val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAlignSelf() { RefPtr val = new nsROCSSPrimitiveValue; auto align = StylePosition()->ComputedAlignSelf(mStyleContext->GetParent()); nsAutoString str; nsCSSValue::AppendAlignJustifyValueToString(align, str); val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetJustifyContent() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString str; auto justify = StylePosition()->ComputedJustifyContent(); nsCSSValue::AppendAlignJustifyValueToString(justify & NS_STYLE_JUSTIFY_ALL_BITS, str); auto fallback = justify >> NS_STYLE_JUSTIFY_ALL_SHIFT; if (fallback) { MOZ_ASSERT(nsCSSProps::ValueToKeywordEnum(fallback & ~NS_STYLE_JUSTIFY_FLAG_BITS, nsCSSProps::kAlignSelfPosition) != eCSSKeyword_UNKNOWN, "unknown fallback value"); str.Append(' '); nsCSSValue::AppendAlignJustifyValueToString(fallback, str); } val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetJustifyItems() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString str; auto justify = StylePosition()->ComputedJustifyItems(mStyleContext->GetParent()); nsCSSValue::AppendAlignJustifyValueToString(justify, str); val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetJustifySelf() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString str; auto justify = StylePosition()->ComputedJustifySelf(mStyleContext->GetParent()); nsCSSValue::AppendAlignJustifyValueToString(justify, str); val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFloatEdge() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleBorder()->mFloatEdge, nsCSSProps::kFloatEdgeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetForceBrokenImageIcon() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleUIReset()->mForceBrokenImageIcon); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetImageOrientation() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString string; nsStyleImageOrientation orientation = StyleVisibility()->mImageOrientation; if (orientation.IsFromImage()) { string.AppendLiteral("from-image"); } else { nsStyleUtil::AppendAngleValue(orientation.AngleAsCoord(), string); if (orientation.IsFlipped()) { string.AppendLiteral(" flip"); } } val->SetString(string); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetIMEMode() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUIReset()->mIMEMode, nsCSSProps::kIMEModeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetUserFocus() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUserInterface()->mUserFocus, nsCSSProps::kUserFocusKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetUserInput() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUserInterface()->mUserInput, nsCSSProps::kUserInputKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetUserModify() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUserInterface()->mUserModify, nsCSSProps::kUserModifyKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetUserSelect() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUIReset()->mUserSelect, nsCSSProps::kUserSelectKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetDisplay() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mDisplay, nsCSSProps::kDisplayKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetContain() { RefPtr val = new nsROCSSPrimitiveValue; int32_t mask = StyleDisplay()->mContain; if (mask == 0) { val->SetIdent(eCSSKeyword_none); } else if (mask & NS_STYLE_CONTAIN_STRICT) { NS_ASSERTION(mask == (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS), "contain: strict should imply contain: layout style paint"); val->SetIdent(eCSSKeyword_strict); } else { nsAutoString valueStr; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_contain, mask, NS_STYLE_CONTAIN_LAYOUT, NS_STYLE_CONTAIN_PAINT, valueStr); val->SetString(valueStr); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPosition() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mPosition, nsCSSProps::kPositionKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetClip() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleDisplay* display = StyleDisplay(); if (display->mClipFlags == NS_STYLE_CLIP_AUTO) { val->SetIdent(eCSSKeyword_auto); } else { // create the cssvalues for the sides, stick them in the rect object nsROCSSPrimitiveValue *topVal = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *rightVal = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *bottomVal = new nsROCSSPrimitiveValue; nsROCSSPrimitiveValue *leftVal = new nsROCSSPrimitiveValue; nsDOMCSSRect * domRect = new nsDOMCSSRect(topVal, rightVal, bottomVal, leftVal); if (display->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) { topVal->SetIdent(eCSSKeyword_auto); } else { topVal->SetAppUnits(display->mClip.y); } if (display->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) { rightVal->SetIdent(eCSSKeyword_auto); } else { rightVal->SetAppUnits(display->mClip.width + display->mClip.x); } if (display->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) { bottomVal->SetIdent(eCSSKeyword_auto); } else { bottomVal->SetAppUnits(display->mClip.height + display->mClip.y); } if (display->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) { leftVal->SetIdent(eCSSKeyword_auto); } else { leftVal->SetAppUnits(display->mClip.x); } val->SetRect(domRect); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWillChange() { const nsTArray& willChange = StyleDisplay()->mWillChange; if (willChange.IsEmpty()) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_auto); return val.forget(); } RefPtr valueList = GetROCSSValueList(true); for (size_t i = 0; i < willChange.Length(); i++) { const nsString& willChangeIdentifier = willChange[i]; RefPtr property = new nsROCSSPrimitiveValue; property->SetString(willChangeIdentifier); valueList->AppendCSSValue(property.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOverflow() { const nsStyleDisplay* display = StyleDisplay(); if (display->mOverflowX != display->mOverflowY) { // No value to return. We can't express this combination of // values as a shorthand. return nullptr; } RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(display->mOverflowX, nsCSSProps::kOverflowKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOverflowX() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOverflowX, nsCSSProps::kOverflowSubKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOverflowY() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOverflowY, nsCSSProps::kOverflowSubKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOverflowClipBox() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOverflowClipBox, nsCSSProps::kOverflowClipBoxKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetResize() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mResize, nsCSSProps::kResizeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPageBreakAfter() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleDisplay *display = StyleDisplay(); if (display->mBreakAfter) { val->SetIdent(eCSSKeyword_always); } else { val->SetIdent(eCSSKeyword_auto); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPageBreakBefore() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleDisplay *display = StyleDisplay(); if (display->mBreakBefore) { val->SetIdent(eCSSKeyword_always); } else { val->SetIdent(eCSSKeyword_auto); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPageBreakInside() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mBreakInside, nsCSSProps::kPageBreakInsideKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTouchAction() { RefPtr val = new nsROCSSPrimitiveValue; int32_t intValue = StyleDisplay()->mTouchAction; // None and Auto and Manipulation values aren't allowed // to be in conjunction with other values. // But there are all checks in CSSParserImpl::ParseTouchAction nsAutoString valueStr; nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_touch_action, intValue, NS_STYLE_TOUCH_ACTION_NONE, NS_STYLE_TOUCH_ACTION_MANIPULATION, valueStr); val->SetString(valueStr); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetHeight() { RefPtr val = new nsROCSSPrimitiveValue; bool calcHeight = false; if (mInnerFrame) { calcHeight = true; const nsStyleDisplay* displayData = StyleDisplay(); if (displayData->mDisplay == NS_STYLE_DISPLAY_INLINE && !(mInnerFrame->IsFrameOfType(nsIFrame::eReplaced)) && // An outer SVG frame should behave the same as eReplaced in this case mInnerFrame->GetType() != nsGkAtoms::svgOuterSVGFrame) { calcHeight = false; } } if (calcHeight) { AssertFlushedPendingReflows(); nsMargin adjustedValues = GetAdjustedValuesForBoxSizing(); val->SetAppUnits(mInnerFrame->GetContentRect().height + adjustedValues.TopBottom()); } else { const nsStylePosition *positionData = StylePosition(); nscoord minHeight = StyleCoordToNSCoord(positionData->mMinHeight, &nsComputedDOMStyle::GetCBContentHeight, 0, true); nscoord maxHeight = StyleCoordToNSCoord(positionData->mMaxHeight, &nsComputedDOMStyle::GetCBContentHeight, nscoord_MAX, true); SetValueToCoord(val, positionData->mHeight, true, nullptr, nsCSSProps::kWidthKTable, minHeight, maxHeight); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWidth() { RefPtr val = new nsROCSSPrimitiveValue; bool calcWidth = false; if (mInnerFrame) { calcWidth = true; const nsStyleDisplay *displayData = StyleDisplay(); if (displayData->mDisplay == NS_STYLE_DISPLAY_INLINE && !(mInnerFrame->IsFrameOfType(nsIFrame::eReplaced)) && // An outer SVG frame should behave the same as eReplaced in this case mInnerFrame->GetType() != nsGkAtoms::svgOuterSVGFrame) { calcWidth = false; } } if (calcWidth) { AssertFlushedPendingReflows(); nsMargin adjustedValues = GetAdjustedValuesForBoxSizing(); val->SetAppUnits(mInnerFrame->GetContentRect().width + adjustedValues.LeftRight()); } else { const nsStylePosition *positionData = StylePosition(); nscoord minWidth = StyleCoordToNSCoord(positionData->mMinWidth, &nsComputedDOMStyle::GetCBContentWidth, 0, true); nscoord maxWidth = StyleCoordToNSCoord(positionData->mMaxWidth, &nsComputedDOMStyle::GetCBContentWidth, nscoord_MAX, true); SetValueToCoord(val, positionData->mWidth, true, nullptr, nsCSSProps::kWidthKTable, minWidth, maxWidth); } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMaxHeight() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StylePosition()->mMaxHeight, true, &nsComputedDOMStyle::GetCBContentHeight, nsCSSProps::kWidthKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMaxWidth() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StylePosition()->mMaxWidth, true, &nsComputedDOMStyle::GetCBContentWidth, nsCSSProps::kWidthKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMinHeight() { RefPtr val = new nsROCSSPrimitiveValue; nsStyleCoord minHeight = StylePosition()->mMinHeight; if (eStyleUnit_Auto == minHeight.GetUnit()) { // In non-flexbox contexts, "min-height: auto" means "min-height: 0" // XXXdholbert For flex items, we should set |minHeight| to the // -moz-min-content keyword, instead of 0, once we support -moz-min-content // as a height value. minHeight.SetCoordValue(0); } SetValueToCoord(val, minHeight, true, &nsComputedDOMStyle::GetCBContentHeight, nsCSSProps::kWidthKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMinWidth() { RefPtr val = new nsROCSSPrimitiveValue; nsStyleCoord minWidth = StylePosition()->mMinWidth; if (eStyleUnit_Auto == minWidth.GetUnit()) { // "min-width: auto" means "0", unless we're a flex item in a horizontal // flex container, in which case it means "min-content" minWidth.SetCoordValue(0); if (mOuterFrame && mOuterFrame->IsFlexItem()) { nsIFrame* flexContainer = mOuterFrame->GetParent(); MOZ_ASSERT(flexContainer && flexContainer->GetType() == nsGkAtoms::flexContainerFrame, "IsFlexItem() lied...?"); if (static_cast(flexContainer)->IsHorizontal()) { minWidth.SetIntValue(NS_STYLE_WIDTH_MIN_CONTENT, eStyleUnit_Enumerated); } } } SetValueToCoord(val, minWidth, true, &nsComputedDOMStyle::GetCBContentWidth, nsCSSProps::kWidthKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMixBlendMode() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mMixBlendMode, nsCSSProps::kBlendModeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetIsolation() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mIsolation, nsCSSProps::kIsolationKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetObjectFit() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StylePosition()->mObjectFit, nsCSSProps::kObjectFitKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetObjectPosition() { RefPtr valueList = GetROCSSValueList(false); SetValueToPosition(StylePosition()->mObjectPosition, valueList); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetLeft() { return GetOffsetWidthFor(NS_SIDE_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetRight() { return GetOffsetWidthFor(NS_SIDE_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetTop() { return GetOffsetWidthFor(NS_SIDE_TOP); } nsDOMCSSValueList* nsComputedDOMStyle::GetROCSSValueList(bool aCommaDelimited) { return new nsDOMCSSValueList(aCommaDelimited, true); } already_AddRefed nsComputedDOMStyle::GetOffsetWidthFor(mozilla::css::Side aSide) { const nsStyleDisplay* display = StyleDisplay(); AssertFlushedPendingReflows(); uint8_t position = display->mPosition; if (!mOuterFrame) { // GetRelativeOffset and GetAbsoluteOffset don't handle elements // without frames in any sensible way. GetStaticOffset, however, // is perfect for that case. position = NS_STYLE_POSITION_STATIC; } switch (position) { case NS_STYLE_POSITION_STATIC: return GetStaticOffset(aSide); case NS_STYLE_POSITION_RELATIVE: return GetRelativeOffset(aSide); case NS_STYLE_POSITION_STICKY: return GetStickyOffset(aSide); case NS_STYLE_POSITION_ABSOLUTE: case NS_STYLE_POSITION_FIXED: return GetAbsoluteOffset(aSide); default: NS_ERROR("Invalid position"); return nullptr; } } already_AddRefed nsComputedDOMStyle::GetAbsoluteOffset(mozilla::css::Side aSide) { MOZ_ASSERT(mOuterFrame, "need a frame, so we can call GetContainingBlock()"); nsIFrame* container = mOuterFrame->GetContainingBlock(); nsMargin margin = mOuterFrame->GetUsedMargin(); nsMargin border = container->GetUsedBorder(); nsMargin scrollbarSizes(0, 0, 0, 0); nsRect rect = mOuterFrame->GetRect(); nsRect containerRect = container->GetRect(); if (container->GetType() == nsGkAtoms::viewportFrame) { // For absolutely positioned frames scrollbars are taken into // account by virtue of getting a containing block that does // _not_ include the scrollbars. For fixed positioned frames, // the containing block is the viewport, which _does_ include // scrollbars. We have to do some extra work. // the first child in the default frame list is what we want nsIFrame* scrollingChild = container->GetFirstPrincipalChild(); nsIScrollableFrame *scrollFrame = do_QueryFrame(scrollingChild); if (scrollFrame) { scrollbarSizes = scrollFrame->GetActualScrollbarSizes(); } } nscoord offset = 0; switch (aSide) { case NS_SIDE_TOP: offset = rect.y - margin.top - border.top - scrollbarSizes.top; break; case NS_SIDE_RIGHT: offset = containerRect.width - rect.width - rect.x - margin.right - border.right - scrollbarSizes.right; break; case NS_SIDE_BOTTOM: offset = containerRect.height - rect.height - rect.y - margin.bottom - border.bottom - scrollbarSizes.bottom; break; case NS_SIDE_LEFT: offset = rect.x - margin.left - border.left - scrollbarSizes.left; break; default: NS_ERROR("Invalid side"); break; } RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(offset); return val.forget(); } static_assert(NS_SIDE_TOP == 0 && NS_SIDE_RIGHT == 1 && NS_SIDE_BOTTOM == 2 && NS_SIDE_LEFT == 3, "box side constants not as expected for NS_OPPOSITE_SIDE"); #define NS_OPPOSITE_SIDE(s_) mozilla::css::Side(((s_) + 2) & 3) already_AddRefed nsComputedDOMStyle::GetRelativeOffset(mozilla::css::Side aSide) { RefPtr val = new nsROCSSPrimitiveValue; const nsStylePosition* positionData = StylePosition(); int32_t sign = 1; nsStyleCoord coord = positionData->mOffset.Get(aSide); NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord || coord.GetUnit() == eStyleUnit_Percent || coord.GetUnit() == eStyleUnit_Auto || coord.IsCalcUnit(), "Unexpected unit"); if (coord.GetUnit() == eStyleUnit_Auto) { coord = positionData->mOffset.Get(NS_OPPOSITE_SIDE(aSide)); sign = -1; } PercentageBaseGetter baseGetter; if (aSide == NS_SIDE_LEFT || aSide == NS_SIDE_RIGHT) { baseGetter = &nsComputedDOMStyle::GetCBContentWidth; } else { baseGetter = &nsComputedDOMStyle::GetCBContentHeight; } val->SetAppUnits(sign * StyleCoordToNSCoord(coord, baseGetter, 0, false)); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetStickyOffset(mozilla::css::Side aSide) { RefPtr val = new nsROCSSPrimitiveValue; const nsStylePosition* positionData = StylePosition(); nsStyleCoord coord = positionData->mOffset.Get(aSide); NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord || coord.GetUnit() == eStyleUnit_Percent || coord.GetUnit() == eStyleUnit_Auto || coord.IsCalcUnit(), "Unexpected unit"); if (coord.GetUnit() == eStyleUnit_Auto) { val->SetIdent(eCSSKeyword_auto); return val.forget(); } PercentageBaseGetter baseGetter; if (aSide == NS_SIDE_LEFT || aSide == NS_SIDE_RIGHT) { baseGetter = &nsComputedDOMStyle::GetScrollFrameContentWidth; } else { baseGetter = &nsComputedDOMStyle::GetScrollFrameContentHeight; } val->SetAppUnits(StyleCoordToNSCoord(coord, baseGetter, 0, false)); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetStaticOffset(mozilla::css::Side aSide) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StylePosition()->mOffset.Get(aSide), false); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetPaddingWidthFor(mozilla::css::Side aSide) { RefPtr val = new nsROCSSPrimitiveValue; if (!mInnerFrame) { SetValueToCoord(val, StylePadding()->mPadding.Get(aSide), true); } else { AssertFlushedPendingReflows(); val->SetAppUnits(mInnerFrame->GetUsedPadding().Side(aSide)); } return val.forget(); } bool nsComputedDOMStyle::GetLineHeightCoord(nscoord& aCoord) { AssertFlushedPendingReflows(); nscoord blockHeight = NS_AUTOHEIGHT; if (StyleText()->mLineHeight.GetUnit() == eStyleUnit_Enumerated) { if (!mInnerFrame) return false; if (nsLayoutUtils::IsNonWrapperBlock(mInnerFrame)) { blockHeight = mInnerFrame->GetContentRect().height; } else { GetCBContentHeight(blockHeight); } } // lie about font size inflation since we lie about font size (since // the inflation only applies to text) aCoord = nsHTMLReflowState::CalcLineHeight(mContent, mStyleContext, blockHeight, 1.0f); // CalcLineHeight uses font->mFont.size, but we want to use // font->mSize as the font size. Adjust for that. Also adjust for // the text zoom, if any. const nsStyleFont* font = StyleFont(); float fCoord = float(aCoord); if (font->mAllowZoom) { fCoord /= mPresShell->GetPresContext()->TextZoom(); } if (font->mFont.size != font->mSize) { fCoord = fCoord * (float(font->mSize) / float(font->mFont.size)); } aCoord = NSToCoordRound(fCoord); return true; } already_AddRefed nsComputedDOMStyle::GetBorderColorsFor(mozilla::css::Side aSide) { const nsStyleBorder *border = StyleBorder(); if (border->mBorderColors) { nsBorderColors* borderColors = border->mBorderColors[aSide]; if (borderColors) { RefPtr valueList = GetROCSSValueList(false); do { RefPtr primitive = new nsROCSSPrimitiveValue; SetToRGBAColor(primitive, borderColors->mColor); valueList->AppendCSSValue(primitive.forget()); borderColors = borderColors->mNext; } while (borderColors); return valueList.forget(); } } RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetBorderWidthFor(mozilla::css::Side aSide) { RefPtr val = new nsROCSSPrimitiveValue; nscoord width; if (mInnerFrame) { AssertFlushedPendingReflows(); width = mInnerFrame->GetUsedBorder().Side(aSide); } else { width = StyleBorder()->GetComputedBorderWidth(aSide); } val->SetAppUnits(width); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetBorderColorFor(mozilla::css::Side aSide) { RefPtr val = new nsROCSSPrimitiveValue; nscolor color; bool foreground; StyleBorder()->GetBorderColor(aSide, color, foreground); if (foreground) { color = StyleColor()->mColor; } SetToRGBAColor(val, color); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetMarginWidthFor(mozilla::css::Side aSide) { RefPtr val = new nsROCSSPrimitiveValue; if (!mInnerFrame) { SetValueToCoord(val, StyleMargin()->mMargin.Get(aSide), false); } else { AssertFlushedPendingReflows(); // For tables, GetUsedMargin always returns an empty margin, so we // should read the margin from the outer table frame instead. val->SetAppUnits(mOuterFrame->GetUsedMargin().Side(aSide)); NS_ASSERTION(mOuterFrame == mInnerFrame || mInnerFrame->GetUsedMargin() == nsMargin(0, 0, 0, 0), "Inner tables must have zero margins"); } return val.forget(); } already_AddRefed nsComputedDOMStyle::GetBorderStyleFor(mozilla::css::Side aSide) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleBorder()->GetBorderStyle(aSide), nsCSSProps::kBorderStyleKTable)); return val.forget(); } void nsComputedDOMStyle::SetValueToCoord(nsROCSSPrimitiveValue* aValue, const nsStyleCoord& aCoord, bool aClampNegativeCalc, PercentageBaseGetter aPercentageBaseGetter, const KTableEntry aTable[], nscoord aMinAppUnits, nscoord aMaxAppUnits) { NS_PRECONDITION(aValue, "Must have a value to work with"); switch (aCoord.GetUnit()) { case eStyleUnit_Normal: aValue->SetIdent(eCSSKeyword_normal); break; case eStyleUnit_Auto: aValue->SetIdent(eCSSKeyword_auto); break; case eStyleUnit_Percent: { nscoord percentageBase; if (aPercentageBaseGetter && (this->*aPercentageBaseGetter)(percentageBase)) { nscoord val = NSCoordSaturatingMultiply(percentageBase, aCoord.GetPercentValue()); aValue->SetAppUnits(std::max(aMinAppUnits, std::min(val, aMaxAppUnits))); } else { aValue->SetPercent(aCoord.GetPercentValue()); } } break; case eStyleUnit_Factor: aValue->SetNumber(aCoord.GetFactorValue()); break; case eStyleUnit_Coord: { nscoord val = aCoord.GetCoordValue(); aValue->SetAppUnits(std::max(aMinAppUnits, std::min(val, aMaxAppUnits))); } break; case eStyleUnit_Integer: aValue->SetNumber(aCoord.GetIntValue()); break; case eStyleUnit_Enumerated: NS_ASSERTION(aTable, "Must have table to handle this case"); aValue->SetIdent(nsCSSProps::ValueToKeywordEnum(aCoord.GetIntValue(), aTable)); break; case eStyleUnit_None: aValue->SetIdent(eCSSKeyword_none); break; case eStyleUnit_Calc: nscoord percentageBase; if (!aCoord.CalcHasPercent()) { nscoord val = nsRuleNode::ComputeCoordPercentCalc(aCoord, 0); if (aClampNegativeCalc && val < 0) { MOZ_ASSERT(aCoord.IsCalcUnit(), "parser should have rejected value"); val = 0; } aValue->SetAppUnits(std::max(aMinAppUnits, std::min(val, aMaxAppUnits))); } else if (aPercentageBaseGetter && (this->*aPercentageBaseGetter)(percentageBase)) { nscoord val = nsRuleNode::ComputeCoordPercentCalc(aCoord, percentageBase); if (aClampNegativeCalc && val < 0) { MOZ_ASSERT(aCoord.IsCalcUnit(), "parser should have rejected value"); val = 0; } aValue->SetAppUnits(std::max(aMinAppUnits, std::min(val, aMaxAppUnits))); } else { nsStyleCoord::Calc *calc = aCoord.GetCalcValue(); SetValueToCalc(calc, aValue); } break; case eStyleUnit_Degree: aValue->SetDegree(aCoord.GetAngleValue()); break; case eStyleUnit_Grad: aValue->SetGrad(aCoord.GetAngleValue()); break; case eStyleUnit_Radian: aValue->SetRadian(aCoord.GetAngleValue()); break; case eStyleUnit_Turn: aValue->SetTurn(aCoord.GetAngleValue()); break; case eStyleUnit_FlexFraction: { nsAutoString tmpStr; nsStyleUtil::AppendCSSNumber(aCoord.GetFlexFractionValue(), tmpStr); tmpStr.AppendLiteral("fr"); aValue->SetString(tmpStr); break; } default: NS_ERROR("Can't handle this unit"); break; } } nscoord nsComputedDOMStyle::StyleCoordToNSCoord(const nsStyleCoord& aCoord, PercentageBaseGetter aPercentageBaseGetter, nscoord aDefaultValue, bool aClampNegativeCalc) { NS_PRECONDITION(aPercentageBaseGetter, "Must have a percentage base getter"); if (aCoord.GetUnit() == eStyleUnit_Coord) { return aCoord.GetCoordValue(); } if (aCoord.GetUnit() == eStyleUnit_Percent || aCoord.IsCalcUnit()) { nscoord percentageBase; if ((this->*aPercentageBaseGetter)(percentageBase)) { nscoord result = nsRuleNode::ComputeCoordPercentCalc(aCoord, percentageBase); if (aClampNegativeCalc && result < 0) { // It's expected that we can get a negative value here with calc(). // We can also get a negative value with a percentage value if // percentageBase is negative; this isn't expected, but can happen // when large length values overflow. NS_WARN_IF_FALSE(percentageBase >= 0, "percentage base value overflowed to become " "negative for a property that disallows negative " "values"); MOZ_ASSERT(aCoord.IsCalcUnit() || (aCoord.HasPercent() && percentageBase < 0), "parser should have rejected value"); result = 0; } return result; } // Fall through to returning aDefaultValue if we have no percentage base. } return aDefaultValue; } bool nsComputedDOMStyle::GetCBContentWidth(nscoord& aWidth) { if (!mOuterFrame) { return false; } AssertFlushedPendingReflows(); nsIFrame* container = mOuterFrame->GetContainingBlock(); aWidth = container->GetContentRect().width; return true; } bool nsComputedDOMStyle::GetCBContentHeight(nscoord& aHeight) { if (!mOuterFrame) { return false; } AssertFlushedPendingReflows(); nsIFrame* container = mOuterFrame->GetContainingBlock(); aHeight = container->GetContentRect().height; return true; } bool nsComputedDOMStyle::GetScrollFrameContentWidth(nscoord& aWidth) { if (!mOuterFrame) { return false; } AssertFlushedPendingReflows(); nsIScrollableFrame* scrollableFrame = nsLayoutUtils::GetNearestScrollableFrame(mOuterFrame->GetParent(), nsLayoutUtils::SCROLLABLE_SAME_DOC | nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); if (!scrollableFrame) { return false; } aWidth = scrollableFrame->GetScrolledFrame()->GetContentRectRelativeToSelf().width; return true; } bool nsComputedDOMStyle::GetScrollFrameContentHeight(nscoord& aHeight) { if (!mOuterFrame) { return false; } AssertFlushedPendingReflows(); nsIScrollableFrame* scrollableFrame = nsLayoutUtils::GetNearestScrollableFrame(mOuterFrame->GetParent(), nsLayoutUtils::SCROLLABLE_SAME_DOC | nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN); if (!scrollableFrame) { return false; } aHeight = scrollableFrame->GetScrolledFrame()->GetContentRectRelativeToSelf().height; return true; } bool nsComputedDOMStyle::GetFrameBorderRectWidth(nscoord& aWidth) { if (!mInnerFrame) { return false; } AssertFlushedPendingReflows(); aWidth = mInnerFrame->GetSize().width; return true; } bool nsComputedDOMStyle::GetFrameBorderRectHeight(nscoord& aHeight) { if (!mInnerFrame) { return false; } AssertFlushedPendingReflows(); aHeight = mInnerFrame->GetSize().height; return true; } bool nsComputedDOMStyle::GetFrameBoundsWidthForTransform(nscoord& aWidth) { // We need a frame to work with. if (!mInnerFrame) { return false; } AssertFlushedPendingReflows(); aWidth = nsStyleTransformMatrix::TransformReferenceBox(mInnerFrame).Width(); return true; } bool nsComputedDOMStyle::GetFrameBoundsHeightForTransform(nscoord& aHeight) { // We need a frame to work with. if (!mInnerFrame) { return false; } AssertFlushedPendingReflows(); aHeight = nsStyleTransformMatrix::TransformReferenceBox(mInnerFrame).Height(); return true; } already_AddRefed nsComputedDOMStyle::GetSVGPaintFor(bool aFill) { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleSVG* svg = StyleSVG(); const nsStyleSVGPaint* paint = nullptr; if (aFill) paint = &svg->mFill; else paint = &svg->mStroke; nsAutoString paintString; switch (paint->mType) { case eStyleSVGPaintType_None: { val->SetIdent(eCSSKeyword_none); break; } case eStyleSVGPaintType_Color: { SetToRGBAColor(val, paint->mPaint.mColor); break; } case eStyleSVGPaintType_Server: { RefPtr valueList = GetROCSSValueList(false); RefPtr fallback = new nsROCSSPrimitiveValue; val->SetURI(paint->mPaint.mPaintServer); SetToRGBAColor(fallback, paint->mFallbackColor); valueList->AppendCSSValue(val.forget()); valueList->AppendCSSValue(fallback.forget()); return valueList.forget(); } case eStyleSVGPaintType_ContextFill: { val->SetIdent(eCSSKeyword_context_fill); break; } case eStyleSVGPaintType_ContextStroke: { val->SetIdent(eCSSKeyword_context_stroke); break; } } return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFill() { return GetSVGPaintFor(true); } already_AddRefed nsComputedDOMStyle::DoGetStroke() { return GetSVGPaintFor(false); } already_AddRefed nsComputedDOMStyle::DoGetMarkerEnd() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleSVG* svg = StyleSVG(); if (svg->mMarkerEnd) val->SetURI(svg->mMarkerEnd); else val->SetIdent(eCSSKeyword_none); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMarkerMid() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleSVG* svg = StyleSVG(); if (svg->mMarkerMid) val->SetURI(svg->mMarkerMid); else val->SetIdent(eCSSKeyword_none); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMarkerStart() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleSVG* svg = StyleSVG(); if (svg->mMarkerStart) val->SetURI(svg->mMarkerStart); else val->SetIdent(eCSSKeyword_none); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStrokeDasharray() { const nsStyleSVG* svg = StyleSVG(); if (!svg->mStrokeDasharrayLength || !svg->mStrokeDasharray) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0; i < svg->mStrokeDasharrayLength; i++) { RefPtr dash = new nsROCSSPrimitiveValue; SetValueToCoord(dash, svg->mStrokeDasharray[i], true); valueList->AppendCSSValue(dash.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStrokeDashoffset() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleSVG()->mStrokeDashoffset, false); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStrokeWidth() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleSVG()->mStrokeWidth, true); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetVectorEffect() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleSVGReset()->mVectorEffect, nsCSSProps::kVectorEffectKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFillOpacity() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleSVG()->mFillOpacity); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFloodOpacity() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleSVGReset()->mFloodOpacity); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStopOpacity() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleSVGReset()->mStopOpacity); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStrokeMiterlimit() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleSVG()->mStrokeMiterlimit); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStrokeOpacity() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleSVG()->mStrokeOpacity); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetClipRule() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum( StyleSVG()->mClipRule, nsCSSProps::kFillRuleKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFillRule() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum( StyleSVG()->mFillRule, nsCSSProps::kFillRuleKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStrokeLinecap() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVG()->mStrokeLinecap, nsCSSProps::kStrokeLinecapKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStrokeLinejoin() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVG()->mStrokeLinejoin, nsCSSProps::kStrokeLinejoinKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextAnchor() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVG()->mTextAnchor, nsCSSProps::kTextAnchorKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColorInterpolation() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVG()->mColorInterpolation, nsCSSProps::kColorInterpolationKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColorInterpolationFilters() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVG()->mColorInterpolationFilters, nsCSSProps::kColorInterpolationKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetDominantBaseline() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVGReset()->mDominantBaseline, nsCSSProps::kDominantBaselineKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetImageRendering() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVG()->mImageRendering, nsCSSProps::kImageRenderingKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetShapeRendering() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVG()->mShapeRendering, nsCSSProps::kShapeRenderingKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextRendering() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVG()->mTextRendering, nsCSSProps::kTextRenderingKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFloodColor() { RefPtr val = new nsROCSSPrimitiveValue; SetToRGBAColor(val, StyleSVGReset()->mFloodColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetLightingColor() { RefPtr val = new nsROCSSPrimitiveValue; SetToRGBAColor(val, StyleSVGReset()->mLightingColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStopColor() { RefPtr val = new nsROCSSPrimitiveValue; SetToRGBAColor(val, StyleSVGReset()->mStopColor); return val.forget(); } inline void AppendBasicShapeTypeToString(nsStyleBasicShape::Type aType, nsAutoString& aString) { nsCSSKeyword functionName; switch (aType) { case nsStyleBasicShape::Type::ePolygon: functionName = eCSSKeyword_polygon; break; case nsStyleBasicShape::Type::eCircle: functionName = eCSSKeyword_circle; break; case nsStyleBasicShape::Type::eEllipse: functionName = eCSSKeyword_ellipse; break; case nsStyleBasicShape::Type::eInset: functionName = eCSSKeyword_inset; break; default: functionName = eCSSKeyword_UNKNOWN; NS_NOTREACHED("unexpected type"); } AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(functionName), aString); } void nsComputedDOMStyle::BoxValuesToString(nsAString& aString, const nsTArray& aBoxValues) { MOZ_ASSERT(aBoxValues.Length() == 4, "wrong number of box values"); nsAutoString value1, value2, value3, value4; SetCssTextToCoord(value1, aBoxValues[0]); SetCssTextToCoord(value2, aBoxValues[1]); SetCssTextToCoord(value3, aBoxValues[2]); SetCssTextToCoord(value4, aBoxValues[3]); // nsROCSSPrimitiveValue do not have binary comparison operators. // Compare string results instead. aString.Append(value1); if (value1 != value2 || value1 != value3 || value1 != value4) { aString.Append(' '); aString.Append(value2); if (value1 != value3 || value2 != value4) { aString.Append(' '); aString.Append(value3); if (value2 != value4) { aString.Append(' '); aString.Append(value4); } } } } void nsComputedDOMStyle::BasicShapeRadiiToString(nsAString& aCssText, const nsStyleCorners& aCorners) { nsTArray horizontal, vertical; nsAutoString horizontalString, verticalString; NS_FOR_CSS_FULL_CORNERS(corner) { horizontal.AppendElement( aCorners.Get(NS_FULL_TO_HALF_CORNER(corner, false))); vertical.AppendElement( aCorners.Get(NS_FULL_TO_HALF_CORNER(corner, true))); } BoxValuesToString(horizontalString, horizontal); BoxValuesToString(verticalString, vertical); aCssText.Append(horizontalString); if (horizontalString == verticalString) { return; } aCssText.AppendLiteral(" / "); aCssText.Append(verticalString); } already_AddRefed nsComputedDOMStyle::CreatePrimitiveValueForClipPath( const nsStyleBasicShape* aStyleBasicShape, uint8_t aSizingBox) { RefPtr valueList = GetROCSSValueList(false); if (aStyleBasicShape) { nsStyleBasicShape::Type type = aStyleBasicShape->GetShapeType(); // Shape function name and opening parenthesis. nsAutoString shapeFunctionString; AppendBasicShapeTypeToString(type, shapeFunctionString); shapeFunctionString.Append('('); switch (type) { case nsStyleBasicShape::Type::ePolygon: { bool hasEvenOdd = aStyleBasicShape->GetFillRule() == NS_STYLE_FILL_RULE_EVENODD; if (hasEvenOdd) { shapeFunctionString.AppendLiteral("evenodd"); } for (size_t i = 0; i < aStyleBasicShape->Coordinates().Length(); i += 2) { nsAutoString coordString; if (i > 0 || hasEvenOdd) { shapeFunctionString.AppendLiteral(", "); } SetCssTextToCoord(coordString, aStyleBasicShape->Coordinates()[i]); shapeFunctionString.Append(coordString); shapeFunctionString.Append(' '); SetCssTextToCoord(coordString, aStyleBasicShape->Coordinates()[i + 1]); shapeFunctionString.Append(coordString); } break; } case nsStyleBasicShape::Type::eCircle: case nsStyleBasicShape::Type::eEllipse: { const nsTArray& radii = aStyleBasicShape->Coordinates(); MOZ_ASSERT(radii.Length() == (nsStyleBasicShape::Type::eCircle ? 1 : 2), "wrong number of radii"); for (size_t i = 0; i < radii.Length(); ++i) { nsAutoString radius; RefPtr value = new nsROCSSPrimitiveValue; bool clampNegativeCalc = true; SetValueToCoord(value, radii[i], clampNegativeCalc, nullptr, nsCSSProps::kShapeRadiusKTable); value->GetCssText(radius); shapeFunctionString.Append(radius); shapeFunctionString.Append(' '); } shapeFunctionString.AppendLiteral("at "); RefPtr position = GetROCSSValueList(false); nsAutoString positionString; SetValueToPosition(aStyleBasicShape->GetPosition(), position); position->GetCssText(positionString); shapeFunctionString.Append(positionString); break; } case nsStyleBasicShape::Type::eInset: { BoxValuesToString(shapeFunctionString, aStyleBasicShape->Coordinates()); if (aStyleBasicShape->HasRadius()) { shapeFunctionString.AppendLiteral(" round "); nsAutoString radiiString; BasicShapeRadiiToString(radiiString, aStyleBasicShape->GetRadius()); shapeFunctionString.Append(radiiString); } break; } default: NS_NOTREACHED("unexpected type"); } shapeFunctionString.Append(')'); RefPtr functionValue = new nsROCSSPrimitiveValue; functionValue->SetString(shapeFunctionString); valueList->AppendCSSValue(functionValue.forget()); } if (aSizingBox == NS_STYLE_CLIP_SHAPE_SIZING_NOBOX) { return valueList.forget(); } nsAutoString boxString; AppendASCIItoUTF16( nsCSSProps::ValueToKeyword(aSizingBox, nsCSSProps::kClipShapeSizingKTable), boxString); RefPtr val = new nsROCSSPrimitiveValue; val->SetString(boxString); valueList->AppendCSSValue(val.forget()); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetClipPath() { const nsStyleSVGReset* svg = StyleSVGReset(); switch (svg->mClipPath.GetType()) { case NS_STYLE_CLIP_PATH_SHAPE: return CreatePrimitiveValueForClipPath(svg->mClipPath.GetBasicShape(), svg->mClipPath.GetSizingBox()); case NS_STYLE_CLIP_PATH_BOX: return CreatePrimitiveValueForClipPath(nullptr, svg->mClipPath.GetSizingBox()); case NS_STYLE_CLIP_PATH_URL: { RefPtr val = new nsROCSSPrimitiveValue; val->SetURI(svg->mClipPath.GetURL()); return val.forget(); } case NS_STYLE_CLIP_PATH_NONE: { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } default: NS_NOTREACHED("unexpected type"); } return nullptr; } void nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord) { RefPtr value = new nsROCSSPrimitiveValue; bool clampNegativeCalc = true; SetValueToCoord(value, aCoord, clampNegativeCalc); value->GetCssText(aCssText); } already_AddRefed nsComputedDOMStyle::CreatePrimitiveValueForStyleFilter( const nsStyleFilter& aStyleFilter) { RefPtr value = new nsROCSSPrimitiveValue; // Handle url(). if (aStyleFilter.GetType() == NS_STYLE_FILTER_URL) { value->SetURI(aStyleFilter.GetURL()); return value.forget(); } // Filter function name and opening parenthesis. nsAutoString filterFunctionString; AppendASCIItoUTF16( nsCSSProps::ValueToKeyword(aStyleFilter.GetType(), nsCSSProps::kFilterFunctionKTable), filterFunctionString); filterFunctionString.Append('('); nsAutoString argumentString; if (aStyleFilter.GetType() == NS_STYLE_FILTER_DROP_SHADOW) { // Handle drop-shadow() RefPtr shadowValue = GetCSSShadowArray(aStyleFilter.GetDropShadow(), StyleColor()->mColor, false); ErrorResult dummy; shadowValue->GetCssText(argumentString, dummy); } else { // Filter function argument. SetCssTextToCoord(argumentString, aStyleFilter.GetFilterParameter()); } filterFunctionString.Append(argumentString); // Filter function closing parenthesis. filterFunctionString.Append(')'); value->SetString(filterFunctionString); return value.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFilter() { const nsTArray& filters = StyleSVGReset()->mFilters; if (filters.IsEmpty()) { RefPtr value = new nsROCSSPrimitiveValue; value->SetIdent(eCSSKeyword_none); return value.forget(); } RefPtr valueList = GetROCSSValueList(false); for(uint32_t i = 0; i < filters.Length(); i++) { RefPtr value = CreatePrimitiveValueForStyleFilter(filters[i]); valueList->AppendCSSValue(value.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMask() { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleSVGReset* svg = StyleSVGReset(); if (svg->mMask) val->SetURI(svg->mMask); else val->SetIdent(eCSSKeyword_none); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMaskType() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleSVGReset()->mMaskType, nsCSSProps::kMaskTypeKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPaintOrder() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString string; uint8_t paintOrder = StyleSVG()->mPaintOrder; nsStyleUtil::AppendPaintOrderValue(paintOrder, string); val->SetString(string); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTransitionDelay() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mTransitionDelayCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleTransition *transition = &display->mTransitions[i]; RefPtr delay = new nsROCSSPrimitiveValue; delay->SetTime((float)transition->GetDelay() / (float)PR_MSEC_PER_SEC); valueList->AppendCSSValue(delay.forget()); } while (++i < display->mTransitionDelayCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTransitionDuration() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mTransitionDurationCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleTransition *transition = &display->mTransitions[i]; RefPtr duration = new nsROCSSPrimitiveValue; duration->SetTime((float)transition->GetDuration() / (float)PR_MSEC_PER_SEC); valueList->AppendCSSValue(duration.forget()); } while (++i < display->mTransitionDurationCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTransitionProperty() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mTransitionPropertyCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleTransition *transition = &display->mTransitions[i]; RefPtr property = new nsROCSSPrimitiveValue; nsCSSProperty cssprop = transition->GetProperty(); if (cssprop == eCSSPropertyExtra_all_properties) property->SetIdent(eCSSKeyword_all); else if (cssprop == eCSSPropertyExtra_no_properties) property->SetIdent(eCSSKeyword_none); else if (cssprop == eCSSProperty_UNKNOWN || cssprop == eCSSPropertyExtra_variable) { nsAutoString escaped; nsStyleUtil::AppendEscapedCSSIdent( nsDependentAtomString(transition->GetUnknownProperty()), escaped); property->SetString(escaped); // really want SetIdent } else property->SetString(nsCSSProps::GetStringValue(cssprop)); valueList->AppendCSSValue(property.forget()); } while (++i < display->mTransitionPropertyCount); return valueList.forget(); } void nsComputedDOMStyle::AppendTimingFunction(nsDOMCSSValueList *aValueList, const nsTimingFunction& aTimingFunction) { RefPtr timingFunction = new nsROCSSPrimitiveValue; nsAutoString tmp; switch (aTimingFunction.mType) { case nsTimingFunction::Type::CubicBezier: nsStyleUtil::AppendCubicBezierTimingFunction(aTimingFunction.mFunc.mX1, aTimingFunction.mFunc.mY1, aTimingFunction.mFunc.mX2, aTimingFunction.mFunc.mY2, tmp); break; case nsTimingFunction::Type::StepStart: case nsTimingFunction::Type::StepEnd: nsStyleUtil::AppendStepsTimingFunction(aTimingFunction.mType, aTimingFunction.mSteps, aTimingFunction.mStepSyntax, tmp); break; default: nsStyleUtil::AppendCubicBezierKeywordTimingFunction(aTimingFunction.mType, tmp); break; } timingFunction->SetString(tmp); aValueList->AppendCSSValue(timingFunction.forget()); } already_AddRefed nsComputedDOMStyle::DoGetTransitionTimingFunction() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mTransitionTimingFunctionCount > 0, "first item must be explicit"); uint32_t i = 0; do { AppendTimingFunction(valueList, display->mTransitions[i].GetTimingFunction()); } while (++i < display->mTransitionTimingFunctionCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAnimationName() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mAnimationNameCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleAnimation *animation = &display->mAnimations[i]; RefPtr property = new nsROCSSPrimitiveValue; const nsString& name = animation->GetName(); if (name.IsEmpty()) { property->SetIdent(eCSSKeyword_none); } else { nsAutoString escaped; nsStyleUtil::AppendEscapedCSSIdent(animation->GetName(), escaped); property->SetString(escaped); // really want SetIdent } valueList->AppendCSSValue(property.forget()); } while (++i < display->mAnimationNameCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAnimationDelay() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mAnimationDelayCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleAnimation *animation = &display->mAnimations[i]; RefPtr delay = new nsROCSSPrimitiveValue; delay->SetTime((float)animation->GetDelay() / (float)PR_MSEC_PER_SEC); valueList->AppendCSSValue(delay.forget()); } while (++i < display->mAnimationDelayCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAnimationDuration() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mAnimationDurationCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleAnimation *animation = &display->mAnimations[i]; RefPtr duration = new nsROCSSPrimitiveValue; duration->SetTime((float)animation->GetDuration() / (float)PR_MSEC_PER_SEC); valueList->AppendCSSValue(duration.forget()); } while (++i < display->mAnimationDurationCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAnimationTimingFunction() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mAnimationTimingFunctionCount > 0, "first item must be explicit"); uint32_t i = 0; do { AppendTimingFunction(valueList, display->mAnimations[i].GetTimingFunction()); } while (++i < display->mAnimationTimingFunctionCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAnimationDirection() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mAnimationDirectionCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleAnimation *animation = &display->mAnimations[i]; RefPtr direction = new nsROCSSPrimitiveValue; direction->SetIdent( nsCSSProps::ValueToKeywordEnum( static_cast(animation->GetDirection()), nsCSSProps::kAnimationDirectionKTable)); valueList->AppendCSSValue(direction.forget()); } while (++i < display->mAnimationDirectionCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAnimationFillMode() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mAnimationFillModeCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleAnimation *animation = &display->mAnimations[i]; RefPtr fillMode = new nsROCSSPrimitiveValue; fillMode->SetIdent( nsCSSProps::ValueToKeywordEnum( static_cast(animation->GetFillMode()), nsCSSProps::kAnimationFillModeKTable)); valueList->AppendCSSValue(fillMode.forget()); } while (++i < display->mAnimationFillModeCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAnimationIterationCount() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mAnimationIterationCountCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleAnimation *animation = &display->mAnimations[i]; RefPtr iterationCount = new nsROCSSPrimitiveValue; float f = animation->GetIterationCount(); /* Need a nasty hack here to work around an optimizer bug in gcc 4.2 on Mac, which somehow gets confused when directly comparing a float to the return value of NS_IEEEPositiveInfinity when building 32-bit builds. */ #ifdef XP_MACOSX volatile #endif float inf = NS_IEEEPositiveInfinity(); if (f == inf) { iterationCount->SetIdent(eCSSKeyword_infinite); } else { iterationCount->SetNumber(f); } valueList->AppendCSSValue(iterationCount.forget()); } while (++i < display->mAnimationIterationCountCount); return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAnimationPlayState() { const nsStyleDisplay* display = StyleDisplay(); RefPtr valueList = GetROCSSValueList(true); MOZ_ASSERT(display->mAnimationPlayStateCount > 0, "first item must be explicit"); uint32_t i = 0; do { const StyleAnimation *animation = &display->mAnimations[i]; RefPtr playState = new nsROCSSPrimitiveValue; playState->SetIdent( nsCSSProps::ValueToKeywordEnum(animation->GetPlayState(), nsCSSProps::kAnimationPlayStateKTable)); valueList->AppendCSSValue(playState.forget()); } while (++i < display->mAnimationPlayStateCount); return valueList.forget(); } static void MarkComputedStyleMapDirty(const char* aPref, void* aData) { static_cast(aData)->MarkDirty(); } already_AddRefed nsComputedDOMStyle::DoGetCustomProperty(const nsAString& aPropertyName) { MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName)); const nsStyleVariables* variables = StyleVariables(); nsString variableValue; const nsAString& name = Substring(aPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH); if (!variables->mVariables.Get(name, variableValue)) { return nullptr; } RefPtr val = new nsROCSSPrimitiveValue; val->SetString(variableValue); return val.forget(); } void nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent) { NS_ASSERTION(mContent == aContent, "didn't we register mContent?"); NS_ASSERTION(mResolvedStyleContext, "should have only registered an observer when " "mResolvedStyleContext is true"); ClearStyleContext(); } /* static */ nsComputedStyleMap* nsComputedDOMStyle::GetComputedStyleMap() { static nsComputedStyleMap map = { { #define COMPUTED_STYLE_PROP(prop_, method_) \ { eCSSProperty_##prop_, &nsComputedDOMStyle::DoGet##method_ }, #include "nsComputedDOMStylePropertyList.h" #undef COMPUTED_STYLE_PROP } }; return ↦ } /* static */ void nsComputedDOMStyle::RegisterPrefChangeCallbacks() { // Note that this will register callbacks for all properties with prefs, not // just those that are implemented on computed style objects, as it's not // easy to grab specific property data from nsCSSPropList.h based on the // entries iterated in nsComputedDOMStylePropertyList.h. nsComputedStyleMap* data = GetComputedStyleMap(); #define REGISTER_CALLBACK(pref_) \ if (pref_[0]) { \ Preferences::RegisterCallback(MarkComputedStyleMapDirty, pref_, data); \ } #define CSS_PROP(prop_, id_, method_, flags_, pref_, parsevariant_, \ kwtable_, stylestruct_, stylestructoffset_, animtype_) \ REGISTER_CALLBACK(pref_) #define CSS_PROP_LIST_INCLUDE_LOGICAL #include "nsCSSPropList.h" #undef CSS_PROP_LIST_INCLUDE_LOGICAL #undef CSS_PROP #undef REGISTER_CALLBACK } /* static */ void nsComputedDOMStyle::UnregisterPrefChangeCallbacks() { nsComputedStyleMap* data = GetComputedStyleMap(); #define UNREGISTER_CALLBACK(pref_) \ if (pref_[0]) { \ Preferences::UnregisterCallback(MarkComputedStyleMapDirty, pref_, data); \ } #define CSS_PROP(prop_, id_, method_, flags_, pref_, parsevariant_, \ kwtable_, stylestruct_, stylestructoffset_, animtype_) \ UNREGISTER_CALLBACK(pref_) #define CSS_PROP_LIST_INCLUDE_LOGICAL #include "nsCSSPropList.h" #undef CSS_PROP_LIST_INCLUDE_LOGICAL #undef CSS_PROP #undef UNREGISTER_CALLBACK }