/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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 "mozilla/ReflowInput.h" #include "nsStyleUtil.h" #include "nsStyleStructInlines.h" #include "nsROCSSPrimitiveValue.h" #include "nsPresContext.h" #include "nsIDocument.h" #include "nsCSSPseudoElements.h" #include "mozilla/StyleSetHandle.h" #include "mozilla/StyleSetHandleInlines.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; typedef nsCSSProps::KTableEntry KTableEntry; #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, nsIDocument* aDocument, nsComputedDOMStyle::StyleType aStyleType) { RefPtr computedStyle; computedStyle = new nsComputedDOMStyle(aElement, aPseudoElt, aDocument, aStyleType); return computedStyle.forget(); } static nsDOMCSSValueList* GetROCSSValueList(bool aCommaDelimited) { return new nsDOMCSSValueList(aCommaDelimited, true); } template already_AddRefed GetBackgroundList(T nsStyleImageLayers::Layer::* aMember, uint32_t nsStyleImageLayers::* aCount, const nsStyleImageLayers& aLayers, const KTableEntry aTable[]) { RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = aLayers.*aCount; i < i_end; ++i) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(aLayers.mLayers[i].*aMember, aTable)); valueList->AppendCSSValue(val.forget()); } return valueList.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)(); nsCSSPropertyID mProperty; ComputeMethod mGetter; bool IsLayoutFlushNeeded() const { return nsCSSProps::PropHasFlags(mProperty, CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH); } bool IsEnabled() const { return nsCSSProps::IsEnabled(mProperty, CSSEnabledState::eForAllContent); } }; // 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. */ nsCSSPropertyID 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(nsCSSPropertyID 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, nsIDocument* aDocument, StyleType aStyleType) : mDocumentWeak(nullptr) , mOuterFrame(nullptr) , mInnerFrame(nullptr) , mPresShell(nullptr) , mStyleType(aStyleType) , mStyleContextGeneration(0) , mExposeVisitedStyle(false) , mResolvedStyleContext(false) { MOZ_ASSERT(aElement); MOZ_ASSERT(aDocument); // TODO(emilio, bug 548397, https://github.com/w3c/csswg-drafts/issues/2403): // Should use aElement->OwnerDoc() instead. mDocumentWeak = do_GetWeakReference(aDocument); mElement = 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 = NS_Atomize(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; } } } nsComputedDOMStyle::~nsComputedDOMStyle() { ClearStyleContext(); } NS_IMPL_CYCLE_COLLECTION_CLASS(nsComputedDOMStyle) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle) tmp->ClearStyleContext(); // remove observer before clearing mElement NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement) 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(mElement) 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 nsCSSPropertyID 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 nsCSSPropertyID 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) { 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(); } else { length = 0; } *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); bool inDocWithShell = true; if (!presShell) { inDocWithShell = false; 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 && inDocWithShell && !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; StyleSetHandle styleSet = presShell->StyleSet(); RefPtr sc; if (aPseudo) { CSSPseudoElementType type = nsCSSPseudoElements:: GetPseudoType(aPseudo, CSSEnabledState::eIgnoreEnabledState); if (type >= CSSPseudoElementType::Count) { return nullptr; } nsIFrame* frame = nsLayoutUtils::GetStyleFrame(aElement); Element* pseudoElement = frame && inDocWithShell ? frame->GetPseudoElement(type) : nullptr; sc = styleSet->ResolvePseudoElementStyle(aElement, type, parentContext, pseudoElement); } else { sc = styleSet->ResolveStyleFor(aElement, parentContext); } if (aStyleType == eDefaultOnly) { if (styleSet->IsServo()) { NS_ERROR("stylo: ServoStyleSets cannot supply UA-only styles yet"); return nullptr; } // 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->AsGecko()->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; if (stylePos->mBoxSizing == StyleBoxSizing::Border) { adjustment = mInnerFrame->GetUsedBorderAndPadding(); } 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. DeclarationBlock* nsComputedDOMStyle::GetCSSDeclaration(Operation) { NS_RUNTIMEABORT("called nsComputedDOMStyle::GetCSSDeclaration"); return nullptr; } nsresult nsComputedDOMStyle::SetCSSDeclaration(DeclarationBlock*) { 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; mElement->RemoveMutationObserver(this); } mStyleContext = nullptr; } void nsComputedDOMStyle::SetResolvedStyleContext(RefPtr&& aContext) { if (!mResolvedStyleContext) { mResolvedStyleContext = true; mElement->AddMutationObserver(this); } mStyleContext = aContext; } void nsComputedDOMStyle::SetFrameStyleContext(nsStyleContext* aContext) { ClearStyleContext(); mStyleContext = aContext; } /** * The following function checks whether we need to explicitly resolve the style * again, even though we have a style context coming from the frame. * * This basically checks whether the style is or may be under a ::first-line or * ::first-letter frame, in which case we can't return the frame style, and we * need to resolve it. See bug 505515. */ static bool MustReresolveStyle(const nsStyleContext* aContext) { MOZ_ASSERT(aContext); if (aContext->HasPseudoElementData()) { if (!aContext->GetPseudo() || aContext->StyleSource().IsServoComputedValues()) { // TODO(emilio): When ::first-line is supported in Servo, we may want to // fix this to avoid re-resolving pseudo-element styles. return true; } return aContext->GetParent() && aContext->GetParent()->HasPseudoElementData(); } return false; } 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 mElement is in -- the two // may be different. document->FlushPendingNotifications( aNeedsLayoutFlush ? Flush_Layout : Flush_Style); #ifdef DEBUG mFlushedPendingReflows = aNeedsLayoutFlush; #endif nsCOMPtr presShellForContent = GetPresShellForContent(mElement); if (presShellForContent && presShellForContent != document->GetShell()) { presShellForContent->FlushPendingNotifications(Flush_Style); } mPresShell = document->GetShell(); if (!mPresShell || !mPresShell->GetPresContext()) { ClearStyleContext(); return; } uint64_t currentGeneration = mPresShell->GetPresContext()->GetRestyleGeneration(); if (mStyleContext) { // We can't rely on the undisplayed restyle generation if mElement is // out-of-document, since that generation is not incremented for DOM changes // on out-of-document elements. if (mStyleContextGeneration == currentGeneration && mElement->IsInComposedDoc()) { // 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 !mElement->IsHTMLElement(nsGkAtoms::area) // check is needed due to bug 135040 (to avoid using // mPrimaryFrame). Remove it once that's fixed. if (mStyleType == eAll && !mElement->IsHTMLElement(nsGkAtoms::area)) { mOuterFrame = nullptr; if (!mPseudo) { mOuterFrame = mElement->GetPrimaryFrame(); } else if (mPseudo == nsCSSPseudoElements::before || mPseudo == nsCSSPseudoElements::after) { nsIAtom* property = mPseudo == nsCSSPseudoElements::before ? nsGkAtoms::beforePseudoProperty : nsGkAtoms::afterPseudoProperty; auto* pseudo = static_cast(mElement->GetProperty(property)); mOuterFrame = pseudo ? pseudo->GetPrimaryFrame() : nullptr; } mInnerFrame = mOuterFrame; if (mOuterFrame) { nsIAtom* type = mOuterFrame->GetType(); if (type == nsGkAtoms::tableWrapperFrame) { // If the frame is a table wrapper frame then we should get the style // from the inner table frame. mInnerFrame = mOuterFrame->PrincipalChildList().FirstChild(); NS_ASSERTION(mInnerFrame, "table wrapper must have an inner"); NS_ASSERTION(!mInnerFrame->GetNextSibling(), "table wrapper frames should have just one child, " "the inner table"); } SetFrameStyleContext(mInnerFrame->StyleContext()); NS_ASSERTION(mStyleContext, "Frame without style context?"); } } if (!mStyleContext || MustReresolveStyle(mStyleContext)) { #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. (We also allow // the element to be NAC, just in case some chrome JS calls // getComputedStyle on a NAC-implemented pseudo.) nsStyleContext* topWithPseudoElementData = mStyleContext; while (topWithPseudoElementData->GetParent()->HasPseudoElementData()) { topWithPseudoElementData = topWithPseudoElementData->GetParent(); } CSSPseudoElementType 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) || mElement->IsNativeAnonymous(), NS_LossyConvertUTF16toASCII(assertMsg).get()); } #endif // Need to resolve a style context RefPtr resolvedStyleContext = nsComputedDOMStyle::GetStyleContextForElementNoFlush( mElement, mPseudo, presShellForContent ? presShellForContent.get() : 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) { nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName, CSSEnabledState::eForAllContent); 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 nsCSSPropertyID* 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) { 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()->mFloat, 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); } void nsComputedDOMStyle::SetValueFromComplexColor(nsROCSSPrimitiveValue* aValue, const StyleComplexColor& aColor) { SetToRGBAColor(aValue, StyleColor()->CalcComplexColor(aColor)); } already_AddRefed nsComputedDOMStyle::DoGetColor() { RefPtr val = new nsROCSSPrimitiveValue; SetToRGBAColor(val, StyleColor()->mColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetColorAdjust() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mColorAdjust, nsCSSProps::kColorAdjustKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOpacity() { RefPtr val = new nsROCSSPrimitiveValue; val->SetNumber(StyleEffects()->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; SetValueFromComplexColor(val, StyleColumn()->mColumnRuleColor); 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: { nsAutoString 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->CounterIncrementAt(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; bool dummyBool; gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform->mHead, mStyleContext, mStyleContext->PresContext(), dummy, refBox, float(mozilla::AppUnitsPerCSSPixel()), &dummyBool); 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->CounterResetAt(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 auto& quotePairs = StyleList()->GetQuotePairs(); if (quotePairs.IsEmpty()) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } RefPtr valueList = GetROCSSValueList(false); for (const auto& quotePair : quotePairs) { RefPtr openVal = new nsROCSSPrimitiveValue; RefPtr closeVal = new nsROCSSPrimitiveValue; nsAutoString s; nsStyleUtil::AppendEscapedCSSString(quotePair.first, s); openVal->SetString(s); s.Truncate(); nsStyleUtil::AppendEscapedCSSString(quotePair.second, 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::DoGetFontVariationSettings() { // TODO: This is still a stub, always returning "normal" RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_normal); 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 { nsAutoString 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::DoGetBackgroundAttachment() { return GetBackgroundList(&nsStyleImageLayers::Layer::mAttachment, &nsStyleImageLayers::mAttachmentCount, StyleBackground()->mImage, nsCSSProps::kImageLayerAttachmentKTable); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundClip() { return GetBackgroundList(&nsStyleImageLayers::Layer::mClip, &nsStyleImageLayers::mClipCount, StyleBackground()->mImage, nsCSSProps::kBackgroundClipKTable); } 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(); if (!req) { // XXXheycam If we had some problem resolving the imgRequestProxy, // maybe we should just use the URL stored in the nsStyleImage's // mImageValue? (Similarly in DoGetListStyleImage.) aValue->SetIdent(eCSSKeyword_none); break; } nsCOMPtr uri; req->GetURI(getter_AddRefs(uri)); const UniquePtr& 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::DoGetImageLayerImage(const nsStyleImageLayers& aLayers) { RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = aLayers.mImageCount; i < i_end; ++i) { RefPtr val = new nsROCSSPrimitiveValue; const nsStyleImage& image = aLayers.mLayers[i].mImage; // Layer::mImage::GetType() returns eStyleImageType_Null in two conditions: // 1. The value of mask-image/bg-image is 'none'. // Since this layer does not refer to any source, Layer::mSourceURI must // be nullptr too. // 2. This layer refers to a local resource, e.g. mask-image:url(#mymask). // For local references, there is no need to download any external // resource, so Layer::mImage is not used. // Instead, we store the local URI in one place -- on Layer::mSourceURI. // Hence, we must serialize using mSourceURI (instead of // SetValueToStyleImage()/mImage) in this case. if (aLayers.mLayers[i].mSourceURI && aLayers.mLayers[i].mSourceURI->IsLocalRef()) { // This is how we represent a 'mask-image' reference for a local URI, // such as 'mask-image:url(#mymask)' or 'mask:url(#mymask)' SetValueToURLValue(aLayers.mLayers[i].mSourceURI, val); } else { SetValueToStyleImage(image, val); } valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetImageLayerPosition(const nsStyleImageLayers& aLayers) { if (aLayers.mPositionXCount != aLayers.mPositionYCount) { // No value to return. We can't express this combination of // values as a shorthand. return nullptr; } RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) { RefPtr itemList = GetROCSSValueList(false); SetValueToPosition(aLayers.mLayers[i].mPosition, itemList); valueList->AppendCSSValue(itemList.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetImageLayerPositionX(const nsStyleImageLayers& aLayers) { RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToPositionCoord(aLayers.mLayers[i].mPosition.mXPosition, val); valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetImageLayerPositionY(const nsStyleImageLayers& aLayers) { RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = aLayers.mPositionYCount; i < i_end; ++i) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToPositionCoord(aLayers.mLayers[i].mPosition.mYPosition, val); valueList->AppendCSSValue(val.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetImageLayerRepeat(const nsStyleImageLayers& aLayers) { RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = aLayers.mRepeatCount; i < i_end; ++i) { RefPtr itemList = GetROCSSValueList(false); RefPtr valX = new nsROCSSPrimitiveValue; const uint8_t& xRepeat = aLayers.mLayers[i].mRepeat.mXRepeat; const uint8_t& yRepeat = aLayers.mLayers[i].mRepeat.mYRepeat; bool hasContraction = true; unsigned contraction; if (xRepeat == yRepeat) { contraction = xRepeat; } else if (xRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT && yRepeat == NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT) { contraction = NS_STYLE_IMAGELAYER_REPEAT_REPEAT_X; } else if (xRepeat == NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT && yRepeat == NS_STYLE_IMAGELAYER_REPEAT_REPEAT) { contraction = NS_STYLE_IMAGELAYER_REPEAT_REPEAT_Y; } else { hasContraction = false; } RefPtr valY; if (hasContraction) { valX->SetIdent(nsCSSProps::ValueToKeywordEnum(contraction, nsCSSProps::kImageLayerRepeatKTable)); } else { valY = new nsROCSSPrimitiveValue; valX->SetIdent(nsCSSProps::ValueToKeywordEnum(xRepeat, nsCSSProps::kImageLayerRepeatKTable)); valY->SetIdent(nsCSSProps::ValueToKeywordEnum(yRepeat, nsCSSProps::kImageLayerRepeatKTable)); } itemList->AppendCSSValue(valX.forget()); if (valY) { itemList->AppendCSSValue(valY.forget()); } valueList->AppendCSSValue(itemList.forget()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetImageLayerSize(const nsStyleImageLayers& aLayers) { RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0, i_end = aLayers.mSizeCount; i < i_end; ++i) { const nsStyleImageLayers::Size &size = aLayers.mLayers[i].mSize; switch (size.mWidthType) { case nsStyleImageLayers::Size::eContain: case nsStyleImageLayers::Size::eCover: { MOZ_ASSERT(size.mWidthType == size.mHeightType, "unsynced types"); nsCSSKeyword keyword = size.mWidthType == nsStyleImageLayers::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 == nsStyleImageLayers::Size::eAuto) { valX->SetIdent(eCSSKeyword_auto); } else { MOZ_ASSERT(size.mWidthType == nsStyleImageLayers::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 == nsStyleImageLayers::Size::eAuto) { valY->SetIdent(eCSSKeyword_auto); } else { MOZ_ASSERT(size.mHeightType == nsStyleImageLayers::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::DoGetBackgroundImage() { const nsStyleImageLayers& layers = StyleBackground()->mImage; return DoGetImageLayerImage(layers); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundBlendMode() { return GetBackgroundList(&nsStyleImageLayers::Layer::mBlendMode, &nsStyleImageLayers::mBlendModeCount, StyleBackground()->mImage, nsCSSProps::kBlendModeKTable); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundOrigin() { return GetBackgroundList(&nsStyleImageLayers::Layer::mOrigin, &nsStyleImageLayers::mOriginCount, StyleBackground()->mImage, nsCSSProps::kBackgroundOriginKTable); } void nsComputedDOMStyle::SetValueToPositionCoord( const Position::Coord& 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 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()); } void nsComputedDOMStyle::SetValueToURLValue(const css::URLValueData* aURL, nsROCSSPrimitiveValue* aValue) { if (aURL && aURL->IsLocalRef()) { nsString fragment; aURL->GetSourceString(fragment); fragment.Insert(u"url(\"", 0); fragment.Append(u"\")"); aValue->SetString(fragment); } else { nsCOMPtr url; if (aURL && (url = aURL->GetURI())) { aValue->SetURI(url); } else { aValue->SetIdent(eCSSKeyword_none); } } } already_AddRefed nsComputedDOMStyle::DoGetBackgroundPosition() { const nsStyleImageLayers& layers = StyleBackground()->mImage; return DoGetImageLayerPosition(layers); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundPositionX() { const nsStyleImageLayers& layers = StyleBackground()->mImage; return DoGetImageLayerPositionX(layers); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundPositionY() { const nsStyleImageLayers& layers = StyleBackground()->mImage; return DoGetImageLayerPositionY(layers); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundRepeat() { const nsStyleImageLayers& layers = StyleBackground()->mImage; return DoGetImageLayerRepeat(layers); } already_AddRefed nsComputedDOMStyle::DoGetBackgroundSize() { const nsStyleImageLayers& layers = StyleBackground()->mImage; return DoGetImageLayerSize(layers); } 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) { uint32_t numLines = aLineNames.Length(); if (numLines == 0) { return; } for (uint32_t i = 0;;) { nsStyleUtil::AppendEscapedCSSIdent(aLineNames[i], aResult); if (++i == numLines) { break; } aResult.Append(' '); } } void nsComputedDOMStyle::AppendGridLineNames(nsDOMCSSValueList* aValueList, const nsTArray& aLineNames, bool aSuppressEmptyList) { if (aLineNames.IsEmpty() && aSuppressEmptyList) { 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.GetUnit() == eStyleUnit_None) { // A fit-content() function. RefPtr val = new nsROCSSPrimitiveValue; nsAutoString argumentStr, fitContentStr; fitContentStr.AppendLiteral("fit-content("); MOZ_ASSERT(aMaxValue.IsCoordPercentCalcUnit(), "unexpected unit for fit-content() argument value"); SetValueToCoord(val, aMaxValue, true); val->GetCssText(argumentStr); fitContentStr.Append(argumentStr); fitContentStr.Append(char16_t(')')); val->SetString(fitContentStr); return val.forget(); } if (aMinValue == aMaxValue) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, aMinValue, true, nullptr, nsCSSProps::kGridTrackBreadthKTable); return val.forget(); } // minmax(auto, ) is equivalent to (and is our internal representation // of) , and both compute to if (aMinValue.GetUnit() == eStyleUnit_Auto && aMaxValue.GetUnit() == eStyleUnit_FlexFraction) { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, aMaxValue, true); 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 ComputedGridTrackInfo* aTrackInfo) { 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, len = aTrackList.mLineNameLists.Length(); ; ++i) { if (MOZ_UNLIKELY(aTrackList.IsRepeatAutoIndex(i))) { MOZ_ASSERT(aTrackList.mIsAutoFill, "subgrid can only have 'auto-fill'"); 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, /*aSuppressEmptyList*/ false); RefPtr end = new nsROCSSPrimitiveValue; end->SetString(NS_LITERAL_STRING(")")); valueList->AppendCSSValue(end.forget()); } if (i == len) { break; } AppendGridLineNames(valueList, aTrackList.mLineNameLists[i], /*aSuppressEmptyList*/ false); } 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 (aTrackInfo) { DebugOnly isAutoFill = aTrackList.HasRepeatAuto() && aTrackList.mIsAutoFill; DebugOnly isAutoFit = aTrackList.HasRepeatAuto() && !aTrackList.mIsAutoFill; DebugOnly numExplicitTracks = aTrackInfo->mNumExplicitTracks; MOZ_ASSERT(numExplicitTracks == numSizes || (isAutoFill && numExplicitTracks >= numSizes) || (isAutoFit && numExplicitTracks + 1 >= numSizes), "expected all explicit tracks (or possibly one less, if there's " "an 'auto-fit' track, since that can collapse away)"); numSizes = aTrackInfo->mSizes.Length(); } // An empty without repeats is represented as "none" in syntax. if (numSizes == 0 && !aTrackList.HasRepeatAuto()) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } RefPtr valueList = GetROCSSValueList(false); if (aTrackInfo) { // 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. const nsTArray& trackSizes = aTrackInfo->mSizes; const uint32_t numExplicitTracks = aTrackInfo->mNumExplicitTracks; const uint32_t numLeadingImplicitTracks = aTrackInfo->mNumLeadingImplicitTracks; MOZ_ASSERT(numSizes >= numLeadingImplicitTracks + numExplicitTracks); // Add any leading implicit tracks. for (uint32_t i = 0; i < numLeadingImplicitTracks; ++i) { RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(trackSizes[i]); valueList->AppendCSSValue(val.forget()); } // Then add any explicit tracks and removed auto-fit tracks. if (numExplicitTracks || aTrackList.HasRepeatAuto()) { 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 = numExplicitTracks + 1 - aTrackList.mLineNameLists.Length(); endOfRepeat = aTrackList.mRepeatAutoIndex + offsetToLastRepeat + 1; } uint32_t repeatIndex = 0; uint32_t numRepeatTracks = aTrackInfo->mRemovedRepeatTracks.Length(); enum LinePlacement { LinesPrecede, LinesFollow, LinesBetween }; auto AppendRemovedAutoFits = [this, aTrackInfo, &valueList, aTrackList, &repeatIndex, numRepeatTracks](LinePlacement aPlacement) { // Add in removed auto-fit tracks and lines here, if necessary bool atLeastOneTrackReported = false; while (repeatIndex < numRepeatTracks && aTrackInfo->mRemovedRepeatTracks[repeatIndex]) { if ((aPlacement == LinesPrecede) || ((aPlacement == LinesBetween) && atLeastOneTrackReported)) { // Precede it with the lines between repeats. AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListAfter, aTrackList.mRepeatAutoLineNameListBefore); } // Removed 'auto-fit' tracks are reported as 0px. RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(0); valueList->AppendCSSValue(val.forget()); atLeastOneTrackReported = true; if (aPlacement == LinesFollow) { // Follow it with the lines between repeats. AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListAfter, aTrackList.mRepeatAutoLineNameListBefore); } repeatIndex++; } repeatIndex++; }; 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, but we report them anyway. AppendGridLineNames(valueList, lineNames, aTrackList.mRepeatAutoLineNameListBefore); AppendRemovedAutoFits(LinesBetween); AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListAfter, aTrackList.mLineNameLists[i + 1]); } else { AppendGridLineNames(valueList, lineNames, aTrackList.mRepeatAutoLineNameListBefore); AppendRemovedAutoFits(LinesFollow); } } else if (i == endOfRepeat) { // Before appending the last line, finish off any removed auto-fits. AppendRemovedAutoFits(LinesPrecede); 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); AppendRemovedAutoFits(LinesFollow); } 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) == numExplicitTracks) { break; } RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(trackSizes[i + numLeadingImplicitTracks]); valueList->AppendCSSValue(val.forget()); } } // Add any trailing implicit tracks. for (uint32_t i = numLeadingImplicitTracks + numExplicitTracks; i < numSizes; ++i) { RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(trackSizes[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 ComputedGridTrackInfo* info = nullptr; nsGridContainerFrame* gridFrame = nsGridContainerFrame::GetGridFrameWithComputedInfo( mElement->GetPrimaryFrame()); if (gridFrame) { info = gridFrame->GetComputedTemplateColumns(); } return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateColumns, info); } already_AddRefed nsComputedDOMStyle::DoGetGridTemplateRows() { const ComputedGridTrackInfo* info = nullptr; nsGridContainerFrame* gridFrame = nsGridContainerFrame::GetGridFrameWithComputedInfo( mElement->GetPrimaryFrame()); if (gridFrame) { info = gridFrame->GetComputedTemplateRows(); } return GetGridTemplateColumnsRows(StylePosition()->mGridTemplateRows, info); } 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; SetValueToCoord(val, StylePosition()->mGridColumnGap, true); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetGridRowGap() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StylePosition()->mGridRowGap, true); 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); } already_AddRefed nsComputedDOMStyle::DoGetBorderBottomRightRadius() { return GetEllipseRadii(StyleBorder()->mBorderRadius, NS_CORNER_BOTTOM_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetBorderTopLeftRadius() { return GetEllipseRadii(StyleBorder()->mBorderRadius, NS_CORNER_TOP_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetBorderTopRightRadius() { return GetEllipseRadii(StyleBorder()->mBorderRadius, NS_CORNER_TOP_RIGHT); } 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::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::DoGetScrollbarWidth() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUserInterface()->mScrollbarWidth, nsCSSProps::kScrollbarWidthKTable)); 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->mOutlineStyle == NS_STYLE_BORDER_STYLE_NONE) { NS_ASSERTION(outline->GetOutlineWidth() == 0, "unexpected width"); width = 0; } else { width = outline->GetOutlineWidth(); } val->SetAppUnits(width); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetOutlineStyle() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleOutline()->mOutlineStyle, 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); } already_AddRefed nsComputedDOMStyle::DoGetOutlineRadiusBottomRight() { return GetEllipseRadii(StyleOutline()->mOutlineRadius, NS_CORNER_BOTTOM_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetOutlineRadiusTopLeft() { return GetEllipseRadii(StyleOutline()->mOutlineRadius, NS_CORNER_TOP_LEFT); } already_AddRefed nsComputedDOMStyle::DoGetOutlineRadiusTopRight() { return GetEllipseRadii(StyleOutline()->mOutlineRadius, NS_CORNER_TOP_RIGHT); } already_AddRefed nsComputedDOMStyle::DoGetOutlineColor() { RefPtr val = new nsROCSSPrimitiveValue; SetValueFromComplexColor(val, StyleOutline()->mOutlineColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::GetEllipseRadii(const nsStyleCorners& aRadius, uint8_t aFullCorner) { nsStyleCoord radiusX = aRadius.Get(NS_FULL_TO_HALF_CORNER(aFullCorner, false)); nsStyleCoord radiusY = aRadius.Get(NS_FULL_TO_HALF_CORNER(aFullCorner, true)); // 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( uint8_t(StyleBoxShadowType::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(StyleEffects()->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(); // XXXheycam As in SetValueToStyleImage, we might want to use the // URL stored in the nsStyleImageRequest's mImageValue if we // failed to resolve the imgRequestProxy. imgRequestProxy* image = list->GetListStyleImage(); if (!image) { val->SetIdent(eCSSKeyword_none); } else { nsCOMPtr uri; image->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(); nsAutoString 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::DoGetInitialLetter() { const nsStyleTextReset* textReset = StyleTextReset(); RefPtr val = new nsROCSSPrimitiveValue; if (textReset->mInitialLetterSink == 0) { val->SetIdent(eCSSKeyword_normal); return val.forget(); } else { RefPtr valueList = GetROCSSValueList(false); val->SetNumber(textReset->mInitialLetterSize); valueList->AppendCSSValue(val.forget()); RefPtr second = new nsROCSSPrimitiveValue; second->SetNumber(textReset->mInitialLetterSink); valueList->AppendCSSValue(second.forget()); return valueList.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, StyleDisplay()->mVerticalAlign, false, nullptr, 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_unsafe); 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->mTextDecorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_SOLID; StyleComplexColor color = textReset->mTextDecorationColor; if (isInitialStyle && color.IsCurrentColor()) { return DoGetTextDecorationLine(); } RefPtr valueList = GetROCSSValueList(false); valueList->AppendCSSValue(DoGetTextDecorationLine()); if (!isInitialStyle) { valueList->AppendCSSValue(DoGetTextDecorationStyle()); } if (!color.IsCurrentColor()) { valueList->AppendCSSValue(DoGetTextDecorationColor()); } return valueList.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextDecorationColor() { RefPtr val = new nsROCSSPrimitiveValue; SetValueFromComplexColor(val, StyleTextReset()->mTextDecorationColor); 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()->mTextDecorationStyle, nsCSSProps::kTextDecorationStyleKTable)); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextEmphasisColor() { RefPtr val = new nsROCSSPrimitiveValue; SetValueFromComplexColor(val, StyleText()->mTextEmphasisColor); 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; nsAutoString 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); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetTextJustify() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleText()->mTextJustify, nsCSSProps::kTextJustifyKTable)); 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) { nsAutoString 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) { nsAutoString 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; SetValueToCoord(val, StyleText()->mTabSize, true); 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(StyleUIReset()->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::DoGetOverflowWrap() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleText()->mOverflowWrap, nsCSSProps::kOverflowWrapKTable)); 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::DoGetWebkitTextFillColor() { RefPtr val = new nsROCSSPrimitiveValue; SetValueFromComplexColor(val, StyleText()->mWebkitTextFillColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWebkitTextStrokeColor() { RefPtr val = new nsROCSSPrimitiveValue; SetValueFromComplexColor(val, StyleText()->mWebkitTextStrokeColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetWebkitTextStrokeWidth() { RefPtr val = new nsROCSSPrimitiveValue; val->SetAppUnits(StyleText()->mWebkitTextStrokeWidth.GetCoordValue()); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetPointerEvents() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(StyleUserInterface()->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::DoGetCaretColor() { RefPtr val = new nsROCSSPrimitiveValue; SetValueFromComplexColor(val, StyleUserInterface()->mCaretColor); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetCursor() { RefPtr valueList = GetROCSSValueList(true); const nsStyleUserInterface *ui = StyleUserInterface(); for (const nsCursorImage& item : ui->mCursorImages) { 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(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()->mAlignContent; 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()->mAlignItems; nsCSSValue::AppendAlignJustifyValueToString(align, str); val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetAlignSelf() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString str; auto align = StylePosition()->mAlignSelf; nsCSSValue::AppendAlignJustifyValueToString(align, str); val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetJustifyContent() { RefPtr val = new nsROCSSPrimitiveValue; nsAutoString str; auto justify = StylePosition()->mJustifyContent; 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()->mJustifySelf; nsCSSValue::AppendAlignJustifyValueToString(justify, str); val->SetString(str); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetFloatEdge() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent( nsCSSProps::ValueToKeywordEnum(uint8_t(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(uint8_t(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 nsStyleEffects* effects = StyleEffects(); if (effects->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 (effects->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) { topVal->SetIdent(eCSSKeyword_auto); } else { topVal->SetAppUnits(effects->mClip.y); } if (effects->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) { rightVal->SetIdent(eCSSKeyword_auto); } else { rightVal->SetAppUnits(effects->mClip.width + effects->mClip.x); } if (effects->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) { bottomVal->SetIdent(eCSSKeyword_auto); } else { bottomVal->SetAppUnits(effects->mClip.height + effects->mClip.y); } if (effects->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) { leftVal->SetIdent(eCSSKeyword_auto); } else { leftVal->SetAppUnits(effects->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 == mozilla::StyleDisplay::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 == mozilla::StyleDisplay::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, nullptr, nsCSSProps::kWidthKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMaxWidth() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StylePosition()->mMaxWidth, true, nullptr, nsCSSProps::kWidthKTable); return val.forget(); } bool nsComputedDOMStyle::ShouldHonorMinSizeAutoInAxis(PhysicalAxis aAxis) { // A {flex,grid} item's min-{width|height} "auto" value gets special // treatment in getComputedStyle(). // https://drafts.csswg.org/css-flexbox-1/#valdef-min-width-auto // https://drafts.csswg.org/css-grid/#min-size-auto // In most cases, "min-{width|height}: auto" is mapped to "0px", unless // we're a flex item (and the min-size is in the flex container's main // axis), or we're a grid item, AND we also have overflow:visible. // Note: We only need to bother checking one "overflow" subproperty for // "visible", because a non-"visible" value in either axis would force the // other axis to also be non-"visible" as well. if (mOuterFrame) { nsIFrame* containerFrame = mOuterFrame->GetParent(); if (containerFrame && StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) { auto containerType = containerFrame->GetType(); if (containerType == nsGkAtoms::flexContainerFrame && (static_cast(containerFrame)->IsHorizontal() == (aAxis == eAxisHorizontal))) { return true; } if (containerType == nsGkAtoms::gridContainerFrame) { return true; } } } return false; } already_AddRefed nsComputedDOMStyle::DoGetMinHeight() { RefPtr val = new nsROCSSPrimitiveValue; nsStyleCoord minHeight = StylePosition()->mMinHeight; if (eStyleUnit_Auto == minHeight.GetUnit() && !ShouldHonorMinSizeAutoInAxis(eAxisVertical)) { minHeight.SetCoordValue(0); } SetValueToCoord(val, minHeight, true, nullptr, nsCSSProps::kWidthKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMinWidth() { RefPtr val = new nsROCSSPrimitiveValue; nsStyleCoord minWidth = StylePosition()->mMinWidth; if (eStyleUnit_Auto == minWidth.GetUnit() && !ShouldHonorMinSizeAutoInAxis(eAxisHorizontal)) { minWidth.SetCoordValue(0); } SetValueToCoord(val, minWidth, true, nullptr, nsCSSProps::kWidthKTable); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMixBlendMode() { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleEffects()->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); } 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->PrincipalChildList().FirstChild(); 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 = ReflowInput::CalcLineHeight(mElement, 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; SetValueFromComplexColor(val, StyleBorder()->mBorderColor[aSide]); 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 table wrapper 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_WARNING_ASSERTION( 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->Type()) { case eStyleSVGPaintType_None: val->SetIdent(eCSSKeyword_none); break; case eStyleSVGPaintType_Color: SetToRGBAColor(val, paint->GetColor()); break; case eStyleSVGPaintType_Server: { RefPtr valueList = GetROCSSValueList(false); RefPtr fallback = new nsROCSSPrimitiveValue; SetValueToURLValue(paint->GetPaintServer(), val); SetToRGBAColor(fallback, paint->GetFallbackColor()); valueList->AppendCSSValue(val.forget()); valueList->AppendCSSValue(fallback.forget()); return valueList.forget(); } case eStyleSVGPaintType_ContextFill: val->SetIdent(eCSSKeyword_context_fill); // XXXheycam context-fill and context-stroke can have fallback colors, // so they should be serialized here too 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; SetValueToURLValue(StyleSVG()->mMarkerEnd, val); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMarkerMid() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToURLValue(StyleSVG()->mMarkerMid, val); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMarkerStart() { RefPtr val = new nsROCSSPrimitiveValue; SetValueToURLValue(StyleSVG()->mMarkerStart, val); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetStrokeDasharray() { const nsStyleSVG* svg = StyleSVG(); if (svg->mStrokeDasharray.IsEmpty()) { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } RefPtr valueList = GetROCSSValueList(true); for (uint32_t i = 0; i < svg->mStrokeDasharray.Length(); 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(StyleVisibility()->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(StyleText()->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(); } 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::CreatePrimitiveValueForBasicShape( const StyleBasicShape* aStyleBasicShape) { MOZ_ASSERT(aStyleBasicShape, "Expect a valid basic shape pointer!"); StyleBasicShapeType type = aStyleBasicShape->GetShapeType(); // Shape function name and opening parenthesis. nsAutoString shapeFunctionString; AppendASCIItoUTF16(nsCSSKeywords::GetStringValue( aStyleBasicShape->GetShapeTypeName()), shapeFunctionString); shapeFunctionString.Append('('); switch (type) { case StyleBasicShapeType::Polygon: { bool hasEvenOdd = aStyleBasicShape->GetFillRule() == StyleFillRule::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 StyleBasicShapeType::Circle: case StyleBasicShapeType::Ellipse: { const nsTArray& radii = aStyleBasicShape->Coordinates(); MOZ_ASSERT(radii.Length() == (type == StyleBasicShapeType::Circle ? 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 StyleBasicShapeType::Inset: { 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); return functionValue.forget(); } template already_AddRefed nsComputedDOMStyle::CreatePrimitiveValueForShapeSource( const StyleBasicShape* aStyleBasicShape, ReferenceBox aReferenceBox, const KTableEntry aBoxKeywordTable[]) { RefPtr valueList = GetROCSSValueList(false); if (aStyleBasicShape) { valueList->AppendCSSValue( CreatePrimitiveValueForBasicShape(aStyleBasicShape)); } if (aReferenceBox == ReferenceBox::NoBox) { return valueList.forget(); } RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(nsCSSProps::ValueToKeywordEnum(aReferenceBox, aBoxKeywordTable)); valueList->AppendCSSValue(val.forget()); return valueList.forget(); } template already_AddRefed nsComputedDOMStyle::GetShapeSource( const StyleShapeSource& aShapeSource, const KTableEntry aBoxKeywordTable[]) { switch (aShapeSource.GetType()) { case StyleShapeSourceType::Shape: return CreatePrimitiveValueForShapeSource(aShapeSource.GetBasicShape(), aShapeSource.GetReferenceBox(), aBoxKeywordTable); case StyleShapeSourceType::Box: return CreatePrimitiveValueForShapeSource(nullptr, aShapeSource.GetReferenceBox(), aBoxKeywordTable); case StyleShapeSourceType::URL: { RefPtr val = new nsROCSSPrimitiveValue; SetValueToURLValue(aShapeSource.GetURL(), val); return val.forget(); } case StyleShapeSourceType::None: { RefPtr val = new nsROCSSPrimitiveValue; val->SetIdent(eCSSKeyword_none); return val.forget(); } default: NS_NOTREACHED("unexpected type"); } return nullptr; } already_AddRefed nsComputedDOMStyle::DoGetClipPath() { return GetShapeSource(StyleSVGReset()->mClipPath, nsCSSProps::kClipPathGeometryBoxKTable); } already_AddRefed nsComputedDOMStyle::DoGetShapeOutside() { return GetShapeSource(StyleDisplay()->mShapeOutside, nsCSSProps::kShapeOutsideShapeBoxKTable); } 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) { MOZ_ASSERT(aStyleFilter.GetURL() && aStyleFilter.GetURL()->GetURI()); SetValueToURLValue(aStyleFilter.GetURL(), value); 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 = StyleEffects()->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() { const nsStyleSVGReset* svg = StyleSVGReset(); const nsStyleImageLayers::Layer& firstLayer = svg->mMask.mLayers[0]; // Mask is now a shorthand, but it used to be a longhand, so that we // need to support computed style for the cases where it used to be // a longhand. if (svg->mMask.mImageCount > 1 || firstLayer.mClip != StyleGeometryBox::Border || firstLayer.mOrigin != StyleGeometryBox::Border || firstLayer.mComposite != NS_STYLE_MASK_COMPOSITE_ADD || firstLayer.mMaskMode != NS_STYLE_MASK_MODE_MATCH_SOURCE || !nsStyleImageLayers::IsInitialPositionForLayerType( firstLayer.mPosition, nsStyleImageLayers::LayerType::Mask) || !firstLayer.mRepeat.IsInitialValue() || !firstLayer.mSize.IsInitialValue() || !(firstLayer.mImage.GetType() == eStyleImageType_Null || firstLayer.mImage.GetType() == eStyleImageType_Image)) { return nullptr; } RefPtr val = new nsROCSSPrimitiveValue; SetValueToURLValue(firstLayer.mSourceURI, val); return val.forget(); } already_AddRefed nsComputedDOMStyle::DoGetMaskClip() { return GetBackgroundList(&nsStyleImageLayers::Layer::mClip, &nsStyleImageLayers::mClipCount, StyleSVGReset()->mMask, nsCSSProps::kMaskClipKTable); } already_AddRefed nsComputedDOMStyle::DoGetMaskComposite() { return GetBackgroundList(&nsStyleImageLayers::Layer::mComposite, &nsStyleImageLayers::mCompositeCount, StyleSVGReset()->mMask, nsCSSProps::kImageLayerCompositeKTable); } already_AddRefed nsComputedDOMStyle::DoGetMaskImage() { const nsStyleImageLayers& layers = StyleSVGReset()->mMask; return DoGetImageLayerImage(layers); } already_AddRefed nsComputedDOMStyle::DoGetMaskMode() { return GetBackgroundList(&nsStyleImageLayers::Layer::mMaskMode, &nsStyleImageLayers::mMaskModeCount, StyleSVGReset()->mMask, nsCSSProps::kImageLayerModeKTable); } already_AddRefed nsComputedDOMStyle::DoGetMaskOrigin() { return GetBackgroundList(&nsStyleImageLayers::Layer::mOrigin, &nsStyleImageLayers::mOriginCount, StyleSVGReset()->mMask, nsCSSProps::kMaskOriginKTable); } already_AddRefed nsComputedDOMStyle::DoGetMaskPosition() { const nsStyleImageLayers& layers = StyleSVGReset()->mMask; return DoGetImageLayerPosition(layers); } already_AddRefed nsComputedDOMStyle::DoGetMaskPositionX() { const nsStyleImageLayers& layers = StyleSVGReset()->mMask; return DoGetImageLayerPositionX(layers); } already_AddRefed nsComputedDOMStyle::DoGetMaskPositionY() { const nsStyleImageLayers& layers = StyleSVGReset()->mMask; return DoGetImageLayerPositionY(layers); } already_AddRefed nsComputedDOMStyle::DoGetMaskRepeat() { const nsStyleImageLayers& layers = StyleSVGReset()->mMask; return DoGetImageLayerRepeat(layers); } already_AddRefed nsComputedDOMStyle::DoGetMaskSize() { const nsStyleImageLayers& layers = StyleSVGReset()->mMask; return DoGetImageLayerSize(layers); } 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; nsCSSPropertyID 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, 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(mElement == aContent, "didn't we register mElement?"); 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 }