gecko-dev/layout/style/nsComputedDOMStyle.cpp
Emilio Cobos Álvarez 0112d2ebe1 Bug 1507305 - Use Servo to serialize most of the already-exposed shorthands. r=heycam
Skip mask and text-decoration for now since there's a single test-case failing
for each that seem worth fixing in a different bug:

 * For mask, there's the case of setting mask: url(foo.svg#bar), which we test
   we serialize absolutely. But given we're uncomputing it we don't serialize
   the resolved URL. Chrome doesn't either so we could just change the test, but
   even if we decided to do it we probably should do it in a separate bug.

 * For text-decoration, we need to resolve the value, when it's an interpolation
   between currentcolor and other color. Right now that returns the empty string
   which is not great:

     https://searchfox.org/mozilla-central/rev/d850d799a0009f851b5535580e0a8b4bb2c591d7/servo/components/style/values/specified/color.rs#194

   So I need to come up with something. Probably we need to implement the "hard"
   version of the serialization code that doesn't reuse the animation machinery.

   Definitely a separate bug though.

While at it, also serialize all <position> longhands with Servo, so that I can
clean up the tests.

Differential Revision: https://phabricator.services.mozilla.com/D11948

--HG--
extra : moz-landing-system : lando
2018-11-15 04:10:08 +00:00

