зеркало из https://github.com/mozilla/gecko-dev.git
4405 строки
136 KiB
C++
4405 строки
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 ↦
|
|
}
|
|
|
|
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;
|
|
}
|