summaryrefslogtreecommitdiffstats
path: root/layout/style/nsStyleUtil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/nsStyleUtil.cpp')
-rw-r--r--layout/style/nsStyleUtil.cpp783
1 files changed, 783 insertions, 0 deletions
diff --git a/layout/style/nsStyleUtil.cpp b/layout/style/nsStyleUtil.cpp
new file mode 100644
index 000000000..840cd03c3
--- /dev/null
+++ b/layout/style/nsStyleUtil.cpp
@@ -0,0 +1,783 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsStyleUtil.h"
+#include "nsStyleConsts.h"
+
+#include "nsIContent.h"
+#include "nsCSSProps.h"
+#include "nsRuleNode.h"
+#include "nsROCSSPrimitiveValue.h"
+#include "nsStyleStruct.h"
+#include "nsIContentPolicy.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIURI.h"
+#include "nsPrintfCString.h"
+
+using namespace mozilla;
+
+//------------------------------------------------------------------------------
+// Font Algorithm Code
+//------------------------------------------------------------------------------
+
+// Compare two language strings
+bool nsStyleUtil::DashMatchCompare(const nsAString& aAttributeValue,
+ const nsAString& aSelectorValue,
+ const nsStringComparator& aComparator)
+{
+ bool result;
+ uint32_t selectorLen = aSelectorValue.Length();
+ uint32_t attributeLen = aAttributeValue.Length();
+ if (selectorLen > attributeLen) {
+ result = false;
+ }
+ else {
+ nsAString::const_iterator iter;
+ if (selectorLen != attributeLen &&
+ *aAttributeValue.BeginReading(iter).advance(selectorLen) !=
+ char16_t('-')) {
+ // to match, the aAttributeValue must have a dash after the end of
+ // the aSelectorValue's text (unless the aSelectorValue and the
+ // aAttributeValue have the same text)
+ result = false;
+ }
+ else {
+ result = StringBeginsWith(aAttributeValue, aSelectorValue, aComparator);
+ }
+ }
+ return result;
+}
+
+bool
+nsStyleUtil::ValueIncludes(const nsSubstring& aValueList,
+ const nsSubstring& aValue,
+ const nsStringComparator& aComparator)
+{
+ const char16_t *p = aValueList.BeginReading(),
+ *p_end = aValueList.EndReading();
+
+ while (p < p_end) {
+ // skip leading space
+ while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p))
+ ++p;
+
+ const char16_t *val_start = p;
+
+ // look for space or end
+ while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p))
+ ++p;
+
+ const char16_t *val_end = p;
+
+ if (val_start < val_end &&
+ aValue.Equals(Substring(val_start, val_end), aComparator))
+ return true;
+
+ ++p; // we know the next character is not whitespace
+ }
+ return false;
+}
+
+void nsStyleUtil::AppendEscapedCSSString(const nsAString& aString,
+ nsAString& aReturn,
+ char16_t quoteChar)
+{
+ NS_PRECONDITION(quoteChar == '\'' || quoteChar == '"',
+ "CSS strings must be quoted with ' or \"");
+ aReturn.Append(quoteChar);
+
+ const char16_t* in = aString.BeginReading();
+ const char16_t* const end = aString.EndReading();
+ for (; in != end; in++) {
+ if (*in < 0x20 || (*in >= 0x7F && *in < 0xA0)) {
+ // Escape U+0000 through U+001F and U+007F through U+009F numerically.
+ aReturn.AppendPrintf("\\%hx ", *in);
+ } else {
+ if (*in == '"' || *in == '\'' || *in == '\\') {
+ // Escape backslash and quote characters symbolically.
+ // It's not technically necessary to escape the quote
+ // character that isn't being used to delimit the string,
+ // but we do it anyway because that makes testing simpler.
+ aReturn.Append(char16_t('\\'));
+ }
+ aReturn.Append(*in);
+ }
+ }
+
+ aReturn.Append(quoteChar);
+}
+
+/* static */ void
+nsStyleUtil::AppendEscapedCSSIdent(const nsAString& aIdent, nsAString& aReturn)
+{
+ // The relevant parts of the CSS grammar are:
+ // ident ([-]?{nmstart}|[-][-]){nmchar}*
+ // nmstart [_a-z]|{nonascii}|{escape}
+ // nmchar [_a-z0-9-]|{nonascii}|{escape}
+ // nonascii [^\0-\177]
+ // escape {unicode}|\\[^\n\r\f0-9a-f]
+ // unicode \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
+ // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
+ // modified for idents by
+ // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
+ // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
+
+ const char16_t* in = aIdent.BeginReading();
+ const char16_t* const end = aIdent.EndReading();
+
+ if (in == end)
+ return;
+
+ // A leading dash does not need to be escaped as long as it is not the
+ // *only* character in the identifier.
+ if (*in == '-') {
+ if (in + 1 == end) {
+ aReturn.Append(char16_t('\\'));
+ aReturn.Append(char16_t('-'));
+ return;
+ }
+
+ aReturn.Append(char16_t('-'));
+ ++in;
+ }
+
+ // Escape a digit at the start (including after a dash),
+ // numerically. If we didn't escape it numerically, it would get
+ // interpreted as a numeric escape for the wrong character.
+ if (in != end && ('0' <= *in && *in <= '9')) {
+ aReturn.AppendPrintf("\\%hx ", *in);
+ ++in;
+ }
+
+ for (; in != end; ++in) {
+ char16_t ch = *in;
+ if (ch == 0x00) {
+ aReturn.Append(char16_t(0xFFFD));
+ } else if (ch < 0x20 || (0x7F <= ch && ch < 0xA0)) {
+ // Escape U+0000 through U+001F and U+007F through U+009F numerically.
+ aReturn.AppendPrintf("\\%hx ", *in);
+ } else {
+ // Escape ASCII non-identifier printables as a backslash plus
+ // the character.
+ if (ch < 0x7F &&
+ ch != '_' && ch != '-' &&
+ (ch < '0' || '9' < ch) &&
+ (ch < 'A' || 'Z' < ch) &&
+ (ch < 'a' || 'z' < ch)) {
+ aReturn.Append(char16_t('\\'));
+ }
+ aReturn.Append(ch);
+ }
+ }
+}
+
+// unquoted family names must be a sequence of idents
+// so escape any parts that require escaping
+static void
+AppendUnquotedFamilyName(const nsAString& aFamilyName, nsAString& aResult)
+{
+ const char16_t *p, *p_end;
+ aFamilyName.BeginReading(p);
+ aFamilyName.EndReading(p_end);
+
+ bool moreThanOne = false;
+ while (p < p_end) {
+ const char16_t* identStart = p;
+ while (++p != p_end && *p != ' ')
+ /* nothing */ ;
+
+ nsDependentSubstring ident(identStart, p);
+ if (!ident.IsEmpty()) {
+ if (moreThanOne) {
+ aResult.Append(' ');
+ }
+ nsStyleUtil::AppendEscapedCSSIdent(ident, aResult);
+ moreThanOne = true;
+ }
+
+ ++p;
+ }
+}
+
+/* static */ void
+nsStyleUtil::AppendEscapedCSSFontFamilyList(
+ const mozilla::FontFamilyList& aFamilyList,
+ nsAString& aResult)
+{
+ const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
+ size_t i, len = fontlist.Length();
+ for (i = 0; i < len; i++) {
+ if (i != 0) {
+ aResult.Append(',');
+ }
+ const FontFamilyName& name = fontlist[i];
+ switch (name.mType) {
+ case eFamily_named:
+ AppendUnquotedFamilyName(name.mName, aResult);
+ break;
+ case eFamily_named_quoted:
+ AppendEscapedCSSString(name.mName, aResult);
+ break;
+ default:
+ name.AppendToString(aResult);
+ }
+ }
+}
+
+
+/* static */ void
+nsStyleUtil::AppendBitmaskCSSValue(nsCSSPropertyID aProperty,
+ int32_t aMaskedValue,
+ int32_t aFirstMask,
+ int32_t aLastMask,
+ nsAString& aResult)
+{
+ for (int32_t mask = aFirstMask; mask <= aLastMask; mask <<= 1) {
+ if (mask & aMaskedValue) {
+ AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, mask),
+ aResult);
+ aMaskedValue &= ~mask;
+ if (aMaskedValue) { // more left
+ aResult.Append(char16_t(' '));
+ }
+ }
+ }
+ MOZ_ASSERT(aMaskedValue == 0, "unexpected bit remaining in bitfield");
+}
+
+/* static */ void
+nsStyleUtil::AppendAngleValue(const nsStyleCoord& aAngle, nsAString& aResult)
+{
+ MOZ_ASSERT(aAngle.IsAngleValue(), "Should have angle value");
+
+ // Append number.
+ AppendCSSNumber(aAngle.GetAngleValue(), aResult);
+
+ // Append unit.
+ switch (aAngle.GetUnit()) {
+ case eStyleUnit_Degree: aResult.AppendLiteral("deg"); break;
+ case eStyleUnit_Grad: aResult.AppendLiteral("grad"); break;
+ case eStyleUnit_Radian: aResult.AppendLiteral("rad"); break;
+ case eStyleUnit_Turn: aResult.AppendLiteral("turn"); break;
+ default: NS_NOTREACHED("unrecognized angle unit");
+ }
+}
+
+/* static */ void
+nsStyleUtil::AppendPaintOrderValue(uint8_t aValue,
+ nsAString& aResult)
+{
+ static_assert
+ (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8,
+ "SVGStyleStruct::mPaintOrder and local variables not big enough");
+
+ if (aValue == NS_STYLE_PAINT_ORDER_NORMAL) {
+ aResult.AppendLiteral("normal");
+ return;
+ }
+
+ // Append the minimal value necessary for the given paint order.
+ static_assert(NS_STYLE_PAINT_ORDER_LAST_VALUE == 3,
+ "paint-order values added; check serialization");
+
+ // The following relies on the default order being the order of the
+ // constant values.
+
+ const uint8_t MASK = (1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1;
+
+ uint32_t lastPositionToSerialize = 0;
+ for (uint32_t position = NS_STYLE_PAINT_ORDER_LAST_VALUE - 1;
+ position > 0;
+ position--) {
+ uint8_t component =
+ (aValue >> (position * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK;
+ uint8_t earlierComponent =
+ (aValue >> ((position - 1) * NS_STYLE_PAINT_ORDER_BITWIDTH)) & MASK;
+ if (component < earlierComponent) {
+ lastPositionToSerialize = position - 1;
+ break;
+ }
+ }
+
+ for (uint32_t position = 0; position <= lastPositionToSerialize; position++) {
+ if (position > 0) {
+ aResult.Append(' ');
+ }
+ uint8_t component = aValue & MASK;
+ switch (component) {
+ case NS_STYLE_PAINT_ORDER_FILL:
+ aResult.AppendLiteral("fill");
+ break;
+
+ case NS_STYLE_PAINT_ORDER_STROKE:
+ aResult.AppendLiteral("stroke");
+ break;
+
+ case NS_STYLE_PAINT_ORDER_MARKERS:
+ aResult.AppendLiteral("markers");
+ break;
+
+ default:
+ NS_NOTREACHED("unexpected paint-order component value");
+ }
+ aValue >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
+ }
+}
+
+/* static */ void
+nsStyleUtil::AppendFontFeatureSettings(const nsTArray<gfxFontFeature>& aFeatures,
+ nsAString& aResult)
+{
+ for (uint32_t i = 0, numFeat = aFeatures.Length(); i < numFeat; i++) {
+ const gfxFontFeature& feat = aFeatures[i];
+
+ if (i != 0) {
+ aResult.AppendLiteral(", ");
+ }
+
+ // output tag
+ char tag[7];
+ tag[0] = '"';
+ tag[1] = (feat.mTag >> 24) & 0xff;
+ tag[2] = (feat.mTag >> 16) & 0xff;
+ tag[3] = (feat.mTag >> 8) & 0xff;
+ tag[4] = feat.mTag & 0xff;
+ tag[5] = '"';
+ tag[6] = 0;
+ aResult.AppendASCII(tag);
+
+ // output value, if necessary
+ if (feat.mValue == 0) {
+ // 0 ==> off
+ aResult.AppendLiteral(" off");
+ } else if (feat.mValue > 1) {
+ aResult.Append(' ');
+ aResult.AppendInt(feat.mValue);
+ }
+ // else, omit value if 1, implied by default
+ }
+}
+
+/* static */ void
+nsStyleUtil::AppendFontFeatureSettings(const nsCSSValue& aSrc,
+ nsAString& aResult)
+{
+ nsCSSUnit unit = aSrc.GetUnit();
+
+ if (unit == eCSSUnit_Normal) {
+ aResult.AppendLiteral("normal");
+ return;
+ }
+
+ NS_PRECONDITION(unit == eCSSUnit_PairList || unit == eCSSUnit_PairListDep,
+ "improper value unit for font-feature-settings:");
+
+ nsTArray<gfxFontFeature> featureSettings;
+ nsRuleNode::ComputeFontFeatures(aSrc.GetPairListValue(), featureSettings);
+ AppendFontFeatureSettings(featureSettings, aResult);
+}
+
+/* static */ void
+nsStyleUtil::GetFunctionalAlternatesName(int32_t aFeature,
+ nsAString& aFeatureName)
+{
+ aFeatureName.Truncate();
+ nsCSSKeyword key =
+ nsCSSProps::ValueToKeywordEnum(aFeature,
+ nsCSSProps::kFontVariantAlternatesFuncsKTable);
+
+ NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "bad alternate feature type");
+ AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(key), aFeatureName);
+}
+
+/* static */ void
+nsStyleUtil::SerializeFunctionalAlternates(
+ const nsTArray<gfxAlternateValue>& aAlternates,
+ nsAString& aResult)
+{
+ nsAutoString funcName, funcParams;
+ uint32_t numValues = aAlternates.Length();
+
+ uint32_t feature = 0;
+ for (uint32_t i = 0; i < numValues; i++) {
+ const gfxAlternateValue& v = aAlternates.ElementAt(i);
+ if (feature != v.alternate) {
+ feature = v.alternate;
+ if (!funcName.IsEmpty() && !funcParams.IsEmpty()) {
+ if (!aResult.IsEmpty()) {
+ aResult.Append(char16_t(' '));
+ }
+
+ // append the previous functional value
+ aResult.Append(funcName);
+ aResult.Append(char16_t('('));
+ aResult.Append(funcParams);
+ aResult.Append(char16_t(')'));
+ }
+
+ // function name
+ GetFunctionalAlternatesName(v.alternate, funcName);
+ NS_ASSERTION(!funcName.IsEmpty(), "unknown property value name");
+
+ // function params
+ funcParams.Truncate();
+ AppendEscapedCSSIdent(v.value, funcParams);
+ } else {
+ if (!funcParams.IsEmpty()) {
+ funcParams.AppendLiteral(", ");
+ }
+ AppendEscapedCSSIdent(v.value, funcParams);
+ }
+ }
+
+ // append the previous functional value
+ if (!funcName.IsEmpty() && !funcParams.IsEmpty()) {
+ if (!aResult.IsEmpty()) {
+ aResult.Append(char16_t(' '));
+ }
+
+ aResult.Append(funcName);
+ aResult.Append(char16_t('('));
+ aResult.Append(funcParams);
+ aResult.Append(char16_t(')'));
+ }
+}
+
+/* static */ void
+nsStyleUtil::ComputeFunctionalAlternates(const nsCSSValueList* aList,
+ nsTArray<gfxAlternateValue>& aAlternateValues)
+{
+ gfxAlternateValue v;
+
+ aAlternateValues.Clear();
+ for (const nsCSSValueList* curr = aList; curr != nullptr; curr = curr->mNext) {
+ // list contains function units
+ if (curr->mValue.GetUnit() != eCSSUnit_Function) {
+ continue;
+ }
+
+ // element 0 is the propval in ident form
+ const nsCSSValue::Array *func = curr->mValue.GetArrayValue();
+
+ // lookup propval
+ nsCSSKeyword key = func->Item(0).GetKeywordValue();
+ NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "unknown alternate property value");
+
+ int32_t alternate;
+ if (key == eCSSKeyword_UNKNOWN ||
+ !nsCSSProps::FindKeyword(key,
+ nsCSSProps::kFontVariantAlternatesFuncsKTable,
+ alternate)) {
+ NS_NOTREACHED("keyword not a font-variant-alternates value");
+ continue;
+ }
+ v.alternate = alternate;
+
+ // other elements are the idents associated with the propval
+ // append one alternate value for each one
+ uint32_t numElems = func->Count();
+ for (uint32_t i = 1; i < numElems; i++) {
+ const nsCSSValue& value = func->Item(i);
+ NS_ASSERTION(value.GetUnit() == eCSSUnit_Ident,
+ "weird unit found in variant alternate");
+ if (value.GetUnit() != eCSSUnit_Ident) {
+ continue;
+ }
+ value.GetStringValue(v.value);
+ aAlternateValues.AppendElement(v);
+ }
+ }
+}
+
+static void
+AppendSerializedUnicodePoint(uint32_t aCode, nsACString& aBuf)
+{
+ aBuf.Append(nsPrintfCString("%0X", aCode));
+}
+
+// A unicode-range: descriptor is represented as an array of integers,
+// to be interpreted as a sequence of pairs: min max min max ...
+// It is in source order. (Possibly it should be sorted and overlaps
+// consolidated, but right now we don't do that.)
+/* static */ void
+nsStyleUtil::AppendUnicodeRange(const nsCSSValue& aValue, nsAString& aResult)
+{
+ NS_PRECONDITION(aValue.GetUnit() == eCSSUnit_Null ||
+ aValue.GetUnit() == eCSSUnit_Array,
+ "improper value unit for unicode-range:");
+ aResult.Truncate();
+ if (aValue.GetUnit() != eCSSUnit_Array)
+ return;
+
+ nsCSSValue::Array const & sources = *aValue.GetArrayValue();
+ nsAutoCString buf;
+
+ MOZ_ASSERT(sources.Count() % 2 == 0,
+ "odd number of entries in a unicode-range: array");
+
+ for (uint32_t i = 0; i < sources.Count(); i += 2) {
+ uint32_t min = sources[i].GetIntValue();
+ uint32_t max = sources[i+1].GetIntValue();
+
+ // We don't try to replicate the U+XX?? notation.
+ buf.AppendLiteral("U+");
+ AppendSerializedUnicodePoint(min, buf);
+
+ if (min != max) {
+ buf.Append('-');
+ AppendSerializedUnicodePoint(max, buf);
+ }
+ buf.AppendLiteral(", ");
+ }
+ buf.Truncate(buf.Length() - 2); // remove the last comma-space
+ CopyASCIItoUTF16(buf, aResult);
+}
+
+/* static */ void
+nsStyleUtil::AppendSerializedFontSrc(const nsCSSValue& aValue,
+ nsAString& aResult)
+{
+ // A src: descriptor is represented as an array value; each entry in
+ // the array can be eCSSUnit_URL, eCSSUnit_Local_Font, or
+ // eCSSUnit_Font_Format. Blocks of eCSSUnit_Font_Format may appear
+ // only after one of the first two. (css3-fonts only contemplates
+ // annotating URLs with formats, but we handle the general case.)
+
+ NS_PRECONDITION(aValue.GetUnit() == eCSSUnit_Array,
+ "improper value unit for src:");
+
+ const nsCSSValue::Array& sources = *aValue.GetArrayValue();
+ size_t i = 0;
+
+ while (i < sources.Count()) {
+ nsAutoString formats;
+
+ if (sources[i].GetUnit() == eCSSUnit_URL) {
+ aResult.AppendLiteral("url(");
+ nsDependentString url(sources[i].GetOriginalURLValue());
+ nsStyleUtil::AppendEscapedCSSString(url, aResult);
+ aResult.Append(')');
+ } else if (sources[i].GetUnit() == eCSSUnit_Local_Font) {
+ aResult.AppendLiteral("local(");
+ nsDependentString local(sources[i].GetStringBufferValue());
+ nsStyleUtil::AppendEscapedCSSString(local, aResult);
+ aResult.Append(')');
+ } else {
+ NS_NOTREACHED("entry in src: descriptor with improper unit");
+ i++;
+ continue;
+ }
+
+ i++;
+ formats.Truncate();
+ while (i < sources.Count() &&
+ sources[i].GetUnit() == eCSSUnit_Font_Format) {
+ formats.Append('"');
+ formats.Append(sources[i].GetStringBufferValue());
+ formats.AppendLiteral("\", ");
+ i++;
+ }
+ if (formats.Length() > 0) {
+ formats.Truncate(formats.Length() - 2); // remove the last comma
+ aResult.AppendLiteral(" format(");
+ aResult.Append(formats);
+ aResult.Append(')');
+ }
+ aResult.AppendLiteral(", ");
+ }
+ aResult.Truncate(aResult.Length() - 2); // remove the last comma-space
+}
+
+/* static */ void
+nsStyleUtil::AppendStepsTimingFunction(nsTimingFunction::Type aType,
+ uint32_t aSteps,
+ nsAString& aResult)
+{
+ MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
+ aType == nsTimingFunction::Type::StepEnd);
+
+ aResult.AppendLiteral("steps(");
+ aResult.AppendInt(aSteps);
+ if (aType == nsTimingFunction::Type::StepStart) {
+ aResult.AppendLiteral(", start)");
+ } else {
+ aResult.AppendLiteral(")");
+ }
+}
+
+/* static */ void
+nsStyleUtil::AppendCubicBezierTimingFunction(float aX1, float aY1,
+ float aX2, float aY2,
+ nsAString& aResult)
+{
+ // set the value from the cubic-bezier control points
+ // (We could try to regenerate the keywords if we want.)
+ aResult.AppendLiteral("cubic-bezier(");
+ aResult.AppendFloat(aX1);
+ aResult.AppendLiteral(", ");
+ aResult.AppendFloat(aY1);
+ aResult.AppendLiteral(", ");
+ aResult.AppendFloat(aX2);
+ aResult.AppendLiteral(", ");
+ aResult.AppendFloat(aY2);
+ aResult.Append(')');
+}
+
+/* static */ void
+nsStyleUtil::AppendCubicBezierKeywordTimingFunction(
+ nsTimingFunction::Type aType,
+ nsAString& aResult)
+{
+ switch (aType) {
+ case nsTimingFunction::Type::Ease:
+ case nsTimingFunction::Type::Linear:
+ case nsTimingFunction::Type::EaseIn:
+ case nsTimingFunction::Type::EaseOut:
+ case nsTimingFunction::Type::EaseInOut: {
+ nsCSSKeyword keyword = nsCSSProps::ValueToKeywordEnum(
+ static_cast<int32_t>(aType),
+ nsCSSProps::kTransitionTimingFunctionKTable);
+ AppendASCIItoUTF16(nsCSSKeywords::GetStringValue(keyword),
+ aResult);
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected aType");
+ break;
+ }
+}
+
+/* static */ float
+nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha)
+{
+ // Alpha values are expressed as decimals, so we should convert
+ // back, using as few decimal places as possible for
+ // round-tripping.
+ // First try two decimal places:
+ float rounded = NS_roundf(float(aAlpha) * 100.0f / 255.0f) / 100.0f;
+ if (FloatToColorComponent(rounded) != aAlpha) {
+ // Use three decimal places.
+ rounded = NS_roundf(float(aAlpha) * 1000.0f / 255.0f) / 1000.0f;
+ }
+ return rounded;
+}
+
+/* static */ bool
+nsStyleUtil::IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
+ bool aWhitespaceIsSignificant)
+{
+ NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant,
+ "Nonsensical arguments");
+
+ bool isText = aChild->IsNodeOfType(nsINode::eTEXT);
+
+ if (!isText && !aChild->IsNodeOfType(nsINode::eCOMMENT) &&
+ !aChild->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
+ return true;
+ }
+
+ return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
+ (aWhitespaceIsSignificant ||
+ !aChild->TextIsOnlyWhitespace());
+}
+
+// For a replaced element whose concrete object size is no larger than the
+// element's content-box, this method checks whether the given
+// "object-position" coordinate might cause overflow in its dimension.
+static bool
+ObjectPositionCoordMightCauseOverflow(const Position::Coord& aCoord)
+{
+ // Any nonzero length in "object-position" can push us to overflow
+ // (particularly if our concrete object size is exactly the same size as the
+ // replaced element's content-box).
+ if (aCoord.mLength != 0) {
+ return true;
+ }
+
+ // Percentages are interpreted as a fraction of the extra space. So,
+ // percentages in the 0-100% range are safe, but values outside of that
+ // range could cause overflow.
+ if (aCoord.mHasPercent &&
+ (aCoord.mPercent < 0.0f || aCoord.mPercent > 1.0f)) {
+ return true;
+ }
+ return false;
+}
+
+
+/* static */ bool
+nsStyleUtil::ObjectPropsMightCauseOverflow(const nsStylePosition* aStylePos)
+{
+ auto objectFit = aStylePos->mObjectFit;
+
+ // "object-fit: cover" & "object-fit: none" can give us a render rect that's
+ // larger than our container element's content-box.
+ if (objectFit == NS_STYLE_OBJECT_FIT_COVER ||
+ objectFit == NS_STYLE_OBJECT_FIT_NONE) {
+ return true;
+ }
+ // (All other object-fit values produce a concrete object size that's no larger
+ // than the constraint region.)
+
+ // Check each of our "object-position" coords to see if it could cause
+ // overflow in its dimension:
+ const Position& objectPosistion = aStylePos->mObjectPosition;
+ if (ObjectPositionCoordMightCauseOverflow(objectPosistion.mXPosition) ||
+ ObjectPositionCoordMightCauseOverflow(objectPosistion.mYPosition)) {
+ return true;
+ }
+
+ return false;
+}
+
+
+/* static */ bool
+nsStyleUtil::CSPAllowsInlineStyle(nsIContent* aContent,
+ nsIPrincipal* aPrincipal,
+ nsIURI* aSourceURI,
+ uint32_t aLineNumber,
+ const nsSubstring& aStyleText,
+ nsresult* aRv)
+{
+ nsresult rv;
+
+ if (aRv) {
+ *aRv = NS_OK;
+ }
+
+ MOZ_ASSERT(!aContent || aContent->NodeInfo()->NameAtom() == nsGkAtoms::style,
+ "aContent passed to CSPAllowsInlineStyle "
+ "for an element that is not <style>");
+
+ nsCOMPtr<nsIContentSecurityPolicy> csp;
+ rv = aPrincipal->GetCsp(getter_AddRefs(csp));
+
+ if (NS_FAILED(rv)) {
+ if (aRv)
+ *aRv = rv;
+ return false;
+ }
+
+ if (!csp) {
+ // No CSP --> the style is allowed
+ return true;
+ }
+
+ // query the nonce
+ nsAutoString nonce;
+ if (aContent) {
+ aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce);
+ }
+
+ bool allowInlineStyle = true;
+ rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_STYLESHEET,
+ nonce,
+ false, // aParserCreated only applies to scripts
+ aStyleText, aLineNumber,
+ &allowInlineStyle);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return allowInlineStyle;
+}