Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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/. */
/*
* structs that contain the data provided by ComputedStyle, the
* internal API for computed style data for an element
*/
#include "nsStyleStruct.h"
#include "nsStyleStructInlines.h"
#include "nsStyleConsts.h"
#include "nsString.h"
#include "nsPresContext.h"
#include "nsIWidget.h"
#include "nsCRTGlue.h"
#include "nsCSSProps.h"
#include "nsDeviceContext.h"
#include "nsStyleUtil.h"
#include "nsIURIMutator.h"
#include "nsCOMPtr.h"
#include "nsBidiUtils.h"
#include "nsLayoutUtils.h"
#include "imgIRequest.h"
#include "imgIContainer.h"
#include "CounterStyleManager.h"
#include "mozilla/dom/AnimationEffectBinding.h" // for PlaybackDirection
#include "mozilla/dom/BaseKeyframeTypesBinding.h" // for CompositeOperation
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/ImageTracker.h"
#include "mozilla/CORSMode.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/GeckoBindings.h"
#include "mozilla/PreferenceSheet.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/StaticPresData.h"
#include "mozilla/Likely.h"
#include "nsIURI.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include <algorithm>
#include "ImageLoader.h"
#include "mozilla/StaticPrefs_layout.h"
using namespace mozilla;
using namespace mozilla::dom;
static const nscoord kMediumBorderWidth = nsPresContext::CSSPixelsToAppUnits(3);
// We set the size limit of style structs to 504 bytes so that when they
// are allocated by Servo side with Arc, the total size doesn't exceed
// 512 bytes, which minimizes allocator slop.
static constexpr size_t kStyleStructSizeLimit = 504;
template <typename Struct, size_t Actual, size_t Limit>
struct AssertSizeIsLessThan {
static_assert(Actual == sizeof(Struct), "Bogus invocation");
static_assert(Actual <= Limit,
"Style struct became larger than the size limit");
static constexpr bool instantiate = true;
};
#define STYLE_STRUCT(name_) \
static_assert(AssertSizeIsLessThan<nsStyle##name_, sizeof(nsStyle##name_), \
kStyleStructSizeLimit>::instantiate, \
"");
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
bool StyleCssUrlData::operator==(const StyleCssUrlData& aOther) const {
// This very intentionally avoids comparing LoadData and such.
const auto& extra = extra_data.get();
const auto& otherExtra = aOther.extra_data.get();
if (extra.BaseURI() != otherExtra.BaseURI() ||
extra.Principal() != otherExtra.Principal() ||
cors_mode != aOther.cors_mode) {
// NOTE(emilio): This does pointer comparison, but it's what URLValue used
// to do. That's ok though since this is only used for style struct diffing.
return false;
}
return serialization == aOther.serialization;
}
StyleLoadData::~StyleLoadData() { Gecko_LoadData_Drop(this); }
already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(
nsIURI* aBase) const {
nsCOMPtr<nsIURI> result = GetURI();
if (result && IsLocalRef()) {
nsCString ref;
result->GetRef(ref);
nsresult rv = NS_MutateURI(aBase).SetRef(ref).Finalize(result);
if (NS_FAILED(rv)) {
// If setting the ref failed, just return the original URI.
result = aBase;
}
}
return result.forget();
}
already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(
const nsIContent* aContent) const {
return ResolveLocalRef(aContent->GetBaseURI());
}
void StyleComputedUrl::ResolveImage(Document& aDocument,
const StyleComputedUrl* aOldImage) {
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
StyleLoadData& data = LoadData();
MOZ_ASSERT(!(data.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE));
data.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE;
MOZ_ASSERT(NS_IsMainThread());
// TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the
// lack of non-http image caching in imagelib (bug 1406134), which causes
// stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug
// 1440305, but that seems too risky, and a lot of work to do before 60.
//
// Once that's fixed, the "old style" argument to TriggerImageLoads can go
// away, and same for mSharedCount in the image loader and so on.
const bool reuseProxy = nsContentUtils::IsChromeDoc(&aDocument) &&
aOldImage && aOldImage->IsImageResolved() &&
*this == *aOldImage;
RefPtr<imgRequestProxy> request;
if (reuseProxy) {
request = aOldImage->LoadData().resolved_image;
if (request) {
css::ImageLoader::NoteSharedLoad(request);
}
} else {
request = css::ImageLoader::LoadImage(*this, aDocument);
}
if (!request) {
return;
}
data.resolved_image = request.forget().take();
// Boost priority now that we know the image is present in the ComputedStyle
// of some frame.
data.resolved_image->BoostPriority(imgIRequest::CATEGORY_FRAME_STYLE);
}
/**
* Runnable to release the image request's mRequestProxy
* and mImageTracker on the main thread, and to perform
* any necessary unlocking and untracking of the image.
*/
class StyleImageRequestCleanupTask final : public mozilla::Runnable {
public:
explicit StyleImageRequestCleanupTask(StyleLoadData& aData)
: mozilla::Runnable("StyleImageRequestCleanupTask"),
mRequestProxy(dont_AddRef(aData.resolved_image)) {
MOZ_ASSERT(mRequestProxy);
aData.resolved_image = nullptr;
}
NS_IMETHOD Run() final {
MOZ_ASSERT(NS_IsMainThread());
css::ImageLoader::UnloadImage(mRequestProxy);
return NS_OK;
}
protected:
virtual ~StyleImageRequestCleanupTask() {
MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(),
"mRequestProxy destructor need to run on the main thread!");
}
private:
// Since we always dispatch this runnable to the main thread, these will be
// released on the main thread when the runnable itself is released.
RefPtr<imgRequestProxy> mRequestProxy;
};
// This is defined here for parallelism with LoadURI.
void Gecko_LoadData_Drop(StyleLoadData* aData) {
if (aData->resolved_image) {
// We want to dispatch this async to prevent reentrancy issues, as
// imgRequestProxy going away can destroy documents, etc, see bug 1677555.
auto task = MakeRefPtr<StyleImageRequestCleanupTask>(*aData);
SchedulerGroup::Dispatch(task.forget());
}
// URIs are safe to refcount from any thread.
NS_IF_RELEASE(aData->resolved_uri);
}
// --------------------
// nsStyleFont
//
nsStyleFont::nsStyleFont(const nsStyleFont& aSrc)
: mFont(aSrc.mFont),
mSize(aSrc.mSize),
mFontSizeFactor(aSrc.mFontSizeFactor),
mFontSizeOffset(aSrc.mFontSizeOffset),
mFontSizeKeyword(aSrc.mFontSizeKeyword),
mFontPalette(aSrc.mFontPalette),
mMathDepth(aSrc.mMathDepth),
mLineHeight(aSrc.mLineHeight),
mMinFontSizeRatio(aSrc.mMinFontSizeRatio),
mMathVariant(aSrc.mMathVariant),
mMathStyle(aSrc.mMathStyle),
mExplicitLanguage(aSrc.mExplicitLanguage),
mXTextScale(aSrc.mXTextScale),
mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize),
mScriptMinSize(aSrc.mScriptMinSize),
mLanguage(aSrc.mLanguage) {
MOZ_COUNT_CTOR(nsStyleFont);
}
static StyleXTextScale InitialTextScale(const Document& aDoc) {
if (nsContentUtils::IsChromeDoc(&aDoc) ||
nsContentUtils::IsPDFJS(aDoc.NodePrincipal())) {
return StyleXTextScale::ZoomOnly;
}
return StyleXTextScale::All;
}
nsStyleFont::nsStyleFont(const Document& aDocument)
: mFont(*aDocument.GetFontPrefsForLang(nullptr)->GetDefaultFont(
StyleGenericFontFamily::None)),
mSize(ZoomText(aDocument, mFont.size)),
mFontSizeFactor(1.0),
mFontSizeOffset{0},
mFontSizeKeyword(StyleFontSizeKeyword::Medium),
mFontPalette(StyleFontPalette::Normal()),
mMathDepth(0),
mLineHeight(StyleLineHeight::Normal()),
mMathVariant(StyleMathVariant::None),
mMathStyle(StyleMathStyle::Normal),
mXTextScale(InitialTextScale(aDocument)),
mScriptUnconstrainedSize(mSize),
mScriptMinSize(Length::FromPixels(
CSSPixel::FromPoints(kMathMLDefaultScriptMinSizePt))),
mLanguage(aDocument.GetLanguageForStyle()) {
MOZ_COUNT_CTOR(nsStyleFont);
MOZ_ASSERT(NS_IsMainThread());
mFont.family.is_initial = true;
mFont.size = mSize;
if (MinFontSizeEnabled()) {
const Length minimumFontSize =
aDocument.GetFontPrefsForLang(mLanguage)->mMinimumFontSize;
mFont.size = Length::FromPixels(
std::max(mSize.ToCSSPixels(), minimumFontSize.ToCSSPixels()));
}
}
nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aNewData) const {
MOZ_ASSERT(mXTextScale == aNewData.mXTextScale,
"expected -x-text-scale to be the same on both nsStyleFonts");
if (mSize != aNewData.mSize || mLanguage != aNewData.mLanguage ||
mExplicitLanguage != aNewData.mExplicitLanguage ||
mMathVariant != aNewData.mMathVariant ||
mMathStyle != aNewData.mMathStyle ||
mMinFontSizeRatio != aNewData.mMinFontSizeRatio ||
mLineHeight != aNewData.mLineHeight) {
return NS_STYLE_HINT_REFLOW;
}
switch (mFont.CalcDifference(aNewData.mFont)) {
case nsFont::MaxDifference::eLayoutAffecting:
return NS_STYLE_HINT_REFLOW;
case nsFont::MaxDifference::eVisual:
return NS_STYLE_HINT_VISUAL;
case nsFont::MaxDifference::eNone:
break;
}
if (mFontPalette != aNewData.mFontPalette) {
return NS_STYLE_HINT_VISUAL;
}
// XXX Should any of these cause a non-nsChangeHint_NeutralChange change?
if (mMathDepth != aNewData.mMathDepth ||
mScriptUnconstrainedSize != aNewData.mScriptUnconstrainedSize ||
mScriptMinSize != aNewData.mScriptMinSize) {
return nsChangeHint_NeutralChange;
}
return nsChangeHint(0);
}
Length nsStyleFont::ZoomText(const Document& aDocument, Length aSize) {
if (auto* pc = aDocument.GetPresContext()) {
aSize.ScaleBy(pc->TextZoom());
}
return aSize;
}
template <typename T>
static StyleRect<T> StyleRectWithAllSides(const T& aSide) {
return {aSide, aSide, aSide, aSide};
}
nsStyleMargin::nsStyleMargin()
: mMargin(StyleRectWithAllSides(
LengthPercentageOrAuto::LengthPercentage(LengthPercentage::Zero()))),
mScrollMargin(StyleRectWithAllSides(StyleLength{0.})),
mOverflowClipMargin(StyleLength::Zero()) {
MOZ_COUNT_CTOR(nsStyleMargin);
}
nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc)
: mMargin(aSrc.mMargin),
mScrollMargin(aSrc.mScrollMargin),
mOverflowClipMargin(aSrc.mOverflowClipMargin) {
MOZ_COUNT_CTOR(nsStyleMargin);
}
nsChangeHint nsStyleMargin::CalcDifference(
const nsStyleMargin& aNewData) const {
nsChangeHint hint = nsChangeHint(0);
if (mMargin != aNewData.mMargin) {
// Margin differences can't affect descendant intrinsic sizes and
// don't need to force children to reflow.
hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition |
nsChangeHint_ClearAncestorIntrinsics;
}
if (mScrollMargin != aNewData.mScrollMargin) {
// FIXME: Bug 1530253 Support re-snapping when scroll-margin changes.
hint |= nsChangeHint_NeutralChange;
}
if (mOverflowClipMargin != aNewData.mOverflowClipMargin) {
hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
}
return hint;
}
nsStylePadding::nsStylePadding()
: mPadding(StyleRectWithAllSides(LengthPercentage::Zero())),
mScrollPadding(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())) {
MOZ_COUNT_CTOR(nsStylePadding);
}
nsStylePadding::nsStylePadding(const nsStylePadding& aSrc)
: mPadding(aSrc.mPadding), mScrollPadding(aSrc.mScrollPadding) {
MOZ_COUNT_CTOR(nsStylePadding);
}
nsChangeHint nsStylePadding::CalcDifference(
const nsStylePadding& aNewData) const {
nsChangeHint hint = nsChangeHint(0);
if (mPadding != aNewData.mPadding) {
// Padding differences can't affect descendant intrinsic sizes, but do need
// to force children to reflow so that we can reposition them, since their
// offsets are from our frame bounds but our content rect's position within
// those bounds is moving.
// FIXME: It would be good to return a weaker hint here that doesn't
// force reflow of all descendants, but the hint would need to force
// reflow of the frame's children (see how
// ReflowInput::InitResizeFlags initializes the inline-resize flag).
hint |= NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics;
}
if (mScrollPadding != aNewData.mScrollPadding) {
// FIXME: Bug 1530253 Support re-snapping when scroll-padding changes.
hint |= nsChangeHint_NeutralChange;
}
return hint;
}
static inline BorderRadius ZeroBorderRadius() {
auto zero = LengthPercentage::Zero();
return {{{zero, zero}}, {{zero, zero}}, {{zero, zero}}, {{zero, zero}}};
}
nsStyleBorder::nsStyleBorder()
: mBorderRadius(ZeroBorderRadius()),
mBorderImageSource(StyleImage::None()),
mBorderImageWidth(
StyleRectWithAllSides(StyleBorderImageSideWidth::Number(1.))),
mBorderImageOutset(
StyleRectWithAllSides(StyleNonNegativeLengthOrNumber::Number(0.))),
mBorderImageSlice(
{StyleRectWithAllSides(StyleNumberOrPercentage::Percentage({1.})),
false}),
mBorderImageRepeatH(StyleBorderImageRepeat::Stretch),
mBorderImageRepeatV(StyleBorderImageRepeat::Stretch),
mFloatEdge(StyleFloatEdge::ContentBox),
mBoxDecorationBreak(StyleBoxDecorationBreak::Slice),
mBorderTopColor(StyleColor::CurrentColor()),
mBorderRightColor(StyleColor::CurrentColor()),
mBorderBottomColor(StyleColor::CurrentColor()),
mBorderLeftColor(StyleColor::CurrentColor()),
mComputedBorder(0, 0, 0, 0) {
MOZ_COUNT_CTOR(nsStyleBorder);
nscoord medium = kMediumBorderWidth;
for (const auto side : mozilla::AllPhysicalSides()) {
mBorder.Side(side) = medium;
mBorderStyle[side] = StyleBorderStyle::None;
}
}
nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc)
: mBorderRadius(aSrc.mBorderRadius),
mBorderImageSource(aSrc.mBorderImageSource),
mBorderImageWidth(aSrc.mBorderImageWidth),
mBorderImageOutset(aSrc.mBorderImageOutset),
mBorderImageSlice(aSrc.mBorderImageSlice),
mBorderImageRepeatH(aSrc.mBorderImageRepeatH),
mBorderImageRepeatV(aSrc.mBorderImageRepeatV),
mFloatEdge(aSrc.mFloatEdge),
mBoxDecorationBreak(aSrc.mBoxDecorationBreak),
mBorderTopColor(aSrc.mBorderTopColor),
mBorderRightColor(aSrc.mBorderRightColor),
mBorderBottomColor(aSrc.mBorderBottomColor),
mBorderLeftColor(aSrc.mBorderLeftColor),
mComputedBorder(aSrc.mComputedBorder),
mBorder(aSrc.mBorder) {
MOZ_COUNT_CTOR(nsStyleBorder);
for (const auto side : mozilla::AllPhysicalSides()) {
mBorderStyle[side] = aSrc.mBorderStyle[side];
}
}
void nsStyleBorder::TriggerImageLoads(Document& aDocument,
const nsStyleBorder* aOldStyle) {
MOZ_ASSERT(NS_IsMainThread());
mBorderImageSource.ResolveImage(
aDocument, aOldStyle ? &aOldStyle->mBorderImageSource : nullptr);
}
nsMargin nsStyleBorder::GetImageOutset() const {
// We don't check whether there is a border-image (which is OK since
// the initial values yields 0 outset) so that we don't have to
// reflow to update overflow areas when an image loads.
nsMargin outset;
for (const auto s : mozilla::AllPhysicalSides()) {
const auto& coord = mBorderImageOutset.Get(s);
nscoord value;
if (coord.IsLength()) {
value = coord.AsLength().ToAppUnits();
} else {
MOZ_ASSERT(coord.IsNumber());
value = coord.AsNumber() * mComputedBorder.Side(s);
}
outset.Side(s) = value;
}
return outset;
}
nsChangeHint nsStyleBorder::CalcDifference(
const nsStyleBorder& aNewData) const {
// FIXME: XXXbz: As in nsStylePadding::CalcDifference, many of these
// differences should not need to clear descendant intrinsics.
// FIXME: It would be good to return a weaker hint for the
// GetComputedBorder() differences (and perhaps others) that doesn't
// force reflow of all descendants, but the hint would need to force
// reflow of the frame's children (see how
// ReflowInput::InitResizeFlags initializes the inline-resize flag).
if (GetComputedBorder() != aNewData.GetComputedBorder() ||
mFloatEdge != aNewData.mFloatEdge ||
mBorderImageOutset != aNewData.mBorderImageOutset ||
mBoxDecorationBreak != aNewData.mBoxDecorationBreak) {
return NS_STYLE_HINT_REFLOW;
}
for (const auto ix : mozilla::AllPhysicalSides()) {
// See the explanation in nsChangeHint.h of
// nsChangeHint_BorderStyleNoneChange .
// Furthermore, even though we know *this* side is 0 width, just
// assume a repaint hint for some other change rather than bother
// tracking this result through the rest of the function.
if (HasVisibleStyle(ix) != aNewData.HasVisibleStyle(ix)) {
return nsChangeHint_RepaintFrame | nsChangeHint_BorderStyleNoneChange;
}
}
// Note that mBorderStyle stores not only the border style but also
// color-related flags. Given that we've already done an mComputedBorder
// comparison, border-style differences can only lead to a repaint hint. So
// it's OK to just compare the values directly -- if either the actual
// style or the color flags differ we want to repaint.
for (const auto ix : mozilla::AllPhysicalSides()) {
if (mBorderStyle[ix] != aNewData.mBorderStyle[ix] ||
BorderColorFor(ix) != aNewData.BorderColorFor(ix)) {
return nsChangeHint_RepaintFrame;
}
}
// Note that border radius also controls the outline radius if the
// layout.css.outline-follows-border-radius.enabled pref is set. Any
// optimizations here should apply to both.
if (mBorderRadius != aNewData.mBorderRadius) {
return nsChangeHint_RepaintFrame;
}
// Loading status of the border image can be accessed in main thread only
// while CalcDifference might be executed on a background thread. As a
// result, we have to check mBorderImage* fields even before border image was
// actually loaded.
if (!mBorderImageSource.IsNone() || !aNewData.mBorderImageSource.IsNone()) {
if (mBorderImageSource != aNewData.mBorderImageSource ||
mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
mBorderImageSlice != aNewData.mBorderImageSlice ||
mBorderImageWidth != aNewData.mBorderImageWidth) {
return nsChangeHint_RepaintFrame;
}
}
// mBorder is the specified border value. Changes to this don't
// need any change processing, since we operate on the computed
// border values instead.
if (mBorder != aNewData.mBorder) {
return nsChangeHint_NeutralChange;
}
// mBorderImage* fields are checked only when border-image is not 'none'.
if (mBorderImageSource != aNewData.mBorderImageSource ||
mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
mBorderImageSlice != aNewData.mBorderImageSlice ||
mBorderImageWidth != aNewData.mBorderImageWidth) {
return nsChangeHint_NeutralChange;
}
return nsChangeHint(0);
}
nsStyleOutline::nsStyleOutline()
: mOutlineWidth(kMediumBorderWidth),
mOutlineOffset({0.0f}),
mOutlineColor(StyleColor::CurrentColor()),
mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)),
mActualOutlineWidth(0) {
MOZ_COUNT_CTOR(nsStyleOutline);
}
nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc)
: mOutlineWidth(aSrc.mOutlineWidth),
mOutlineOffset(aSrc.mOutlineOffset),
mOutlineColor(aSrc.mOutlineColor),
mOutlineStyle(aSrc.mOutlineStyle),
mActualOutlineWidth(aSrc.mActualOutlineWidth) {
MOZ_COUNT_CTOR(nsStyleOutline);
}
nsChangeHint nsStyleOutline::CalcDifference(
const nsStyleOutline& aNewData) const {
const bool shouldPaintOutline = ShouldPaintOutline();
// We need the explicit 'outline-style: auto' check because
// 'outline-style: auto' effectively also changes 'outline-width'.
if (shouldPaintOutline != aNewData.ShouldPaintOutline() ||
mActualOutlineWidth != aNewData.mActualOutlineWidth ||
mOutlineStyle.IsAuto() != aNewData.mOutlineStyle.IsAuto() ||
(shouldPaintOutline && mOutlineOffset != aNewData.mOutlineOffset)) {
return nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
nsChangeHint_RepaintFrame;
}
if (mOutlineStyle != aNewData.mOutlineStyle ||
mOutlineColor != aNewData.mOutlineColor) {
return shouldPaintOutline ? nsChangeHint_RepaintFrame
: nsChangeHint_NeutralChange;
}
if (mOutlineWidth != aNewData.mOutlineWidth ||
mOutlineOffset != aNewData.mOutlineOffset) {
return nsChangeHint_NeutralChange;
}
return nsChangeHint(0);
}
nsSize nsStyleOutline::EffectiveOffsetFor(const nsRect& aRect) const {
const nscoord offset = mOutlineOffset.ToAppUnits();
if (offset >= 0) {
// Fast path for non-negative offset values
return nsSize(offset, offset);
}
return nsSize(std::max(offset, -(aRect.Width() / 2)),
std::max(offset, -(aRect.Height() / 2)));
}
// --------------------
// nsStyleList
//
nsStyleList::nsStyleList()
: mListStylePosition(StyleListStylePosition::Outside),
mListStyleType(StyleCounterStyle::Name({StyleAtom(nsGkAtoms::disc)})),
mQuotes(StyleQuotes::Auto()),
mListStyleImage(StyleImage::None()) {
MOZ_COUNT_CTOR(nsStyleList);
MOZ_ASSERT(NS_IsMainThread());
}
nsStyleList::nsStyleList(const nsStyleList& aSource)
: mListStylePosition(aSource.mListStylePosition),
mListStyleType(aSource.mListStyleType),
mQuotes(aSource.mQuotes),
mListStyleImage(aSource.mListStyleImage) {
MOZ_COUNT_CTOR(nsStyleList);
}
void nsStyleList::TriggerImageLoads(Document& aDocument,
const nsStyleList* aOldStyle) {
MOZ_ASSERT(NS_IsMainThread());
mListStyleImage.ResolveImage(
aDocument, aOldStyle ? &aOldStyle->mListStyleImage : nullptr);
}
nsChangeHint nsStyleList::CalcDifference(const nsStyleList& aNewData,
const ComputedStyle& aOldStyle) const {
// If the quotes implementation is ever going to change we might not need
// a framechange here and a reflow should be sufficient. See bug 35768.
if (mQuotes != aNewData.mQuotes) {
return nsChangeHint_ReconstructFrame;
}
nsChangeHint hint = nsChangeHint(0);
// Only elements whose display value is list-item can be affected by
// list-style-{position,type,image}. This also relies on that when the display
// value changes from something else to list-item, that change itself would
// cause ReconstructFrame.
if (mListStylePosition != aNewData.mListStylePosition ||
mListStyleType != aNewData.mListStyleType ||
mListStyleImage != aNewData.mListStyleImage) {
if (aOldStyle.StyleDisplay()->IsListItem()) {
return nsChangeHint_ReconstructFrame;
}
// list-style-image may affect nsImageFrame for XUL elements, but that is
// dealt with explicitly in nsImageFrame::DidSetComputedStyle.
hint = nsChangeHint_NeutralChange;
}
return hint;
}
already_AddRefed<nsIURI> nsStyleList::GetListStyleImageURI() const {
if (!mListStyleImage.IsUrl()) {
return nullptr;
}
return do_AddRef(mListStyleImage.AsUrl().GetURI());
}
// --------------------
// nsStyleXUL
//
nsStyleXUL::nsStyleXUL()
: mBoxFlex(0.0f),
mBoxOrdinal(1),
mBoxAlign(StyleBoxAlign::Stretch),
mBoxDirection(StyleBoxDirection::Normal),
mBoxOrient(StyleBoxOrient::Horizontal),
mBoxPack(StyleBoxPack::Start) {
MOZ_COUNT_CTOR(nsStyleXUL);
}
nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource)
: mBoxFlex(aSource.mBoxFlex),
mBoxOrdinal(aSource.mBoxOrdinal),
mBoxAlign(aSource.mBoxAlign),
mBoxDirection(aSource.mBoxDirection),
mBoxOrient(aSource.mBoxOrient),
mBoxPack(aSource.mBoxPack) {
MOZ_COUNT_CTOR(nsStyleXUL);
}
nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aNewData) const {
if (mBoxAlign == aNewData.mBoxAlign &&
mBoxDirection == aNewData.mBoxDirection &&
mBoxFlex == aNewData.mBoxFlex && mBoxOrient == aNewData.mBoxOrient &&
mBoxPack == aNewData.mBoxPack && mBoxOrdinal == aNewData.mBoxOrdinal) {
return nsChangeHint(0);
}
if (mBoxOrdinal != aNewData.mBoxOrdinal) {
return nsChangeHint_ReconstructFrame;
}
return NS_STYLE_HINT_REFLOW;
}
// --------------------
// nsStyleColumn
//
nsStyleColumn::nsStyleColumn()
: mColumnWidth(LengthOrAuto::Auto()),
mColumnRuleColor(StyleColor::CurrentColor()),
mColumnRuleStyle(StyleBorderStyle::None),
mColumnRuleWidth(kMediumBorderWidth),
mActualColumnRuleWidth(0) {
MOZ_COUNT_CTOR(nsStyleColumn);
}
nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource)
: mColumnCount(aSource.mColumnCount),
mColumnWidth(aSource.mColumnWidth),
mColumnRuleColor(aSource.mColumnRuleColor),
mColumnRuleStyle(aSource.mColumnRuleStyle),
mColumnFill(aSource.mColumnFill),
mColumnSpan(aSource.mColumnSpan),
mColumnRuleWidth(aSource.mColumnRuleWidth),
mActualColumnRuleWidth(aSource.mActualColumnRuleWidth) {
MOZ_COUNT_CTOR(nsStyleColumn);
}
nsChangeHint nsStyleColumn::CalcDifference(
const nsStyleColumn& aNewData) const {
if (mColumnWidth.IsAuto() != aNewData.mColumnWidth.IsAuto() ||
mColumnCount != aNewData.mColumnCount ||
mColumnSpan != aNewData.mColumnSpan) {
// We force column count changes to do a reframe, because it's tricky to
// handle some edge cases where the column count gets smaller and content
// overflows.
// XXX not ideal
return nsChangeHint_ReconstructFrame;
}
if (mColumnWidth != aNewData.mColumnWidth ||
mColumnFill != aNewData.mColumnFill) {
return NS_STYLE_HINT_REFLOW;
}
if (mActualColumnRuleWidth != aNewData.mActualColumnRuleWidth ||
mColumnRuleStyle != aNewData.mColumnRuleStyle ||
mColumnRuleColor != aNewData.mColumnRuleColor) {
return NS_STYLE_HINT_VISUAL;
}
if (mColumnRuleWidth != aNewData.mColumnRuleWidth) {
return nsChangeHint_NeutralChange;
}
return nsChangeHint(0);
}
using SVGPaintFallback = StyleGenericSVGPaintFallback<StyleColor>;
// --------------------
// nsStyleSVG
//
nsStyleSVG::nsStyleSVG()
: mFill{StyleSVGPaintKind::Color(StyleColor::Black()),
SVGPaintFallback::Unset()},
mStroke{StyleSVGPaintKind::None(), SVGPaintFallback::Unset()},
mMarkerEnd(StyleUrlOrNone::None()),
mMarkerMid(StyleUrlOrNone::None()),
mMarkerStart(StyleUrlOrNone::None()),
mMozContextProperties{{}, {0}},
mStrokeDasharray(StyleSVGStrokeDashArray::Values({})),
mStrokeDashoffset(
StyleSVGLength::LengthPercentage(LengthPercentage::Zero())),
mStrokeWidth(
StyleSVGWidth::LengthPercentage(LengthPercentage::FromPixels(1.0f))),
mFillOpacity(StyleSVGOpacity::Opacity(1.0f)),
mStrokeMiterlimit(4.0f),
mStrokeOpacity(StyleSVGOpacity::Opacity(1.0f)),
mClipRule(StyleFillRule::Nonzero),
mColorInterpolation(StyleColorInterpolation::Srgb),
mColorInterpolationFilters(StyleColorInterpolation::Linearrgb),
mFillRule(StyleFillRule::Nonzero),
mPaintOrder(0),
mShapeRendering(StyleShapeRendering::Auto),
mStrokeLinecap(StyleStrokeLinecap::Butt),
mStrokeLinejoin(StyleStrokeLinejoin::Miter),
mDominantBaseline(StyleDominantBaseline::Auto),
mTextAnchor(StyleTextAnchor::Start) {
MOZ_COUNT_CTOR(nsStyleSVG);
}
nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource)
: mFill(aSource.mFill),
mStroke(aSource.mStroke),
mMarkerEnd(aSource.mMarkerEnd),
mMarkerMid(aSource.mMarkerMid),
mMarkerStart(aSource.mMarkerStart),
mMozContextProperties(aSource.mMozContextProperties),
mStrokeDasharray(aSource.mStrokeDasharray),
mStrokeDashoffset(aSource.mStrokeDashoffset),
mStrokeWidth(aSource.mStrokeWidth),
mFillOpacity(aSource.mFillOpacity),
mStrokeMiterlimit(aSource.mStrokeMiterlimit),
mStrokeOpacity(aSource.mStrokeOpacity),
mClipRule(aSource.mClipRule),
mColorInterpolation(aSource.mColorInterpolation),
mColorInterpolationFilters(aSource.mColorInterpolationFilters),
mFillRule(aSource.mFillRule),
mPaintOrder(aSource.mPaintOrder),
mShapeRendering(aSource.mShapeRendering),
mStrokeLinecap(aSource.mStrokeLinecap),
mStrokeLinejoin(aSource.mStrokeLinejoin),
mDominantBaseline(aSource.mDominantBaseline),
mTextAnchor(aSource.mTextAnchor) {
MOZ_COUNT_CTOR(nsStyleSVG);
}
static bool PaintURIChanged(const StyleSVGPaint& aPaint1,
const StyleSVGPaint& aPaint2) {
if (aPaint1.kind.IsPaintServer() != aPaint2.kind.IsPaintServer()) {
return true;
}
return aPaint1.kind.IsPaintServer() &&
aPaint1.kind.AsPaintServer() != aPaint2.kind.AsPaintServer();
}
nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aNewData) const {
nsChangeHint hint = nsChangeHint(0);
if (mMarkerEnd != aNewData.mMarkerEnd || mMarkerMid != aNewData.mMarkerMid ||
mMarkerStart != aNewData.mMarkerStart) {
// Markers currently contribute to SVGGeometryFrame::mRect,
// so we need a reflow as well as a repaint. No intrinsic sizes need
// to change, so nsChangeHint_NeedReflow is sufficient.
return nsChangeHint_UpdateEffects | nsChangeHint_NeedReflow |
nsChangeHint_RepaintFrame;
}
if (mFill != aNewData.mFill || mStroke != aNewData.mStroke ||
mFillOpacity != aNewData.mFillOpacity ||
mStrokeOpacity != aNewData.mStrokeOpacity) {
hint |= nsChangeHint_RepaintFrame;
if (HasStroke() != aNewData.HasStroke() ||
(!HasStroke() && HasFill() != aNewData.HasFill())) {
// Frame bounds and overflow rects depend on whether we "have" fill or
// stroke. Whether we have stroke or not just changed, or else we have no
// stroke (in which case whether we have fill or not is significant to
// frame bounds) and whether we have fill or not just changed. In either
// case we need to reflow so the frame rect is updated.
// XXXperf this is a waste on non SVGGeometryFrames.
hint |= nsChangeHint_NeedReflow;
}
if (PaintURIChanged(mFill, aNewData.mFill) ||
PaintURIChanged(mStroke, aNewData.mStroke)) {
hint |= nsChangeHint_UpdateEffects;
}
}
// Stroke currently contributes to SVGGeometryFrame::mRect, so
// we need a reflow here. No intrinsic sizes need to change, so
// nsChangeHint_NeedReflow is sufficient.
// Note that stroke-dashoffset does not affect SVGGeometryFrame::mRect.
// text-anchor and dominant-baseline changes also require a reflow since
// they change frames' rects.
if (mStrokeWidth != aNewData.mStrokeWidth ||
mStrokeMiterlimit != aNewData.mStrokeMiterlimit ||
mStrokeLinecap != aNewData.mStrokeLinecap ||
mStrokeLinejoin != aNewData.mStrokeLinejoin ||
mDominantBaseline != aNewData.mDominantBaseline ||
mTextAnchor != aNewData.mTextAnchor) {
return hint | nsChangeHint_NeedReflow | nsChangeHint_RepaintFrame;
}
if (hint & nsChangeHint_RepaintFrame) {
return hint; // we don't add anything else below
}
if (mStrokeDashoffset != aNewData.mStrokeDashoffset ||
mClipRule != aNewData.mClipRule ||
mColorInterpolation != aNewData.mColorInterpolation ||
mColorInterpolationFilters != aNewData.mColorInterpolationFilters ||
mFillRule != aNewData.mFillRule || mPaintOrder != aNewData.mPaintOrder ||
mShapeRendering != aNewData.mShapeRendering ||
mStrokeDasharray != aNewData.mStrokeDasharray ||
mMozContextProperties.bits != aNewData.mMozContextProperties.bits) {
return hint | nsChangeHint_RepaintFrame;
}
if (!hint) {
if (mMozContextProperties.idents != aNewData.mMozContextProperties.idents) {
hint = nsChangeHint_NeutralChange;
}
}
return hint;
}
// --------------------
// nsStyleSVGReset
//
nsStyleSVGReset::nsStyleSVGReset()
: mX(LengthPercentage::Zero()),
mY(LengthPercentage::Zero()),
mCx(LengthPercentage::Zero()),
mCy(LengthPercentage::Zero()),
mRx(NonNegativeLengthPercentageOrAuto::Auto()),
mRy(NonNegativeLengthPercentageOrAuto::Auto()),
mR(NonNegativeLengthPercentage::Zero()),
mMask(nsStyleImageLayers::LayerType::Mask),
mClipPath(StyleClipPath::None()),
mStopColor(StyleColor::Black()),
mFloodColor(StyleColor::Black()),
mLightingColor(StyleColor::White()),
mStopOpacity(1.0f),
mFloodOpacity(1.0f),
mVectorEffect(StyleVectorEffect::None),
mMaskType(StyleMaskType::Luminance),
mD(StyleDProperty::None()) {
MOZ_COUNT_CTOR(nsStyleSVGReset);
}
nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource)
: mX(aSource.mX),
mY(aSource.mY),
mCx(aSource.mCx),
mCy(aSource.mCy),
mRx(aSource.mRx),
mRy(aSource.mRy),
mR(aSource.mR),
mMask(aSource.mMask),
mClipPath(aSource.mClipPath),
mStopColor(aSource.mStopColor),
mFloodColor(aSource.mFloodColor),
mLightingColor(aSource.mLightingColor),
mStopOpacity(aSource.mStopOpacity),
mFloodOpacity(aSource.mFloodOpacity),
mVectorEffect(aSource.mVectorEffect),
mMaskType(aSource.mMaskType),
mD(aSource.mD) {
MOZ_COUNT_CTOR(nsStyleSVGReset);
}
void nsStyleSVGReset::TriggerImageLoads(Document& aDocument,
const nsStyleSVGReset* aOldStyle) {
MOZ_ASSERT(NS_IsMainThread());
// NOTE(emilio): we intentionally don't call TriggerImageLoads for clip-path.
NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mMask) {
auto& image = mMask.mLayers[i].mImage;
if (!image.IsImageRequestType()) {
continue;
}
const auto* url = image.GetImageRequestURLValue();
// If the url is a local ref, it must be a <mask-resource>, so we don't
// need to resolve the style image.
if (url->IsLocalRef()) {
continue;
}
#if 0
// XXX The old style system also checks whether this is a reference to
// the current document with reference, but it doesn't seem to be a
// behavior mentioned anywhere, so we comment out the code for now.
nsIURI* docURI = aPresContext->Document()->GetDocumentURI();
if (url->EqualsExceptRef(docURI)) {
continue;
}
#endif
// Otherwise, we may need the image even if it has a reference, in case
// the referenced element isn't a valid SVG <mask> element.
const auto* oldImage = (aOldStyle && aOldStyle->mMask.mLayers.Length() > i)
? &aOldStyle->mMask.mLayers[i].mImage
: nullptr;
image.ResolveImage(aDocument, oldImage);
}
}
nsChangeHint nsStyleSVGReset::CalcDifference(
const nsStyleSVGReset& aNewData) const {
nsChangeHint hint = nsChangeHint(0);
if (mX != aNewData.mX || mY != aNewData.mY || mCx != aNewData.mCx ||
mCy != aNewData.mCy || mR != aNewData.mR || mRx != aNewData.mRx ||
mRy != aNewData.mRy || mD != aNewData.mD) {
hint |= nsChangeHint_InvalidateRenderingObservers | nsChangeHint_NeedReflow;
}
if (mClipPath != aNewData.mClipPath) {
hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
}
if (mVectorEffect != aNewData.mVectorEffect) {
// Stroke currently affects SVGGeometryFrame::mRect, and
// vector-effect affect stroke. As a result we need to reflow if
// vector-effect changes in order to have SVGGeometryFrame::
// ReflowSVG called to update its mRect. No intrinsic sizes need
// to change so nsChangeHint_NeedReflow is sufficient.
hint |= nsChangeHint_NeedReflow | nsChangeHint_RepaintFrame;
} else if (mStopColor != aNewData.mStopColor ||
mFloodColor != aNewData.mFloodColor ||
mLightingColor != aNewData.mLightingColor ||
mStopOpacity != aNewData.mStopOpacity ||
mFloodOpacity != aNewData.mFloodOpacity ||
mMaskType != aNewData.mMaskType || mD != aNewData.mD) {
hint |= nsChangeHint_RepaintFrame;
}
hint |=
mMask.CalcDifference(aNewData.mMask, nsStyleImageLayers::LayerType::Mask);
return hint;
}
bool nsStyleSVGReset::HasMask() const {
for (uint32_t i = 0; i < mMask.mImageCount; i++) {
if (!mMask.mLayers[i].mImage.IsNone()) {
return true;
}
}
return false;
}
// --------------------
// nsStylePage
//
nsStylePage::nsStylePage(const nsStylePage& aSrc)
: mSize(aSrc.mSize),
mPage(aSrc.mPage),
mPageOrientation(aSrc.mPageOrientation) {
MOZ_COUNT_CTOR(nsStylePage);
}
nsChangeHint nsStylePage::CalcDifference(const nsStylePage& aNewData) const {
// Page rule styling only matters when printing or using print preview.
if (aNewData.mSize != mSize || aNewData.mPage != mPage ||
aNewData.mPageOrientation != mPageOrientation) {
return nsChangeHint_NeutralChange;
}
return nsChangeHint_Empty;
}
// --------------------
// nsStylePosition
//
nsStylePosition::nsStylePosition()
: mObjectPosition(Position::FromPercentage(0.5f)),
mOffset(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())),
mWidth(StyleSize::Auto()),
mMinWidth(StyleSize::Auto()),
mMaxWidth(StyleMaxSize::None()),
mHeight(StyleSize::Auto()),
mMinHeight(StyleSize::Auto()),
mMaxHeight(StyleMaxSize::None()),
mPositionAnchor(StylePositionAnchor::Auto()),
mPositionVisibility(StylePositionVisibility::ALWAYS),
mPositionTryOptions(StylePositionTryOptions()),
mPositionTryOrder(StylePositionTryOrder::Normal),
mInsetArea(StyleInsetArea{StyleInsetAreaKeyword::None,
StyleInsetAreaKeyword::None}),
mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())),
mAspectRatio(StyleAspectRatio::Auto()),
mGridAutoFlow(StyleGridAutoFlow::ROW),
mMasonryAutoFlow(
{StyleMasonryPlacement::Pack, StyleMasonryItemOrder::DefiniteFirst}),
mAlignContent({StyleAlignFlags::NORMAL}),
mAlignItems({StyleAlignFlags::NORMAL}),
mAlignSelf({StyleAlignFlags::AUTO}),
mJustifyContent({StyleAlignFlags::NORMAL}),
mJustifyItems({{StyleAlignFlags::LEGACY}, {StyleAlignFlags::NORMAL}}),
mJustifySelf({StyleAlignFlags::AUTO}),
mFlexDirection(StyleFlexDirection::Row),
mFlexWrap(StyleFlexWrap::Nowrap),
mObjectFit(StyleObjectFit::Fill),
mBoxSizing(StyleBoxSizing::Content),
mOrder(0),
mFlexGrow(0.0f),
mFlexShrink(1.0f),
mZIndex(StyleZIndex::Auto()),
mGridTemplateColumns(StyleGridTemplateComponent::None()),
mGridTemplateRows(StyleGridTemplateComponent::None()),
mGridTemplateAreas(StyleGridTemplateAreas::None()),
mColumnGap(NonNegativeLengthPercentageOrNormal::Normal()),
mRowGap(NonNegativeLengthPercentageOrNormal::Normal()),
mContainIntrinsicWidth(StyleContainIntrinsicSize::None()),
mContainIntrinsicHeight(StyleContainIntrinsicSize::None()) {
MOZ_COUNT_CTOR(nsStylePosition);
// The initial value of grid-auto-columns and grid-auto-rows is 'auto',
// which computes to 'minmax(auto, auto)'.
// Other members get their default constructors
// which initialize them to representations of their respective initial value.
// mGridTemplate{Rows,Columns}: false and empty arrays for 'none'
// mGrid{Column,Row}{Start,End}: false/0/empty values for 'auto'
}
nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
: mObjectPosition(aSource.mObjectPosition),
mOffset(aSource.mOffset),
mWidth(aSource.mWidth),
mMinWidth(aSource.mMinWidth),
mMaxWidth(aSource.mMaxWidth),
mHeight(aSource.mHeight),
mMinHeight(aSource.mMinHeight),
mMaxHeight(aSource.mMaxHeight),
mPositionAnchor(aSource.mPositionAnchor),
mPositionVisibility(aSource.mPositionVisibility),
mPositionTryOptions(aSource.mPositionTryOptions),
mPositionTryOrder(aSource.mPositionTryOrder),
mInsetArea(aSource.mInsetArea),
mFlexBasis(aSource.mFlexBasis),
mGridAutoColumns(aSource.mGridAutoColumns),
mGridAutoRows(aSource.mGridAutoRows),
mAspectRatio(aSource.mAspectRatio),
mGridAutoFlow(aSource.mGridAutoFlow),
mMasonryAutoFlow(aSource.mMasonryAutoFlow),
mAlignContent(aSource.mAlignContent),
mAlignItems(aSource.mAlignItems),
mAlignSelf(aSource.mAlignSelf),
mJustifyContent(aSource.mJustifyContent),
mJustifyItems(aSource.mJustifyItems),
mJustifySelf(aSource.mJustifySelf),
mFlexDirection(aSource.mFlexDirection),
mFlexWrap(aSource.mFlexWrap),
mObjectFit(aSource.mObjectFit),
mBoxSizing(aSource.mBoxSizing),
mOrder(aSource.mOrder),
mFlexGrow(aSource.mFlexGrow),
mFlexShrink(aSource.mFlexShrink),
mZIndex(aSource.mZIndex),
mGridTemplateColumns(aSource.mGridTemplateColumns),
mGridTemplateRows(aSource.mGridTemplateRows),
mGridTemplateAreas(aSource.mGridTemplateAreas),
mGridColumnStart(aSource.mGridColumnStart),
mGridColumnEnd(aSource.mGridColumnEnd),
mGridRowStart(aSource.mGridRowStart),
mGridRowEnd(aSource.mGridRowEnd),
mColumnGap(aSource.mColumnGap),
mRowGap(aSource.mRowGap),
mContainIntrinsicWidth(aSource.mContainIntrinsicWidth),
mContainIntrinsicHeight(aSource.mContainIntrinsicHeight) {
MOZ_COUNT_CTOR(nsStylePosition);
}
static bool IsAutonessEqual(const StyleRect<LengthPercentageOrAuto>& aSides1,
const StyleRect<LengthPercentageOrAuto>& aSides2) {
for (const auto side : mozilla::AllPhysicalSides()) {
if (aSides1.Get(side).IsAuto() != aSides2.Get(side).IsAuto()) {
return false;
}
}
return true;
}
nsChangeHint nsStylePosition::CalcDifference(
const nsStylePosition& aNewData, const ComputedStyle& aOldStyle) const {
if (mGridTemplateColumns.IsMasonry() !=
aNewData.mGridTemplateColumns.IsMasonry() ||
mGridTemplateRows.IsMasonry() != aNewData.mGridTemplateRows.IsMasonry()) {
// XXXmats this could be optimized to AllReflowHints with a bit of work,
// but I'll assume this is a very rare use case in practice. (bug 1623886)
return nsChangeHint_ReconstructFrame;
}
nsChangeHint hint = nsChangeHint(0);
// Changes to "z-index" require a repaint.
if (mZIndex != aNewData.mZIndex) {
hint |= nsChangeHint_RepaintFrame;
}
// Changes to "object-fit" & "object-position" require a repaint. They
// may also require a reflow, if we have a nsSubDocumentFrame, so that we
// can adjust the size & position of the subdocument.
if (mObjectFit != aNewData.mObjectFit ||
mObjectPosition != aNewData.mObjectPosition) {
hint |= nsChangeHint_RepaintFrame | nsChangeHint_NeedReflow;
}
if (mContainIntrinsicWidth != aNewData.mContainIntrinsicWidth ||
mContainIntrinsicHeight != aNewData.mContainIntrinsicHeight) {
hint |= NS_STYLE_HINT_REFLOW;
}
if (mOrder != aNewData.mOrder) {
// "order" impacts both layout order and stacking order, so we need both a
// reflow and a repaint when it changes. (Technically, we only need a
// reflow if we're in a multi-line flexbox (which we can't be sure about,
// since that's determined by styling on our parent) -- there, "order" can
// affect which flex line we end up on, & hence can affect our sizing by
// changing the group of flex items we're competing with for space.)
return hint | nsChangeHint_RepaintFrame | nsChangeHint_AllReflowHints;
}
if (mBoxSizing != aNewData.mBoxSizing) {
// Can affect both widths and heights; just a bad scene.
return hint | nsChangeHint_AllReflowHints;
}
if (mAlignItems != aNewData.mAlignItems ||
mAlignSelf != aNewData.mAlignSelf) {
return hint | nsChangeHint_AllReflowHints;
}
// Properties that apply to flex items:
// XXXdholbert These should probably be more targeted (bug 819536)
if (mFlexBasis != aNewData.mFlexBasis || mFlexGrow != aNewData.mFlexGrow ||
mFlexShrink != aNewData.mFlexShrink) {
return hint | nsChangeHint_AllReflowHints;
}
// Properties that apply to flex containers:
// - flex-direction can swap a flex container between vertical & horizontal.
// - flex-wrap changes whether a flex container's children are wrapped, which
// impacts their sizing/positioning and hence impacts the container's size.
if (mFlexDirection != aNewData.mFlexDirection ||
mFlexWrap != aNewData.mFlexWrap) {
return hint | nsChangeHint_AllReflowHints;
}
// Properties that apply to grid containers:
// FIXME: only for grid containers
// (ie. 'display: grid' or 'display: inline-grid')
if (mGridTemplateColumns != aNewData.mGridTemplateColumns ||
mGridTemplateRows != aNewData.mGridTemplateRows ||
mGridTemplateAreas != aNewData.mGridTemplateAreas ||
mGridAutoColumns != aNewData.mGridAutoColumns ||
mGridAutoRows != aNewData.mGridAutoRows ||
mGridAutoFlow != aNewData.mGridAutoFlow ||
mMasonryAutoFlow != aNewData.mMasonryAutoFlow) {
return hint | nsChangeHint_AllReflowHints;
}
// Properties that apply to grid items:
// FIXME: only for grid items
// (ie. parent frame is 'display: grid' or 'display: inline-grid')
if (mGridColumnStart != aNewData.mGridColumnStart ||
mGridColumnEnd != aNewData.mGridColumnEnd ||
mGridRowStart != aNewData.mGridRowStart ||
mGridRowEnd != aNewData.mGridRowEnd ||
mColumnGap != aNewData.mColumnGap || mRowGap != aNewData.mRowGap) {
return hint | nsChangeHint_AllReflowHints;
}
// Changing 'justify-content/items/self' might affect the positioning,
// but it won't affect any sizing.
if (mJustifyContent != aNewData.mJustifyContent ||
mJustifyItems.computed != aNewData.mJustifyItems.computed ||
mJustifySelf != aNewData.mJustifySelf) {
hint |= nsChangeHint_NeedReflow;
}
// No need to do anything if specified justify-items changes, as long as the
// computed one (tested above) is unchanged.
if (mJustifyItems.specified != aNewData.mJustifyItems.specified) {
hint |= nsChangeHint_NeutralChange;
}
// 'align-content' doesn't apply to a single-line flexbox but we don't know
// if we're a flex container at this point so we can't optimize for that.
if (mAlignContent != aNewData.mAlignContent) {
hint |= nsChangeHint_NeedReflow;
}
bool widthChanged = mWidth != aNewData.mWidth ||
mMinWidth != aNewData.mMinWidth ||
mMaxWidth != aNewData.mMaxWidth;
bool heightChanged = mHeight != aNewData.mHeight ||
mMinHeight != aNewData.mMinHeight ||
mMaxHeight != aNewData.mMaxHeight;
if (widthChanged || heightChanged) {
// It doesn't matter whether we're looking at the old or new visibility
// struct, since a change between vertical and horizontal writing-mode will
// cause a reframe.
const bool isVertical = aOldStyle.StyleVisibility()->mWritingMode !=
StyleWritingModeProperty::HorizontalTb;
if (isVertical ? widthChanged : heightChanged) {
hint |= nsChangeHint_ReflowHintsForBSizeChange;
}
if (isVertical ? heightChanged : widthChanged) {
hint |= nsChangeHint_ReflowHintsForISizeChange;
}
}
if (mPositionAnchor != aNewData.mPositionAnchor) {
// 'position-anchor' provides a default anchor for other anchor positioning
// properties in the event that they don't specify one explicitly.
// TODO(jwatt): Re-evaluate what we're doing here.
hint |= nsChangeHint_NeutralChange;
}
if (mPositionVisibility != aNewData.mPositionVisibility ||
mPositionTryOptions != aNewData.mPositionTryOptions ||
mPositionTryOrder != aNewData.mPositionTryOrder ||
mInsetArea != aNewData.mInsetArea) {
hint |= nsChangeHint_NeutralChange;
}
if (mAspectRatio != aNewData.mAspectRatio) {
hint |= nsChangeHint_ReflowHintsForISizeChange |
nsChangeHint_ReflowHintsForBSizeChange;
}
// If any of the offsets have changed, then return the respective hints
// so that we would hopefully be able to avoid reflowing.
// Note that it is possible that we'll need to reflow when processing
// restyles, but we don't have enough information to make a good decision
// right now.
// Don't try to handle changes between "auto" and non-auto efficiently;
// that's tricky to do and will hardly ever be able to avoid a reflow.
if (mOffset != aNewData.mOffset) {
if (IsAutonessEqual(mOffset, aNewData.mOffset)) {
hint |=
nsChangeHint_RecomputePosition | nsChangeHint_UpdateParentOverflow;
} else {
hint |=
nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
}
}
return hint;
}
const StyleContainIntrinsicSize& nsStylePosition::ContainIntrinsicBSize(
const WritingMode& aWM) const {
return aWM.IsVertical() ? mContainIntrinsicWidth : mContainIntrinsicHeight;
}
const StyleContainIntrinsicSize& nsStylePosition::ContainIntrinsicISize(
const WritingMode& aWM) const {
return aWM.IsVertical() ? mContainIntrinsicHeight : mContainIntrinsicWidth;
}
StyleAlignSelf nsStylePosition::UsedAlignSelf(
const ComputedStyle* aParent) const {
if (mAlignSelf._0 != StyleAlignFlags::AUTO) {
return mAlignSelf;
}
if (MOZ_LIKELY(aParent)) {
auto parentAlignItems = aParent->StylePosition()->mAlignItems;
MOZ_ASSERT(!(parentAlignItems._0 & StyleAlignFlags::LEGACY),
"align-items can't have 'legacy'");
return {parentAlignItems._0};
}
return {StyleAlignFlags::NORMAL};
}
StyleJustifySelf nsStylePosition::UsedJustifySelf(
const ComputedStyle* aParent) const {
if (mJustifySelf._0 != StyleAlignFlags::AUTO) {
return mJustifySelf;
}
if (MOZ_LIKELY(aParent)) {
const auto& inheritedJustifyItems =
aParent->StylePosition()->mJustifyItems.computed;
return {inheritedJustifyItems._0 & ~StyleAlignFlags::LEGACY};
}
return {StyleAlignFlags::NORMAL};
}
// --------------------
// nsStyleTable
//
nsStyleTable::nsStyleTable()
: mLayoutStrategy(StyleTableLayout::Auto), mXSpan(1) {
MOZ_COUNT_CTOR(nsStyleTable);
}
nsStyleTable::nsStyleTable(const nsStyleTable& aSource)
: mLayoutStrategy(aSource.mLayoutStrategy), mXSpan(aSource.mXSpan) {
MOZ_COUNT_CTOR(nsStyleTable);
}
nsChangeHint nsStyleTable::CalcDifference(const nsStyleTable& aNewData) const {
if (mXSpan != aNewData.mXSpan ||
mLayoutStrategy != aNewData.mLayoutStrategy) {
return nsChangeHint_ReconstructFrame;
}
return nsChangeHint(0);
}
// -----------------------
// nsStyleTableBorder
nsStyleTableBorder::nsStyleTableBorder()
: mBorderSpacing{Length::Zero(), Length::Zero()},
mBorderCollapse(StyleBorderCollapse::Separate),
mCaptionSide(StyleCaptionSide::Top),
mEmptyCells(StyleEmptyCells::Show) {
MOZ_COUNT_CTOR(nsStyleTableBorder);
}
nsStyleTableBorder::nsStyleTableBorder(const nsStyleTableBorder& aSource)
: mBorderSpacing(aSource.mBorderSpacing),
mBorderCollapse(aSource.mBorderCollapse),
mCaptionSide(aSource.mCaptionSide),
mEmptyCells(aSource.mEmptyCells) {
MOZ_COUNT_CTOR(nsStyleTableBorder);
}
nsChangeHint nsStyleTableBorder::CalcDifference(
const nsStyleTableBorder& aNewData) const {
// Border-collapse changes need a reframe, because we use a different frame
// class for table cells in the collapsed border model. This is used to
// conserve memory when using the separated border model (collapsed borders
// require extra state to be stored).
if (mBorderCollapse != aNewData.mBorderCollapse) {
return nsChangeHint_ReconstructFrame;
}
if (mCaptionSide != aNewData.mCaptionSide ||
mBorderSpacing != aNewData.mBorderSpacing) {
return NS_STYLE_HINT_REFLOW;
}
if (mEmptyCells != aNewData.mEmptyCells) {
return NS_STYLE_HINT_VISUAL;
}
return nsChangeHint(0);
}
template <typename T>
static bool GradientItemsAreOpaque(
Span<const StyleGenericGradientItem<StyleColor, T>> aItems) {
for (auto& stop : aItems) {
if (stop.IsInterpolationHint()) {
continue;
}
auto& color = stop.IsSimpleColorStop() ? stop.AsSimpleColorStop()
: stop.AsComplexColorStop().color;
if (color.MaybeTransparent()) {
// We don't know the foreground color here, so if it's being used
// we must assume it might be transparent.
return false;
}
}
return true;
}
template <>
bool StyleGradient::IsOpaque() const {
if (IsLinear()) {
return GradientItemsAreOpaque(AsLinear().items.AsSpan());
}
if (IsRadial()) {
return GradientItemsAreOpaque(AsRadial().items.AsSpan());
}
return GradientItemsAreOpaque(AsConic().items.AsSpan());
}
template <>
bool StyleImage::IsOpaque() const {
if (IsImageSet()) {
return FinalImage().IsOpaque();
}
if (!IsComplete()) {
return false;
}
if (IsGradient()) {
return AsGradient()->IsOpaque();
}
if (IsElement()) {
return false;
}
MOZ_ASSERT(IsImageRequestType(), "unexpected image type");
MOZ_ASSERT(GetImageRequest(), "should've returned earlier above");
nsCOMPtr<imgIContainer> imageContainer;
GetImageRequest()->GetImage(getter_AddRefs(imageContainer));
MOZ_ASSERT(imageContainer, "IsComplete() said image container is ready");
return imageContainer->WillDrawOpaqueNow();
}
template <>
bool StyleImage::IsComplete() const {
switch (tag) {
case Tag::None:
return false;
case Tag::Gradient:
case Tag::Element:
return true;
case Tag::Url: {
if (!IsResolved()) {
return false;
}
imgRequestProxy* req = GetImageRequest();
if (!req) {
return false;
}
uint32_t status = imgIRequest::STATUS_ERROR;
return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
(status & imgIRequest::STATUS_SIZE_AVAILABLE) &&
(status & imgIRequest::STATUS_FRAME_COMPLETE);
}
case Tag::ImageSet:
return FinalImage().IsComplete();
// Bug 546052 cross-fade not yet implemented.
case Tag::CrossFade:
return true;
}
MOZ_ASSERT_UNREACHABLE("unexpected image type");
return false;
}
template <>
bool StyleImage::IsSizeAvailable() const {
switch (tag) {
case Tag::None:
return false;
case Tag::Gradient:
case Tag::Element:
return true;
case Tag::Url: {
imgRequestProxy* req = GetImageRequest();
if (!req) {
return false;
}
uint32_t status =