4405 lines
136 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* DOM object returned from element.getComputedStyle() */
#include "nsComputedDOMStyle.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/FontPropertyTypes.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPtr.h"
#include "nsError.h"
#include "nsIFrame.h"
#include "nsIFrameInlines.h"
#include "mozilla/ComputedStyle.h"
#include "nsIScrollableFrame.h"
#include "nsContentUtils.h"
#include "nsIContent.h"
#include "nsStyleConsts.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 "nsCSSProps.h"
#include "nsCSSPseudoElements.h"
#include "mozilla/EffectSet.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/RestyleManager.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 "mozilla/dom/ElementInlines.h"
#include "prtime.h"
#include "nsWrapperCacheInlines.h"
#include "mozilla/AppUnits.h"
#include <algorithm>
#include "mozilla/ComputedStyleInlines.h"
using namespace mozilla;
using namespace mozilla::dom;
#if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon)
#define DEBUG_ComputedDOMStyle
#endif
/*
* This is the implementation of the readonly CSSStyleDeclaration that is
* returned by the getComputedStyle() function.
*/
already_AddRefed<nsComputedDOMStyle>
NS_NewComputedDOMStyle(dom::Element* aElement,
const nsAString& aPseudoElt,
nsIDocument* aDocument,
nsComputedDOMStyle::StyleType aStyleType)
{
RefPtr<nsComputedDOMStyle> computedStyle =
new nsComputedDOMStyle(aElement, aPseudoElt, aDocument, aStyleType);
return computedStyle.forget();
}
static nsDOMCSSValueList*
GetROCSSValueList(bool aCommaDelimited)
{
return new nsDOMCSSValueList(aCommaDelimited);
}
template<typename T>
already_AddRefed<CSSValue>
GetBackgroundList(T nsStyleImageLayers::Layer::* aMember,
uint32_t nsStyleImageLayers::* aCount,
const nsStyleImageLayers& aLayers,
const nsCSSKTableEntry aTable[])
{
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
for (uint32_t i = 0, i_end = aLayers.*aCount; i < i_end; ++i) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(nsCSSProps::ValueToKeywordEnum(aLayers.mLayers[i].*aMember, aTable));
valueList->AppendCSSValue(val.forget());
}
return valueList.forget();
}
// Whether aDocument needs to restyle for aElement
static bool
DocumentNeedsRestyle(
const nsIDocument* aDocument,
Element* aElement,
nsAtom* aPseudo)
{
nsIPresShell* shell = aDocument->GetShell();
if (!shell) {
return true;
}
nsPresContext* presContext = shell->GetPresContext();
MOZ_ASSERT(presContext);
// Unfortunately we don't know if the sheet change affects mElement or not, so
// just assume it will and that we need to flush normally.
ServoStyleSet* styleSet = shell->StyleSet();
if (styleSet->StyleSheetsHaveChanged()) {
return true;
}
// Pending media query updates can definitely change style on the element. For
// example, if you change the zoom factor and then call getComputedStyle, you
// should be able to observe the style with the new media queries.
//
// TODO(emilio): Does this need to also check the user font set? (it affects
// ch / ex units).
if (presContext->HasPendingMediaQueryUpdates()) {
// So gotta flush.
return true;
}
// If the pseudo-element is animating, make sure to flush.
if (aElement->MayHaveAnimations() && aPseudo) {
if (aPseudo == nsCSSPseudoElements::before()) {
if (EffectSet::GetEffectSet(aElement, CSSPseudoElementType::before)) {
return true;
}
} else if (aPseudo == nsCSSPseudoElements::after()) {
if (EffectSet::GetEffectSet(aElement, CSSPseudoElementType::after)) {
return true;
}
}
}
// For Servo, we need to process the restyle-hint-invalidations first, to
// expand LaterSiblings hint, so that we can look whether ancestors need
// restyling.
RestyleManager* restyleManager = presContext->RestyleManager();
restyleManager->ProcessAllPendingAttributeAndStateInvalidations();
if (!presContext->EffectCompositor()->HasPendingStyleUpdates() &&
!aDocument->GetServoRestyleRoot()) {
return false;
}
// Then if there is a restyle root, we check if the root is an ancestor of
// this content. If it is not, then we don't need to restyle immediately.
// Note this is different from Gecko: we only check if any ancestor needs
// to restyle _itself_, not descendants, since dirty descendants can be
// another subtree.
return restyleManager->HasPendingRestyleAncestor(aElement);
}
/**
* 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 ComputedStyleMap
{
friend class nsComputedDOMStyle;
struct Entry
{
// Create a pointer-to-member-function type.
typedef already_AddRefed<CSSValue> (nsComputedDOMStyle::*ComputeMethod)();
nsCSSPropertyID mProperty;
ComputeMethod mGetter;
bool IsLayoutFlushNeeded() const
{
return nsCSSProps::PropHasFlags(mProperty,
CSSPropFlags::GetCSNeedsLayoutFlush);
}
bool IsEnabled() const
{
return nsCSSProps::IsEnabled(mProperty, CSSEnabledState::eForAllContent);
}
};
// This generated file includes definition of kEntries which is typed
// Entry[] and used below, so this #include has to be put here.
#include "nsComputedDOMStyleGenerated.cpp"
/**
* 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.
/**
* 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[ArrayLength(kEntries)];
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];
}
};
constexpr ComputedStyleMap::Entry
ComputedStyleMap::kEntries[ArrayLength(kEntries)];
void
ComputedStyleMap::Update()
{
if (!IsDirty()) {
return;
}
uint32_t index = 0;
for (uint32_t i = 0; i < ArrayLength(kEntries); 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)
, mComputedStyleGeneration(0)
, mExposeVisitedStyle(false)
, mResolvedComputedStyle(false)
#ifdef DEBUG
, mFlushedPendingReflows(false)
#endif
{
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;
mPseudo = nsCSSPseudoElements::GetPseudoAtom(aPseudoElt);
}
nsComputedDOMStyle::~nsComputedDOMStyle()
{
MOZ_ASSERT(!mResolvedComputedStyle,
"Should have called ClearComputedStyle() during last release.");
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsComputedDOMStyle)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle)
tmp->ClearComputedStyle(); // 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->HasKnownLiveWrapper();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsComputedDOMStyle)
return tmp->HasKnownLiveWrapper();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsComputedDOMStyle)
return tmp->HasKnownLiveWrapper();
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_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsComputedDOMStyle)
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
nsComputedDOMStyle, ClearComputedStyle())
nsresult
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);
}
nsresult
nsComputedDOMStyle::SetPropertyValue(const nsCSSPropertyID aPropID,
const nsAString& aValue,
nsIPrincipal* aSubjectPrincipal)
{
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
void
nsComputedDOMStyle::GetCssText(nsAString& aCssText)
{
aCssText.Truncate();
}
void
nsComputedDOMStyle::SetCssText(const nsAString& aCssText,
nsIPrincipal* aSubjectPrincipal,
ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
}
uint32_t
nsComputedDOMStyle::Length()
{
// Make sure we have up to date style so that we can include custom
// properties.
UpdateCurrentStyleSources(false);
if (!mComputedStyle) {
return 0;
}
uint32_t length =
GetComputedStyleMap()->Length() +
Servo_GetCustomPropertiesCount(mComputedStyle);
ClearCurrentStyleSources();
return length;
}
css::Rule*
nsComputedDOMStyle::GetParentRule()
{
return nullptr;
}
NS_IMETHODIMP
nsComputedDOMStyle::GetPropertyValue(const nsAString& aPropertyName,
nsAString& aReturn)
{
aReturn.Truncate();
nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName);
const ComputedStyleMap::Entry* entry = nullptr;
if (prop != eCSSPropertyExtra_variable) {
entry = GetComputedStyleMap()->FindEntryForProperty(prop);
if (!entry) {
return NS_OK;
}
}
const bool layoutFlushIsNeeded = entry && entry->IsLayoutFlushNeeded();
UpdateCurrentStyleSources(layoutFlushIsNeeded);
if (!mComputedStyle) {
return NS_OK;
}
auto cleanup = mozilla::MakeScopeExit([&] {
ClearCurrentStyleSources();
});
if (!entry) {
MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
const nsAString& name =
Substring(aPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
Servo_GetCustomPropertyValue(mComputedStyle, &name, &aReturn);
return NS_OK;
}
if (nsCSSProps::PropHasFlags(prop, CSSPropFlags::IsLogical)) {
MOZ_ASSERT(entry);
MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter);
prop = Servo_ResolveLogicalProperty(prop, mComputedStyle);
entry = GetComputedStyleMap()->FindEntryForProperty(prop);
MOZ_ASSERT(layoutFlushIsNeeded == entry->IsLayoutFlushNeeded(),
"Logical and physical property don't agree on whether layout is "
"needed");
}
if (!nsCSSProps::PropHasFlags(prop, CSSPropFlags::SerializedByServo)) {
if (RefPtr<CSSValue> value = (this->*entry->mGetter)()) {
ErrorResult rv;
nsString text;
value->GetCssText(text, rv);
aReturn.Assign(text);
return rv.StealNSResult();
}
return NS_OK;
}
MOZ_ASSERT(entry->mGetter == &nsComputedDOMStyle::DummyGetter);
Servo_GetPropertyValue(mComputedStyle, prop, &aReturn);
return NS_OK;
}
/* static */
already_AddRefed<ComputedStyle>
nsComputedDOMStyle::GetComputedStyle(Element* aElement,
nsAtom* aPseudo,
StyleType aStyleType)
{
if (nsIDocument* doc = aElement->GetComposedDoc()) {
doc->FlushPendingNotifications(FlushType::Style);
}
return GetComputedStyleNoFlush(aElement, aPseudo, aStyleType);
}
/**
* The following function checks whether we need to explicitly resolve the style
* again, even though we have a style 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 mozilla::ComputedStyle* aStyle)
{
MOZ_ASSERT(aStyle);
// TODO(emilio): We may want to avoid re-resolving pseudo-element styles
// more often.
return aStyle->HasPseudoElementData() && !aStyle->GetPseudo();
}
static inline CSSPseudoElementType
GetPseudoType(nsAtom* aPseudo)
{
if (!aPseudo) {
return CSSPseudoElementType::NotPseudo;
}
return nsCSSPseudoElements::GetPseudoType(
aPseudo, CSSEnabledState::eForAllContent);
}
already_AddRefed<ComputedStyle>
nsComputedDOMStyle::DoGetComputedStyleNoFlush(Element* aElement,
nsAtom* 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 = nsContentUtils::GetPresShellForContent(aElement);
bool inDocWithShell = true;
if (!presShell) {
inDocWithShell = false;
presShell = aPresShell;
if (!presShell) {
return nullptr;
}
}
CSSPseudoElementType pseudoType = GetPseudoType(aPseudo);
if (aPseudo && pseudoType >= CSSPseudoElementType::Count) {
return nullptr;
}
if (aElement->IsInNativeAnonymousSubtree() && !aElement->IsInComposedDoc()) {
// Normal web content can't access NAC, but Accessibility, DevTools and
// Editor use this same API and this may get called for anonymous content.
// Computing the style of a pseudo-element that doesn't have a parent doesn't
// really make sense.
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 (inDocWithShell &&
aStyleType == eAll &&
!aElement->IsHTMLElement(nsGkAtoms::area)) {
Element* element = nullptr;
if (aPseudo == nsCSSPseudoElements::before()) {
element = nsLayoutUtils::GetBeforePseudo(aElement);
} else if (aPseudo == nsCSSPseudoElements::after()) {
element = nsLayoutUtils::GetAfterPseudo(aElement);
} else if (!aPseudo) {
element = aElement;
}
if (element) {
if (nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(element)) {
ComputedStyle* result = styleFrame->Style();
// Don't use the style if it was influenced by pseudo-elements,
// since then it's not the primary style for this element / pseudo.
if (!MustReresolveStyle(result)) {
RefPtr<ComputedStyle> 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.
ServoStyleSet* styleSet = presShell->StyleSet();
StyleRuleInclusion rules = aStyleType == eDefaultOnly
? StyleRuleInclusion::DefaultOnly
: StyleRuleInclusion::All;
RefPtr<ComputedStyle> result =
styleSet->ResolveStyleLazily(aElement, pseudoType, rules);
return result.forget();
}
already_AddRefed<ComputedStyle>
nsComputedDOMStyle::GetUnanimatedComputedStyleNoFlush(Element* aElement,
nsAtom* aPseudo)
{
RefPtr<ComputedStyle> style = GetComputedStyleNoFlush(aElement, aPseudo);
if (!style) {
return nullptr;
}
CSSPseudoElementType pseudoType = GetPseudoType(aPseudo);
nsIPresShell* shell = aElement->OwnerDoc()->GetShell();
MOZ_ASSERT(shell, "How in the world did we get a style a few lines above?");
Element* elementOrPseudoElement =
EffectCompositor::GetElementToRestyle(aElement, pseudoType);
if (!elementOrPseudoElement) {
return nullptr;
}
return shell->StyleSet()->
GetBaseContextForElement(elementOrPseudoElement, style);
}
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 void
AddImageURL(nsIURI& aURI, nsTArray<nsString>& aURLs)
{
nsAutoCString spec;
nsresult rv = aURI.GetSpec(spec);
if (NS_FAILED(rv)) {
return;
}
aURLs.AppendElement(NS_ConvertUTF8toUTF16(spec));
}
static void
AddImageURL(const css::URLValue& aURL, nsTArray<nsString>& aURLs)
{
if (aURL.IsLocalRef()) {
return;
}
if (nsIURI* uri = aURL.GetURI()) {
AddImageURL(*uri, aURLs);
}
}
static void
AddImageURL(const nsStyleImageRequest& aRequest, nsTArray<nsString>& aURLs)
{
if (auto* value = aRequest.GetImageValue()) {
AddImageURL(*value, aURLs);
}
}
static void
AddImageURL(const nsStyleImage& aImage, nsTArray<nsString>& aURLs)
{
if (auto* urlValue = aImage.GetURLValue()) {
AddImageURL(*urlValue, aURLs);
}
}
static void
AddImageURL(const StyleShapeSource& aShapeSource, nsTArray<nsString>& aURLs)
{
switch (aShapeSource.GetType()) {
case StyleShapeSourceType::URL:
AddImageURL(aShapeSource.URL(), aURLs);
break;
case StyleShapeSourceType::Image:
AddImageURL(aShapeSource.ShapeImage(), aURLs);
break;
default:
break;
}
}
static void
AddImageURLs(const nsStyleImageLayers& aLayers, nsTArray<nsString>& aURLs)
{
for (auto i : IntegerRange(aLayers.mLayers.Length())) {
AddImageURL(aLayers.mLayers[i].mImage, aURLs);
}
}
// FIXME(stylo-everywhere): This should be `const ComputedStyle&`.
static void
CollectImageURLsForProperty(nsCSSPropertyID aProp,
ComputedStyle& aStyle,
nsTArray<nsString>& aURLs)
{
if (nsCSSProps::IsShorthand(aProp)) {
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProp, CSSEnabledState::eForAllContent) {
CollectImageURLsForProperty(*p, aStyle, aURLs);
}
return;
}
switch (aProp) {
case eCSSProperty_cursor:
for (auto& image : aStyle.StyleUI()->mCursorImages) {
AddImageURL(*image.mImage, aURLs);
}
break;
case eCSSProperty_background_image:
AddImageURLs(aStyle.StyleBackground()->mImage, aURLs);
break;
case eCSSProperty_mask_clip:
AddImageURLs(aStyle.StyleSVGReset()->mMask, aURLs);
break;
case eCSSProperty_list_style_image:
if (nsStyleImageRequest* image = aStyle.StyleList()->mListStyleImage) {
AddImageURL(*image, aURLs);
}
break;
case eCSSProperty_border_image_source:
AddImageURL(aStyle.StyleBorder()->mBorderImageSource, aURLs);
break;
case eCSSProperty_clip_path:
AddImageURL(aStyle.StyleSVGReset()->mClipPath, aURLs);
break;
case eCSSProperty_shape_outside:
AddImageURL(aStyle.StyleDisplay()->mShapeOutside, aURLs);
break;
default:
break;
}
}
void
nsComputedDOMStyle::GetCSSImageURLs(const nsAString& aPropertyName,
nsTArray<nsString>& aImageURLs,
mozilla::ErrorResult& aRv)
{
nsCSSPropertyID prop = nsCSSProps::LookupProperty(aPropertyName);
if (prop == eCSSProperty_UNKNOWN) {
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return;
}
UpdateCurrentStyleSources(false);
if (!mComputedStyle) {
return;
}
CollectImageURLsForProperty(prop, *mComputedStyle, aImageURLs);
ClearCurrentStyleSources();
}
// nsDOMCSSDeclaration abstract methods which should never be called
// on a nsComputedDOMStyle object, but must be defined to avoid
// compile errors.
DeclarationBlock*
nsComputedDOMStyle::GetOrCreateCSSDeclaration(Operation aOperation,
DeclarationBlock** aCreated)
{
MOZ_CRASH("called nsComputedDOMStyle::GetCSSDeclaration");
}
nsresult
nsComputedDOMStyle::SetCSSDeclaration(DeclarationBlock*,
MutationClosureData*)
{
MOZ_CRASH("called nsComputedDOMStyle::SetCSSDeclaration");
}
nsIDocument*
nsComputedDOMStyle::DocToUpdate()
{
MOZ_CRASH("called nsComputedDOMStyle::DocToUpdate");
}
nsDOMCSSDeclaration::ParsingEnvironment
nsComputedDOMStyle::GetParsingEnvironment(
nsIPrincipal* aSubjectPrincipal) const
{
MOZ_CRASH("called nsComputedDOMStyle::GetParsingEnvironment");
}
void
nsComputedDOMStyle::ClearComputedStyle()
{
if (mResolvedComputedStyle) {
mResolvedComputedStyle = false;
mElement->RemoveMutationObserver(this);
}
mComputedStyle = nullptr;
}
void
nsComputedDOMStyle::SetResolvedComputedStyle(RefPtr<ComputedStyle>&& aContext,
uint64_t aGeneration)
{
if (!mResolvedComputedStyle) {
mResolvedComputedStyle = true;
mElement->AddMutationObserver(this);
}
mComputedStyle = aContext;
mComputedStyleGeneration = aGeneration;
}
void
nsComputedDOMStyle::SetFrameComputedStyle(mozilla::ComputedStyle* aStyle,
uint64_t aGeneration)
{
ClearComputedStyle();
mComputedStyle = aStyle;
mComputedStyleGeneration = aGeneration;
}
bool
nsComputedDOMStyle::NeedsToFlush(nsIDocument* aDocument) const
{
// If mElement is not in the same document, we could do some checks to know if
// there are some pending restyles can be ignored across documents (since we
// will use the caller document's style), but it can be complicated and should
// be an edge case, so we just don't bother to do the optimization in this
// case.
//
// FIXME(emilio): This is likely to want GetComposedDoc() instead of
// OwnerDoc().
if (aDocument != mElement->OwnerDoc()) {
return true;
}
if (DocumentNeedsRestyle(aDocument, mElement, mPseudo)) {
return true;
}
// If parent document is there, also needs to check if there is some change
// that needs to flush this document (e.g. size change for iframe).
while (nsIDocument* parentDocument = aDocument->GetParentDocument()) {
Element* element = parentDocument->FindContentForSubDocument(aDocument);
if (DocumentNeedsRestyle(parentDocument, element, nullptr)) {
return true;
}
aDocument = parentDocument;
}
return false;
}
void
nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
{
nsCOMPtr<nsIDocument> document = do_QueryReferent(mDocumentWeak);
if (!document) {
ClearComputedStyle();
return;
}
// TODO(emilio): We may want to handle a few special-cases here:
//
// * https://github.com/w3c/csswg-drafts/issues/1964
// * https://github.com/w3c/csswg-drafts/issues/1548
// If the property we are computing relies on layout, then we must flush.
const bool needsToFlush = aNeedsLayoutFlush || NeedsToFlush(document);
if (needsToFlush) {
// 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 ? FlushType::Layout : FlushType::Style);
}
#ifdef DEBUG
mFlushedPendingReflows = aNeedsLayoutFlush;
#endif
nsCOMPtr<nsIPresShell> presShellForContent =
nsContentUtils::GetPresShellForContent(mElement);
if (presShellForContent && presShellForContent->GetDocument() != document) {
presShellForContent->GetDocument()->FlushPendingNotifications(FlushType::Style);
if (presShellForContent->IsDestroying()) {
presShellForContent = nullptr;
}
}
mPresShell = document->GetShell();
if (!mPresShell || !mPresShell->GetPresContext()) {
ClearComputedStyle();
return;
}
// We need to use GetUndisplayedRestyleGeneration instead of
// GetRestyleGeneration, because the caching of mComputedStyle is an
// optimization that is useful only for displayed elements.
// For undisplayed elements we need to take into account any DOM changes that
// might cause a restyle, because Servo will not increase the generation for
// undisplayed elements.
// As for Gecko, GetUndisplayedRestyleGeneration is effectively equal to
// GetRestyleGeneration, since the generation is incremented whenever we
// process restyles.
uint64_t currentGeneration =
mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration();
if (mComputedStyle) {
// 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.
//
// So we always need to update the style to ensure it it up-to-date.
if (mComputedStyleGeneration == currentGeneration &&
mElement->IsInComposedDoc()) {
// Our cached style is still valid.
return;
}
// We've processed some restyles, so the cached style might be out of date.
mComputedStyle = 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()) {
nsAtom* property = mPseudo == nsCSSPseudoElements::before()
? nsGkAtoms::beforePseudoProperty
: nsGkAtoms::afterPseudoProperty;
auto* pseudo = static_cast<Element*>(mElement->GetProperty(property));
mOuterFrame = pseudo ? pseudo->GetPrimaryFrame() : nullptr;
}
mInnerFrame = mOuterFrame;
if (mOuterFrame) {
LayoutFrameType type = mOuterFrame->Type();
if (type == LayoutFrameType::TableWrapper) {
// 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");
}
SetFrameComputedStyle(mInnerFrame->Style(), currentGeneration);
NS_ASSERTION(mComputedStyle, "Frame without style?");
}
}
if (!mComputedStyle || MustReresolveStyle(mComputedStyle)) {
// Need to resolve a style.
RefPtr<ComputedStyle> resolvedComputedStyle =
DoGetComputedStyleNoFlush(
mElement,
mPseudo,
presShellForContent ? presShellForContent.get() : mPresShell,
mStyleType);
if (!resolvedComputedStyle) {
ClearComputedStyle();
return;
}
// No need to re-get the generation, even though GetComputedStyle
// will flush, since we flushed style at the top of this function.
// We don't need to check this if we only flushed the parent.
NS_ASSERTION(!needsToFlush ||
currentGeneration ==
mPresShell->GetPresContext()->GetUndisplayedRestyleGeneration(),
"why should we have flushed style again?");
SetResolvedComputedStyle(std::move(resolvedComputedStyle), currentGeneration);
NS_ASSERTION(mPseudo || !mComputedStyle->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 && mComputedStyle->RelevantLinkVisited()) {
if (ComputedStyle* styleIfVisited = mComputedStyle->GetStyleIfVisited()) {
mComputedStyle = styleIfVisited;
}
}
}
void
nsComputedDOMStyle::ClearCurrentStyleSources()
{
// Release the current style if we got it off the frame.
//
// For a style we resolved, keep it around so that we can re-use it next time
// this object is queried, but not if it-s a re-resolved style because we were
// inside a pseudo-element.
if (!mResolvedComputedStyle || mOuterFrame) {
ClearComputedStyle();
}
mOuterFrame = nullptr;
mInnerFrame = nullptr;
mPresShell = nullptr;
}
NS_IMETHODIMP
nsComputedDOMStyle::RemoveProperty(const nsAString& aPropertyName,
nsAString& aReturn)
{
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
void
nsComputedDOMStyle::GetPropertyPriority(const nsAString& aPropertyName,
nsAString& aReturn)
{
aReturn.Truncate();
}
NS_IMETHODIMP
nsComputedDOMStyle::SetProperty(const nsAString& aPropertyName,
const nsAString& aValue,
const nsAString& aPriority,
nsIPrincipal* aSubjectPrincipal)
{
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
void
nsComputedDOMStyle::IndexedGetter(uint32_t aIndex,
bool& aFound,
nsAString& aPropName)
{
ComputedStyleMap* 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 (!mComputedStyle) {
aFound = false;
return;
}
uint32_t count =
Servo_GetCustomPropertiesCount(mComputedStyle);
const uint32_t index = aIndex - length;
if (index < count) {
aFound = true;
nsString varName;
Servo_GetCustomPropertyNameAt(mComputedStyle, index, &varName);
aPropName.AssignLiteral("--");
aPropName.Append(varName);
} else {
aFound = false;
}
ClearCurrentStyleSources();
}
// Property getters...
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBinding()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
const nsStyleDisplay* display = StyleDisplay();
if (display->mBinding && display->mBinding->GetURI()) {
val->SetURI(display->mBinding->GetURI());
} else {
val->SetIdent(eCSSKeyword_none);
}
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBottom()
{
return GetOffsetWidthFor(eSideBottom);
}
void
nsComputedDOMStyle::SetToRGBAColor(nsROCSSPrimitiveValue* aValue,
nscolor aColor)
{
nsAutoString string;
const bool hasAlpha = NS_GET_A(aColor) != 255;
if (hasAlpha) {
string.AppendLiteral("rgba(");
} else {
string.AppendLiteral("rgb(");
}
string.AppendInt(NS_GET_R(aColor));
string.AppendLiteral(", ");
string.AppendInt(NS_GET_G(aColor));
string.AppendLiteral(", ");
string.AppendInt(NS_GET_B(aColor));
if (hasAlpha) {
string.AppendLiteral(", ");
float alpha = nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor));
nsStyleUtil::AppendCSSNumber(alpha, string);
}
string.AppendLiteral(")");
aValue->SetString(string);
}
void
nsComputedDOMStyle::SetValueFromComplexColor(nsROCSSPrimitiveValue* aValue,
const StyleComplexColor& aColor)
{
SetToRGBAColor(aValue, aColor.CalcColor(mComputedStyle));
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetColor()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetToRGBAColor(val, StyleColor()->mColor);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetColumnCount()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
const nsStyleColumn* column = StyleColumn();
if (column->mColumnCount == nsStyleColumn::kColumnCountAuto) {
val->SetIdent(eCSSKeyword_auto);
} else {
val->SetNumber(column->mColumnCount);
}
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetColumnWidth()
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetColumnRuleWidth()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetAppUnits(StyleColumn()->GetComputedColumnRuleWidth());
return val.forget();
}
/* Convert the stored representation into a list of two values and then hand
* it back.
*/
already_AddRefed<CSSValue>
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<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
/* Now, get the values. */
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsROCSSPrimitiveValue> width = new nsROCSSPrimitiveValue;
SetValueToCoord(width, display->mTransformOrigin[0], false,
&nsComputedDOMStyle::GetFrameBoundsWidthForTransform);
valueList->AppendCSSValue(width.forget());
RefPtr<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> 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<CSSValue>
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<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
/* Now, get the values. */
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsROCSSPrimitiveValue> width = new nsROCSSPrimitiveValue;
SetValueToCoord(width, display->mPerspectiveOrigin[0], false,
&nsComputedDOMStyle::GetFrameBoundsWidthForTransform);
valueList->AppendCSSValue(width.forget());
RefPtr<nsROCSSPrimitiveValue> height = new nsROCSSPrimitiveValue;
SetValueToCoord(height, display->mPerspectiveOrigin[1], false,
&nsComputedDOMStyle::GetFrameBoundsHeightForTransform);
valueList->AppendCSSValue(height.forget());
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetPerspective()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StyleDisplay()->mChildPerspective, false);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTransform()
{
const nsStyleDisplay* display = StyleDisplay();
return GetTransformValue(display->mSpecifiedTransform);
}
/* static */ already_AddRefed<nsROCSSPrimitiveValue>
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<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetString(resultString);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOsxFontSmoothing()
{
if (nsContentUtils::ShouldResistFingerprinting(
mPresShell->GetPresContext()->GetDocShell()))
return nullptr;
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleFont()->mFont.smoothing,
nsCSSProps::kFontSmoothingKTable));
return val.forget();
}
static void
SetValueToCalc(const nsStyleCoord::CalcValue* aCalc,
nsROCSSPrimitiveValue* aValue)
{
RefPtr<nsROCSSPrimitiveValue> 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
}
already_AddRefed<CSSValue>
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<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
for (uint32_t i = 0, i_end = aLayers.mPositionXCount; i < i_end; ++i) {
RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
SetValueToPosition(aLayers.mLayers[i].mPosition, itemList);
valueList->AppendCSSValue(itemList.forget());
}
return valueList.forget();
}
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<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
SetValueToPositionCoord(aPosition.mXPosition, valX);
aValueList->AppendCSSValue(valX.forget());
RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
SetValueToPositionCoord(aPosition.mYPosition, valY);
aValueList->AppendCSSValue(valY.forget());
}
void
nsComputedDOMStyle::SetValueToURLValue(const css::URLValue* aURL,
nsROCSSPrimitiveValue* aValue)
{
if (!aURL) {
aValue->SetIdent(eCSSKeyword_none);
return;
}
// If we have a usable nsIURI in the URLValue, and the url() wasn't
// a fragment-only URL, serialize the nsIURI.
if (!aURL->IsLocalRef()) {
if (nsIURI* uri = aURL->GetURI()) {
aValue->SetURI(uri);
return;
}
}
// Otherwise, serialize the specified URL value.
nsAutoString source;
aURL->GetSourceString(source);
nsAutoString url;
url.AppendLiteral(u"url(");
nsStyleUtil::AppendEscapedCSSString(source, url, '"');
url.Append(')');
aValue->SetString(url);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetGridTemplateAreas()
{
const css::GridTemplateAreasValue* areas =
StylePosition()->mGridTemplateAreas;
if (!areas) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(eCSSKeyword_none);
return val.forget();
}
MOZ_ASSERT(!areas->mTemplates.IsEmpty(),
"Unexpected empty array in GridTemplateAreasValue");
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
for (uint32_t i = 0; i < areas->mTemplates.Length(); i++) {
nsAutoString str;
nsStyleUtil::AppendEscapedCSSString(areas->mTemplates[i], str);
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetString(str);
valueList->AppendCSSValue(val.forget());
}
return valueList.forget();
}
void
nsComputedDOMStyle::AppendGridLineNames(nsString& aResult,
const nsTArray<nsString>& 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<nsString>& aLineNames,
bool aSuppressEmptyList)
{
if (aLineNames.IsEmpty() && aSuppressEmptyList) {
return;
}
RefPtr<nsROCSSPrimitiveValue> 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<nsString>& aLineNames1,
const nsTArray<nsString>& aLineNames2)
{
if (aLineNames1.IsEmpty() && aLineNames2.IsEmpty()) {
return;
}
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::GetGridTrackSize(const nsStyleCoord& aMinValue,
const nsStyleCoord& aMaxValue)
{
if (aMinValue.GetUnit() == eStyleUnit_None) {
// A fit-content() function.
RefPtr<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, aMinValue, true,
nullptr, nsCSSProps::kGridTrackBreadthKTable);
return val.forget();
}
// minmax(auto, <flex>) is equivalent to (and is our internal representation
// of) <flex>, and both compute to <flex>
if (aMinValue.GetUnit() == eStyleUnit_Auto &&
aMaxValue.GetUnit() == eStyleUnit_FlexFraction) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, aMaxValue, true);
return val.forget();
}
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
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<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
RefPtr<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> start = new nsROCSSPrimitiveValue;
start->SetString(NS_LITERAL_STRING("repeat(auto-fill,"));
valueList->AppendCSSValue(start.forget());
AppendGridLineNames(valueList, aTrackList.mRepeatAutoLineNameListBefore,
/*aSuppressEmptyList*/ false);
RefPtr<nsROCSSPrimitiveValue> 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<bool> isAutoFill =
aTrackList.HasRepeatAuto() && aTrackList.mIsAutoFill;
DebugOnly<bool> isAutoFit =
aTrackList.HasRepeatAuto() && !aTrackList.mIsAutoFill;
DebugOnly<uint32_t> 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 <track-list> without repeats is represented as "none" in syntax.
if (numSizes == 0 && !aTrackList.HasRepeatAuto()) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(eCSSKeyword_none);
return val.forget();
}
RefPtr<nsDOMCSSValueList> 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(<positive-integer>, 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<nscoord>& 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<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> 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<nsString>& 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<nsString>& 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<nsString>& lineNames = aTrackList.mLineNameLists[j];
AppendGridLineNames(valueList, lineNames);
}
} else {
const nsTArray<nsString>& lineNames = aTrackList.mLineNameLists[i];
AppendGridLineNames(valueList, lineNames);
}
if (uint32_t(i) == numExplicitTracks) {
break;
}
RefPtr<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> 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<nsString>& lineNames = aTrackList.mLineNameLists[i];
if (!lineNames.IsEmpty()) {
AppendGridLineNames(valueList, lineNames);
}
if (i == numSizes) {
break;
}
if (MOZ_UNLIKELY(aTrackList.IsRepeatAutoIndex(i))) {
RefPtr<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetGridAutoFlow()
{
nsAutoString str;
nsStyleUtil::AppendBitmaskCSSValue(nsCSSProps::kGridAutoFlowKTable,
StylePosition()->mGridAutoFlow,
NS_STYLE_GRID_AUTO_FLOW_ROW,
NS_STYLE_GRID_AUTO_FLOW_DENSE,
str);
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetString(str);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetGridAutoColumns()
{
return GetGridTrackSize(StylePosition()->mGridAutoColumnsMin,
StylePosition()->mGridAutoColumnsMax);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetGridAutoRows()
{
return GetGridTrackSize(StylePosition()->mGridAutoRowsMin,
StylePosition()->mGridAutoRowsMax);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetGridTemplateColumns()
{
const ComputedGridTrackInfo* info = nullptr;
nsGridContainerFrame* gridFrame =
nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
if (gridFrame) {
info = gridFrame->GetComputedTemplateColumns();
}
return GetGridTemplateColumnsRows(
StylePosition()->GridTemplateColumns(), info);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetGridTemplateRows()
{
const ComputedGridTrackInfo* info = nullptr;
nsGridContainerFrame* gridFrame =
nsGridContainerFrame::GetGridFrameWithComputedInfo(mInnerFrame);
if (gridFrame) {
info = gridFrame->GetComputedTemplateRows();
}
return GetGridTemplateColumnsRows(
StylePosition()->GridTemplateRows(), info);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetGridLine(const nsStyleGridLine& aGridLine)
{
if (aGridLine.IsAuto()) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(eCSSKeyword_auto);
return val.forget();
}
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
if (aGridLine.mHasSpan) {
RefPtr<nsROCSSPrimitiveValue> span = new nsROCSSPrimitiveValue;
span->SetIdent(eCSSKeyword_span);
valueList->AppendCSSValue(span.forget());
}
if (aGridLine.mInteger != 0) {
RefPtr<nsROCSSPrimitiveValue> integer = new nsROCSSPrimitiveValue;
integer->SetNumber(aGridLine.mInteger);
valueList->AppendCSSValue(integer.forget());
}
if (!aGridLine.mLineName.IsEmpty()) {
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetGridColumnStart()
{
return GetGridLine(StylePosition()->mGridColumnStart);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetGridColumnEnd()
{
return GetGridLine(StylePosition()->mGridColumnEnd);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetGridRowStart()
{
return GetGridLine(StylePosition()->mGridRowStart);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetGridRowEnd()
{
return GetGridLine(StylePosition()->mGridRowEnd);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetColumnGap()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
const auto& columnGap = StylePosition()->mColumnGap;
if (columnGap.GetUnit() == eStyleUnit_Normal) {
val->SetIdent(eCSSKeyword_normal);
} else {
SetValueToCoord(val, columnGap, true);
}
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetRowGap()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
const auto& rowGap = StylePosition()->mRowGap;
if (rowGap.GetUnit() == eStyleUnit_Normal) {
val->SetIdent(eCSSKeyword_normal);
} else {
SetValueToCoord(val, rowGap, true);
}
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetPaddingTop()
{
return GetPaddingWidthFor(eSideTop);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetPaddingBottom()
{
return GetPaddingWidthFor(eSideBottom);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetPaddingLeft()
{
return GetPaddingWidthFor(eSideLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetPaddingRight()
{
return GetPaddingWidthFor(eSideRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderSpacing()
{
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
RefPtr<nsROCSSPrimitiveValue> xSpacing = new nsROCSSPrimitiveValue;
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetBorderTopStyle()
{
return GetBorderStyleFor(eSideTop);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderBottomStyle()
{
return GetBorderStyleFor(eSideBottom);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderLeftStyle()
{
return GetBorderStyleFor(eSideLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderRightStyle()
{
return GetBorderStyleFor(eSideRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderBottomLeftRadius()
{
return GetEllipseRadii(StyleBorder()->mBorderRadius,
eCornerBottomLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderBottomRightRadius()
{
return GetEllipseRadii(StyleBorder()->mBorderRadius,
eCornerBottomRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderTopLeftRadius()
{
return GetEllipseRadii(StyleBorder()->mBorderRadius,
eCornerTopLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderTopRightRadius()
{
return GetEllipseRadii(StyleBorder()->mBorderRadius,
eCornerTopRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderTopWidth()
{
return GetBorderWidthFor(eSideTop);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderBottomWidth()
{
return GetBorderWidthFor(eSideBottom);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderLeftWidth()
{
return GetBorderWidthFor(eSideLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderRightWidth()
{
return GetBorderWidthFor(eSideRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMarginTopWidth()
{
return GetMarginWidthFor(eSideTop);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMarginBottomWidth()
{
return GetMarginWidthFor(eSideBottom);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMarginLeftWidth()
{
return GetMarginWidthFor(eSideLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMarginRightWidth()
{
return GetMarginWidthFor(eSideRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOverscrollBehaviorX()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOverscrollBehaviorX,
nsCSSProps::kOverscrollBehaviorKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOverscrollBehaviorY()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOverscrollBehaviorY,
nsCSSProps::kOverscrollBehaviorKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetScrollSnapTypeX()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mScrollSnapTypeX,
nsCSSProps::kScrollSnapTypeKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetScrollSnapTypeY()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mScrollSnapTypeY,
nsCSSProps::kScrollSnapTypeKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetScrollSnapPoints(const nsStyleCoord& aCoord)
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
if (aCoord.GetUnit() == eStyleUnit_None) {
val->SetIdent(eCSSKeyword_none);
} else {
nsAutoString argumentString;
SetCssTextToCoord(argumentString, aCoord, true);
nsAutoString tmp;
tmp.AppendLiteral("repeat(");
tmp.Append(argumentString);
tmp.Append(')');
val->SetString(tmp);
}
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetScrollSnapPointsX()
{
return GetScrollSnapPoints(StyleDisplay()->mScrollSnapPointsX);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetScrollSnapPointsY()
{
return GetScrollSnapPoints(StyleDisplay()->mScrollSnapPointsY);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetScrollbarColor()
{
const nsStyleUI* ui = StyleUI();
MOZ_ASSERT(ui->mScrollbarFaceColor.IsAuto() ==
ui->mScrollbarTrackColor.IsAuto(),
"Whether the two colors are auto should be identical");
if (ui->mScrollbarFaceColor.IsAuto()) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(eCSSKeyword_auto);
return val.forget();
}
RefPtr<nsDOMCSSValueList> list = GetROCSSValueList(false);
auto put = [this, &list](const StyleComplexColor& color) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueFromComplexColor(val, color);
list->AppendCSSValue(val.forget());
};
put(ui->mScrollbarFaceColor);
put(ui->mScrollbarTrackColor);
return list.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOutlineWidth()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
const nsStyleOutline* outline = StyleOutline();
nscoord width;
if (outline->mOutlineStyle == StyleBorderStyle::None) {
NS_ASSERTION(outline->GetOutlineWidth() == 0, "unexpected width");
width = 0;
} else {
width = outline->GetOutlineWidth();
}
val->SetAppUnits(width);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOutlineStyle()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleOutline()->mOutlineStyle,
nsCSSProps::kOutlineStyleKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOutlineRadiusBottomLeft()
{
return GetEllipseRadii(StyleOutline()->mOutlineRadius,
eCornerBottomLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOutlineRadiusBottomRight()
{
return GetEllipseRadii(StyleOutline()->mOutlineRadius,
eCornerBottomRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOutlineRadiusTopLeft()
{
return GetEllipseRadii(StyleOutline()->mOutlineRadius,
eCornerTopLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOutlineRadiusTopRight()
{
return GetEllipseRadii(StyleOutline()->mOutlineRadius,
eCornerTopRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetEllipseRadii(const nsStyleCorners& aRadius,
Corner aFullCorner)
{
nsStyleCoord radiusX = aRadius.Get(FullToHalfCorner(aFullCorner, false));
nsStyleCoord radiusY = aRadius.Get(FullToHalfCorner(aFullCorner, true));
// for compatibility, return a single value if X and Y are equal
if (radiusX == radiusY) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, radiusX, true);
return val.forget();
}
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
RefPtr<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
SetValueToCoord(valX, radiusX, true);
SetValueToCoord(valY, radiusY, true);
valueList->AppendCSSValue(valX.forget());
valueList->AppendCSSValue(valY.forget());
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetCSSShadowArray(nsCSSShadowArray* aArray,
bool aIsBoxShadow)
{
if (!aArray) {
RefPtr<nsROCSSPrimitiveValue> 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<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
for (nsCSSShadowItem *item = aArray->ShadowAt(0),
*item_end = item + aArray->Length();
item < item_end; ++item) {
RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
// Color is either the specified shadow color or the foreground color
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueFromComplexColor(val, item->mColor);
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<CSSValue>
nsComputedDOMStyle::DoGetBoxShadow()
{
return GetCSSShadowArray(StyleEffects()->mBoxShadow, true);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetZIndex()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StylePosition()->mZIndex, false);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetInitialLetter()
{
const nsStyleTextReset* textReset = StyleTextReset();
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
if (textReset->mInitialLetterSink == 0) {
val->SetIdent(eCSSKeyword_normal);
return val.forget();
} else {
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
val->SetNumber(textReset->mInitialLetterSize);
valueList->AppendCSSValue(val.forget());
RefPtr<nsROCSSPrimitiveValue> second = new nsROCSSPrimitiveValue;
second->SetNumber(textReset->mInitialLetterSink);
valueList->AppendCSSValue(second.forget());
return valueList.forget();
}
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetLineHeight()
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetVerticalAlign()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StyleDisplay()->mVerticalAlign, false,
nullptr, nsCSSProps::kVerticalAlignKTable);
return val.forget();
}
already_AddRefed<CSSValue>
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<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
valueList->AppendCSSValue(DoGetTextDecorationLine());
if (!isInitialStyle) {
valueList->AppendCSSValue(DoGetTextDecorationStyle());
}
if (!color.IsCurrentColor()) {
valueList->AppendCSSValue(DoGetTextDecorationColor());
}
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTextDecorationColor()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueFromComplexColor(val, StyleTextReset()->mTextDecorationColor);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTextDecorationLine()
{
RefPtr<nsROCSSPrimitiveValue> 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 OVERRIDE_ALL bits -- we don't want these to appear in
// the computed style.
intValue &= ~NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL;
nsStyleUtil::AppendBitmaskCSSValue(nsCSSProps::kTextDecorationLineKTable,
intValue,
NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
NS_STYLE_TEXT_DECORATION_LINE_BLINK,
decorationLineString);
val->SetString(decorationLineString);
}
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTextDecorationStyle()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleTextReset()->mTextDecorationStyle,
nsCSSProps::kTextDecorationStyleKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTextEmphasisPosition()
{
auto position = StyleText()->mTextEmphasisPosition;
MOZ_ASSERT(!(position & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER) !=
!(position & NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER));
RefPtr<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> second = new nsROCSSPrimitiveValue;
second->SetIdent((position & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT) ?
eCSSKeyword_left : eCSSKeyword_right);
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
valueList->AppendCSSValue(first.forget());
valueList->AppendCSSValue(second.forget());
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTextEmphasisStyle()
{
auto style = StyleText()->mTextEmphasisStyle;
if (style == NS_STYLE_TEXT_EMPHASIS_STYLE_NONE) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(eCSSKeyword_none);
return val.forget();
}
if (style == NS_STYLE_TEXT_EMPHASIS_STYLE_STRING) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
nsAutoString tmp;
nsStyleUtil::AppendEscapedCSSString(
StyleText()->mTextEmphasisStyleString, tmp);
val->SetString(tmp);
return val.forget();
}
RefPtr<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> shapeVal = new nsROCSSPrimitiveValue;
shapeVal->SetIdent(nsCSSProps::ValueToKeywordEnum(
style & NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK,
nsCSSProps::kTextEmphasisStyleShapeKTable));
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
valueList->AppendCSSValue(fillVal.forget());
valueList->AppendCSSValue(shapeVal.forget());
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTextOverflow()
{
const nsStyleTextReset *style = StyleTextReset();
RefPtr<nsROCSSPrimitiveValue> 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<nsROCSSPrimitiveValue> 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<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
valueList->AppendCSSValue(first.forget());
valueList->AppendCSSValue(second.forget());
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTextShadow()
{
return GetCSSShadowArray(StyleText()->mTextShadow, false);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTabSize()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StyleText()->mTabSize, true);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetLetterSpacing()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StyleText()->mLetterSpacing, false);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetWordSpacing()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StyleText()->mWordSpacing, false);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetWebkitTextStrokeWidth()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetAppUnits(StyleText()->mWebkitTextStrokeWidth);
return val.forget();
}
static_assert(NS_STYLE_UNICODE_BIDI_NORMAL == 0,
"unicode-bidi style constants not as expected");
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetCaretColor()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueFromComplexColor(val, StyleUI()->mCaretColor);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetCursor()
{
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
const nsStyleUI *ui = StyleUI();
for (const nsCursorImage& item : ui->mCursorImages) {
RefPtr<nsDOMCSSValueList> itemList = GetROCSSValueList(false);
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToURLValue(item.mImage->GetImageValue(), val);
itemList->AppendCSSValue(val.forget());
if (item.mHaveHotspot) {
RefPtr<nsROCSSPrimitiveValue> valX = new nsROCSSPrimitiveValue;
RefPtr<nsROCSSPrimitiveValue> valY = new nsROCSSPrimitiveValue;
valX->SetNumber(item.mHotspotX);
valY->SetNumber(item.mHotspotY);
itemList->AppendCSSValue(valX.forget());
itemList->AppendCSSValue(valY.forget());
}
valueList->AppendCSSValue(itemList.forget());
}
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(nsCSSProps::ValueToKeywordEnum(ui->mCursor,
nsCSSProps::kCursorKTable));
valueList->AppendCSSValue(val.forget());
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBoxFlex()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetNumber(StyleXUL()->mBoxFlex);
return val.forget();
}
/* Border image properties */
void
nsComputedDOMStyle::AppendFourSideCoordValues(nsDOMCSSValueList* aList,
const nsStyleSides& aValues)
{
const nsStyleCoord& top = aValues.Get(eSideTop);
const nsStyleCoord& right = aValues.Get(eSideRight);
const nsStyleCoord& bottom = aValues.Get(eSideBottom);
const nsStyleCoord& left = aValues.Get(eSideLeft);
auto appendValue = [this, aList](const nsStyleCoord& value) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, value, true);
aList->AppendCSSValue(val.forget());
};
appendValue(top);
if (top != right || top != bottom || top != left) {
appendValue(right);
if (top != bottom || right != left) {
appendValue(bottom);
if (right != left) {
appendValue(left);
}
}
}
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderImageSlice()
{
const nsStyleBorder* border = StyleBorder();
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
AppendFourSideCoordValues(valueList, border->mBorderImageSlice);
// Fill keyword.
if (NS_STYLE_BORDER_IMAGE_SLICE_FILL == border->mBorderImageFill) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(eCSSKeyword_fill);
valueList->AppendCSSValue(val.forget());
}
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderImageWidth()
{
const nsStyleBorder* border = StyleBorder();
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
AppendFourSideCoordValues(valueList, border->mBorderImageWidth);
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetBorderImageOutset()
{
const nsStyleBorder* border = StyleBorder();
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
AppendFourSideCoordValues(valueList, border->mBorderImageOutset);
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetFlexBasis()
{
RefPtr<nsROCSSPrimitiveValue> 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::kFlexBasisKTable);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetFlexGrow()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetNumber(StylePosition()->mFlexGrow);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetFlexShrink()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetNumber(StylePosition()->mFlexShrink);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetAlignContent()
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetAlignItems()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
nsAutoString str;
auto align = StylePosition()->mAlignItems;
nsCSSValue::AppendAlignJustifyValueToString(align, str);
val->SetString(str);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetAlignSelf()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
nsAutoString str;
auto align = StylePosition()->mAlignSelf;
nsCSSValue::AppendAlignJustifyValueToString(align, str);
val->SetString(str);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetJustifyContent()
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetJustifyItems()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
nsAutoString str;
auto justify = StylePosition()->mJustifyItems;
nsCSSValue::AppendAlignJustifyValueToString(justify, str);
val->SetString(str);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetJustifySelf()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
nsAutoString str;
auto justify = StylePosition()->mJustifySelf;
nsCSSValue::AppendAlignJustifyValueToString(justify, str);
val->SetString(str);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetForceBrokenImageIcon()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetNumber(StyleUIReset()->mForceBrokenImageIcon);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetDisplay()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mDisplay,
nsCSSProps::kDisplayKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetContain()
{
RefPtr<nsROCSSPrimitiveValue> 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: size layout style paint");
val->SetIdent(eCSSKeyword_strict);
} else if (mask & NS_STYLE_CONTAIN_CONTENT) {
NS_ASSERTION(mask == (NS_STYLE_CONTAIN_CONTENT | NS_STYLE_CONTAIN_CONTENT_BITS),
"contain: content should imply contain: layout style paint");
val->SetIdent(eCSSKeyword_content);
} else {
nsAutoString valueStr;
nsStyleUtil::AppendBitmaskCSSValue(nsCSSProps::kContainKTable,
mask,
NS_STYLE_CONTAIN_SIZE, NS_STYLE_CONTAIN_PAINT,
valueStr);
val->SetString(valueStr);
}
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetWillChange()
{
const nsTArray<RefPtr<nsAtom>>& willChange = StyleDisplay()->mWillChange;
if (willChange.IsEmpty()) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(eCSSKeyword_auto);
return val.forget();
}
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
for (const nsAtom* ident : willChange) {
RefPtr<nsROCSSPrimitiveValue> property = new nsROCSSPrimitiveValue;
property->SetString(nsDependentAtomString(ident));
valueList->AppendCSSValue(property.forget());
}
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOverflowY()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOverflowY,
nsCSSProps::kOverflowSubKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOverflowClipBoxBlock()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOverflowClipBoxBlock,
nsCSSProps::kOverflowClipBoxKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetOverflowClipBoxInline()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(StyleDisplay()->mOverflowClipBoxInline,
nsCSSProps::kOverflowClipBoxKTable));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTouchAction()
{
RefPtr<nsROCSSPrimitiveValue> 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(nsCSSProps::kTouchActionKTable,
intValue,
NS_STYLE_TOUCH_ACTION_NONE,
NS_STYLE_TOUCH_ACTION_MANIPULATION,
valueStr);
val->SetString(valueStr);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetHeight()
{
RefPtr<nsROCSSPrimitiveValue> 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->IsSVGOuterSVGFrame()) {
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<CSSValue>
nsComputedDOMStyle::DoGetWidth()
{
RefPtr<nsROCSSPrimitiveValue> 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->IsSVGOuterSVGFrame()) {
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<CSSValue>
nsComputedDOMStyle::DoGetMaxHeight()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StylePosition()->mMaxHeight, true,
nullptr, nsCSSProps::kWidthKTable);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMaxWidth()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StylePosition()->mMaxWidth, true,
nullptr, nsCSSProps::kWidthKTable);
return val.forget();
}
/**
* This function indicates whether we should return "auto" as the
* getComputedStyle() result for the (default) "min-width: auto" and
* "min-height: auto" CSS values.
*
* As of this writing, the CSS Sizing draft spec says this "auto" value
* *always* computes to itself. However, for now, we only make it compute to
* itself for grid and flex items (the containers where "auto" has special
* significance), because those are the only areas where the CSSWG has actually
* resolved on this "computes-to-itself" behavior. For elements in other sorts
* of containers, this function returns false, which will make us resolve
* "auto" to 0.
*/
bool
nsComputedDOMStyle::ShouldHonorMinSizeAutoInAxis(PhysicalAxis aAxis)
{
return mOuterFrame && mOuterFrame->IsFlexOrGridItem();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMinHeight()
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetMinWidth()
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetLeft()
{
return GetOffsetWidthFor(eSideLeft);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetRight()
{
return GetOffsetWidthFor(eSideRight);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTop()
{
return GetOffsetWidthFor(eSideTop);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetOffsetWidthFor(mozilla::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<CSSValue>
nsComputedDOMStyle::GetAbsoluteOffset(mozilla::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->IsViewportFrame()) {
// 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();
}
} else if (container->IsGridContainerFrame() &&
(mOuterFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
containerRect = nsGridContainerFrame::GridItemCB(mOuterFrame);
rect.MoveBy(-containerRect.x, -containerRect.y);
}
nscoord offset = 0;
switch (aSide) {
case eSideTop:
offset = rect.y - margin.top - border.top - scrollbarSizes.top;
break;
case eSideRight:
offset = containerRect.width - rect.width -
rect.x - margin.right - border.right - scrollbarSizes.right;
break;
case eSideBottom:
offset = containerRect.height - rect.height -
rect.y - margin.bottom - border.bottom - scrollbarSizes.bottom;
break;
case eSideLeft:
offset = rect.x - margin.left - border.left - scrollbarSizes.left;
break;
default:
NS_ERROR("Invalid side");
break;
}
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetAppUnits(offset);
return val.forget();
}
static_assert(eSideTop == 0 && eSideRight == 1 &&
eSideBottom == 2 && eSideLeft == 3,
"box side constants not as expected for NS_OPPOSITE_SIDE");
#define NS_OPPOSITE_SIDE(s_) mozilla::Side(((s_) + 2) & 3)
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetRelativeOffset(mozilla::Side aSide)
{
RefPtr<nsROCSSPrimitiveValue> 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 == eSideLeft || aSide == eSideRight) {
baseGetter = &nsComputedDOMStyle::GetCBContentWidth;
} else {
baseGetter = &nsComputedDOMStyle::GetCBContentHeight;
}
val->SetAppUnits(sign * StyleCoordToNSCoord(coord, baseGetter, 0, false));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetStickyOffset(mozilla::Side aSide)
{
RefPtr<nsROCSSPrimitiveValue> 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 == eSideLeft || aSide == eSideRight) {
baseGetter = &nsComputedDOMStyle::GetScrollFrameContentWidth;
} else {
baseGetter = &nsComputedDOMStyle::GetScrollFrameContentHeight;
}
val->SetAppUnits(StyleCoordToNSCoord(coord, baseGetter, 0, false));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetStaticOffset(mozilla::Side aSide)
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StylePosition()->mOffset.Get(aSide), false);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetPaddingWidthFor(mozilla::Side aSide)
{
RefPtr<nsROCSSPrimitiveValue> 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);
}
}
nsPresContext* presContext = mPresShell->GetPresContext();
// lie about font size inflation since we lie about font size (since
// the inflation only applies to text)
aCoord = ReflowInput::CalcLineHeight(mElement,
mComputedStyle,
presContext,
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 /= presContext->EffectiveTextZoom();
}
if (font->mFont.size != font->mSize) {
fCoord = fCoord * (float(font->mSize) / float(font->mFont.size));
}
aCoord = NSToCoordRound(fCoord);
return true;
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetBorderWidthFor(mozilla::Side aSide)
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::GetBorderColorFor(mozilla::Side aSide)
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueFromComplexColor(val, StyleBorder()->BorderColorFor(aSide));
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetMarginWidthFor(mozilla::Side aSide)
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::GetBorderStyleFor(mozilla::Side aSide)
{
RefPtr<nsROCSSPrimitiveValue> 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)
{
MOZ_ASSERT(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 = aCoord.ComputeCoordPercentCalc(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 = aCoord.ComputeCoordPercentCalc(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_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)
{
MOZ_ASSERT(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 = aCoord.ComputeCoordPercentCalc(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<CSSValue>
nsComputedDOMStyle::GetFallbackValue(const nsStyleSVGPaint* aPaint)
{
RefPtr<nsROCSSPrimitiveValue> fallback = new nsROCSSPrimitiveValue;
if (aPaint->GetFallbackType() == eStyleSVGFallbackType_Color) {
SetToRGBAColor(fallback, aPaint->GetFallbackColor(mComputedStyle));
} else {
fallback->SetIdent(eCSSKeyword_none);
}
return fallback.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::GetSVGPaintFor(bool aFill)
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
const nsStyleSVG* svg = StyleSVG();
const nsStyleSVGPaint* paint = aFill ? &svg->mFill : &svg->mStroke;
nsAutoString paintString;
switch (paint->Type()) {
case eStyleSVGPaintType_None:
val->SetIdent(eCSSKeyword_none);
break;
case eStyleSVGPaintType_Color:
SetToRGBAColor(val, paint->GetColor(mComputedStyle));
break;
case eStyleSVGPaintType_Server: {
SetValueToURLValue(paint->GetPaintServer(), val);
if (paint->GetFallbackType() != eStyleSVGFallbackType_NotSet) {
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
RefPtr<CSSValue> fallback = GetFallbackValue(paint);
valueList->AppendCSSValue(val.forget());
valueList->AppendCSSValue(fallback.forget());
return valueList.forget();
}
break;
}
case eStyleSVGPaintType_ContextFill:
case eStyleSVGPaintType_ContextStroke: {
val->SetIdent(paint->Type() == eStyleSVGPaintType_ContextFill ?
eCSSKeyword_context_fill : eCSSKeyword_context_stroke);
if (paint->GetFallbackType() != eStyleSVGFallbackType_NotSet) {
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
RefPtr<CSSValue> fallback = GetFallbackValue(paint);
valueList->AppendCSSValue(val.forget());
valueList->AppendCSSValue(fallback.forget());
return valueList.forget();
}
break;
}
}
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<CSSValue>
nsComputedDOMStyle::GetTransformValue(nsCSSValueSharedList* aSpecifiedTransform)
{
/* If there are no transforms, then we should construct a single-element
* entry and hand it back.
*/
if (!aSpecifiedTransform) {
RefPtr<nsROCSSPrimitiveValue> 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));
gfx::Matrix4x4 matrix =
nsStyleTransformMatrix::ReadTransforms(aSpecifiedTransform->mHead,
refBox,
float(mozilla::AppUnitsPerCSSPixel()));
return MatrixToCSSValue(matrix);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetFill()
{
return GetSVGPaintFor(true);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetStroke()
{
return GetSVGPaintFor(false);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMarkerEnd()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToURLValue(StyleSVG()->mMarkerEnd, val);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMarkerMid()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToURLValue(StyleSVG()->mMarkerMid, val);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetMarkerStart()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToURLValue(StyleSVG()->mMarkerStart, val);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetStrokeDasharray()
{
const nsStyleSVG* svg = StyleSVG();
if (svg->mStrokeDasharray.IsEmpty()) {
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(eCSSKeyword_none);
return val.forget();
}
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(true);
for (uint32_t i = 0; i < svg->mStrokeDasharray.Length(); i++) {
RefPtr<nsROCSSPrimitiveValue> dash = new nsROCSSPrimitiveValue;
SetValueToCoord(dash, svg->mStrokeDasharray[i], true);
valueList->AppendCSSValue(dash.forget());
}
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetStrokeDashoffset()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StyleSVG()->mStrokeDashoffset, false);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetStrokeWidth()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToCoord(val, StyleSVG()->mStrokeWidth, true);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetFillOpacity()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetNumber(StyleSVG()->mFillOpacity);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetStrokeMiterlimit()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetNumber(StyleSVG()->mStrokeMiterlimit);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetStrokeOpacity()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetNumber(StyleSVG()->mStrokeOpacity);
return val.forget();
}
void
nsComputedDOMStyle::BoxValuesToString(nsAString& aString,
const nsTArray<nsStyleCoord>& aBoxValues,
bool aClampNegativeCalc)
{
MOZ_ASSERT(aBoxValues.Length() == 4, "wrong number of box values");
nsAutoString value1, value2, value3, value4;
SetCssTextToCoord(value1, aBoxValues[0], aClampNegativeCalc);
SetCssTextToCoord(value2, aBoxValues[1], aClampNegativeCalc);
SetCssTextToCoord(value3, aBoxValues[2], aClampNegativeCalc);
SetCssTextToCoord(value4, aBoxValues[3], aClampNegativeCalc);
// 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<nsStyleCoord> horizontal, vertical;
nsAutoString horizontalString, verticalString;
NS_FOR_CSS_FULL_CORNERS(corner) {
horizontal.AppendElement(
aCorners.Get(FullToHalfCorner(corner, false)));
vertical.AppendElement(
aCorners.Get(FullToHalfCorner(corner, true)));
}
BoxValuesToString(horizontalString, horizontal, true);
BoxValuesToString(verticalString, vertical, true);
aCssText.Append(horizontalString);
if (horizontalString == verticalString) {
return;
}
aCssText.AppendLiteral(" / ");
aCssText.Append(verticalString);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::CreatePrimitiveValueForBasicShape(
const UniquePtr<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],
false);
shapeFunctionString.Append(coordString);
shapeFunctionString.Append(' ');
SetCssTextToCoord(coordString,
aStyleBasicShape->Coordinates()[i + 1],
false);
shapeFunctionString.Append(coordString);
}
break;
}
case StyleBasicShapeType::Circle:
case StyleBasicShapeType::Ellipse: {
const nsTArray<nsStyleCoord>& 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<nsROCSSPrimitiveValue> 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<nsDOMCSSValueList> position = GetROCSSValueList(false);
nsAutoString positionString;
SetValueToPosition(aStyleBasicShape->GetPosition(), position);
position->GetCssText(positionString);
shapeFunctionString.Append(positionString);
break;
}
case StyleBasicShapeType::Inset: {
BoxValuesToString(shapeFunctionString, aStyleBasicShape->Coordinates(), false);
if (aStyleBasicShape->HasRadius()) {
shapeFunctionString.AppendLiteral(" round ");
nsAutoString radiiString;
BasicShapeRadiiToString(radiiString, aStyleBasicShape->GetRadius());
shapeFunctionString.Append(radiiString);
}
break;
}
default:
MOZ_ASSERT_UNREACHABLE("unexpected type");
}
shapeFunctionString.Append(')');
RefPtr<nsROCSSPrimitiveValue> functionValue = new nsROCSSPrimitiveValue;
functionValue->SetString(shapeFunctionString);
return functionValue.forget();
}
template<typename ReferenceBox>
already_AddRefed<CSSValue>
nsComputedDOMStyle::CreatePrimitiveValueForShapeSource(
const UniquePtr<StyleBasicShape>& aStyleBasicShape,
ReferenceBox aReferenceBox,
const KTableEntry aBoxKeywordTable[])
{
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
if (aStyleBasicShape) {
valueList->AppendCSSValue(
CreatePrimitiveValueForBasicShape(aStyleBasicShape));
}
if (aReferenceBox == ReferenceBox::NoBox) {
return valueList.forget();
}
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
val->SetIdent(nsCSSProps::ValueToKeywordEnum(aReferenceBox, aBoxKeywordTable));
valueList->AppendCSSValue(val.forget());
return valueList.forget();
}
void
nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText,
const nsStyleCoord& aCoord,
bool aClampNegativeCalc)
{
RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
SetValueToCoord(value, aCoord, aClampNegativeCalc);
value->GetCssText(aCssText);
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::CreatePrimitiveValueForStyleFilter(
const nsStyleFilter& aStyleFilter)
{
RefPtr<nsROCSSPrimitiveValue> 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<CSSValue> shadowValue =
GetCSSShadowArray(aStyleFilter.GetDropShadow(), false);
ErrorResult dummy;
shadowValue->GetCssText(argumentString, dummy);
} else {
// Filter function argument.
SetCssTextToCoord(argumentString, aStyleFilter.GetFilterParameter(), true);
}
filterFunctionString.Append(argumentString);
// Filter function closing parenthesis.
filterFunctionString.Append(')');
value->SetString(filterFunctionString);
return value.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetFilter()
{
const nsTArray<nsStyleFilter>& filters = StyleEffects()->mFilters;
if (filters.IsEmpty()) {
RefPtr<nsROCSSPrimitiveValue> value = new nsROCSSPrimitiveValue;
value->SetIdent(eCSSKeyword_none);
return value.forget();
}
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
for(uint32_t i = 0; i < filters.Length(); i++) {
RefPtr<CSSValue> value = CreatePrimitiveValueForStyleFilter(filters[i]);
valueList->AppendCSSValue(value.forget());
}
return valueList.forget();
}
already_AddRefed<CSSValue>
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::BorderBox ||
firstLayer.mOrigin != StyleGeometryBox::BorderBox ||
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 ||
firstLayer.mImage.GetType() == eStyleImageType_URL)) {
return nullptr;
}
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
SetValueToURLValue(firstLayer.mImage.GetURLValue(), val);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetPaintOrder()
{
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
nsAutoString string;
uint8_t paintOrder = StyleSVG()->mPaintOrder;
nsStyleUtil::AppendPaintOrderValue(paintOrder, string);
val->SetString(string);
return val.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetTransitionDelay()
{
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsDOMCSSValueList> 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<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetTransitionDuration()
{
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsDOMCSSValueList> 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<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetTransitionProperty()
{
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsDOMCSSValueList> 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<nsROCSSPrimitiveValue> 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();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetAnimationName()
{
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsDOMCSSValueList> 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<nsROCSSPrimitiveValue> property = new nsROCSSPrimitiveValue;
nsAtom* name = animation->GetName();
if (name == nsGkAtoms::_empty) {
property->SetIdent(eCSSKeyword_none);
} else {
nsDependentAtomString nameStr(name);
nsAutoString escaped;
nsStyleUtil::AppendEscapedCSSIdent(nameStr, escaped);
property->SetString(escaped); // really want SetIdent
}
valueList->AppendCSSValue(property.forget());
} while (++i < display->mAnimationNameCount);
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DoGetAnimationDelay()
{
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsDOMCSSValueList> 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<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetAnimationDuration()
{
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsDOMCSSValueList> 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<nsROCSSPrimitiveValue> 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<CSSValue>
nsComputedDOMStyle::DoGetAnimationIterationCount()
{
const nsStyleDisplay* display = StyleDisplay();
RefPtr<nsDOMCSSValueList> 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<nsROCSSPrimitiveValue> iterationCount = new nsROCSSPrimitiveValue;
float f = animation->GetIterationCount();
if (f == PositiveInfinity<float>()) {
iterationCount->SetIdent(eCSSKeyword_infinite);
} else {
iterationCount->SetNumber(f);
}
valueList->AppendCSSValue(iterationCount.forget());
} while (++i < display->mAnimationIterationCountCount);
return valueList.forget();
}
already_AddRefed<CSSValue>
nsComputedDOMStyle::DummyGetter()
{
MOZ_CRASH("DummyGetter is not supposed to be invoked");
}
static void
MarkComputedStyleMapDirty(const char* aPref, ComputedStyleMap* aData)
{
aData->MarkDirty();
}
void
nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent)
{
NS_ASSERTION(mElement == aContent, "didn't we register mElement?");
NS_ASSERTION(mResolvedComputedStyle,
"should have only registered an observer when "
"mResolvedComputedStyle is true");
ClearComputedStyle();
}
/* static */ ComputedStyleMap*
nsComputedDOMStyle::GetComputedStyleMap()
{
static ComputedStyleMap map{};
return &map;
}
static StaticAutoPtr<nsTArray<const char*>> gCallbackPrefs;
/* 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 ServoCSSPropList.h based on the
// entries iterated in nsComputedDOMStylePropertyList.h.
AutoTArray<const char*, 64> prefs;
for (const auto* p = nsCSSProps::kPropertyPrefTable;
p->mPropID != eCSSProperty_UNKNOWN; p++) {
// Many properties are controlled by the same preference, so de-duplicate
// them before adding observers.
//
// Note: This is done by pointer comparison, which works because the mPref
// members are string literals from the same same translation unit, and are
// therefore de-duplicated by the compiler. On the off chance that we wind
// up with some duplicates with different pointers, though, it's not a bit
// deal.
if (!prefs.ContainsSorted(p->mPref)) {
prefs.InsertElementSorted(p->mPref);
}
}
prefs.AppendElement(nullptr);
MOZ_ASSERT(!gCallbackPrefs);
gCallbackPrefs = new nsTArray<const char*>(std::move(prefs));
Preferences::RegisterCallbacks(MarkComputedStyleMapDirty,
gCallbackPrefs->Elements(),
GetComputedStyleMap());
}
/* static */ void
nsComputedDOMStyle::UnregisterPrefChangeCallbacks()
{
if (!gCallbackPrefs) {
return;
}
Preferences::UnregisterCallbacks(MarkComputedStyleMapDirty,
gCallbackPrefs->Elements(),
GetComputedStyleMap());
gCallbackPrefs = nullptr;
}