gecko-dev/layout/style/nsStyleUtil.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

418 строки
14 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: */
2012-05-21 15:12:37 +04:00
/* 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/. */
1998-07-23 22:04:24 +04:00
#include "nsStyleUtil.h"
#include "nsStyleConsts.h"
#include "mozilla/ExpandedPrincipal.h"
#include "mozilla/FontPropertyTypes.h"
#include "nsIContent.h"
#include "nsCSSProps.h"
#include "nsContentUtils.h"
#include "nsROCSSPrimitiveValue.h"
#include "nsStyleStruct.h"
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIURI.h"
#include "nsISupportsPrimitives.h"
#include "nsLayoutUtils.h"
#include "nsPrintfCString.h"
#include <cctype>
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 nsAString& aValueList,
const nsAString& 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) {
MOZ_ASSERT(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) {
// Escape U+0000 through U+001F and U+007F numerically.
aReturn.AppendPrintf("\\%x ", *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("\\%x ", *in);
++in;
}
for (; in != end; ++in) {
char16_t ch = *in;
if (ch == 0x00) {
aReturn.Append(char16_t(0xFFFD));
} else if (ch < 0x20 || 0x7F == ch) {
// Escape U+0000 through U+001F and U+007F numerically.
aReturn.AppendPrintf("\\%x ", *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);
}
}
}
/* static */
void nsStyleUtil::AppendBitmaskCSSValue(const nsCSSKTableEntry aTable[],
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::ValueToKeyword(mask, aTable), aResult);
aMaskedValue &= ~mask;
if (aMaskedValue) { // more left
aResult.Append(char16_t(' '));
}
}
}
MOZ_ASSERT(aMaskedValue == 0, "unexpected bit remaining in bitfield");
}
/* static */
void nsStyleUtil::AppendPaintOrderValue(uint8_t aValue, nsAString& aResult) {
Bug 895322 - Part 1: Replace the usages of MOZ_STATIC_ASSERT with C++11 static_assert; r=Waldo This patch was mostly generated by running the following scripts on the codebase, with some manual changes made afterwards: # static_assert.sh #!/bin/bash # Command to convert an NSPR integer type to the equivalent standard integer type function convert() { echo "Converting $1 to $2..." find . ! -wholename "*nsprpub*" \ ! -wholename "*security/nss*" \ ! -wholename "*/.hg*" \ ! -wholename "obj-ff-dbg*" \ ! -name nsXPCOMCID.h \ ! -name prtypes.h \ -type f \ \( -iname "*.cpp" \ -o -iname "*.h" \ -o -iname "*.cc" \ -o -iname "*.mm" \) | \ xargs -n 1 `dirname $0`/assert_replacer.py #sed -i -e "s/\b$1\b/$2/g" } convert MOZ_STATIC_ASSERT static_assert hg rev --no-backup mfbt/Assertions.h \ media/webrtc/signaling/src/sipcc/core/includes/ccapi.h \ modules/libmar/src/mar_private.h \ modules/libmar/src/mar.h # assert_replacer.py #!/usr/bin/python import sys import re pattern = re.compile(r"\bMOZ_STATIC_ASSERT\b") def replaceInPlace(fname): print fname f = open(fname, "rw+") lines = f.readlines() for i in range(0, len(lines)): while True: index = re.search(pattern, lines[i]) if index != None: index = index.start() lines[i] = lines[i][0:index] + "static_assert" + lines[i][index+len("MOZ_STATIC_ASSERT"):] for j in range(i + 1, len(lines)): if lines[j].find(" ", index) == index: lines[j] = lines[j][0:index] + lines[j][index+4:] else: break else: break f.seek(0, 0) f.truncate() f.write("".join(lines)) f.close() argc = len(sys.argv) for i in range(1, argc): replaceInPlace(sys.argv[i]) --HG-- extra : rebase_source : 4b4a4047d82f2c205b9fad8d56dfc3f1afc0b045
2013-07-18 21:59:53 +04:00
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.
Bug 895322 - Part 1: Replace the usages of MOZ_STATIC_ASSERT with C++11 static_assert; r=Waldo This patch was mostly generated by running the following scripts on the codebase, with some manual changes made afterwards: # static_assert.sh #!/bin/bash # Command to convert an NSPR integer type to the equivalent standard integer type function convert() { echo "Converting $1 to $2..." find . ! -wholename "*nsprpub*" \ ! -wholename "*security/nss*" \ ! -wholename "*/.hg*" \ ! -wholename "obj-ff-dbg*" \ ! -name nsXPCOMCID.h \ ! -name prtypes.h \ -type f \ \( -iname "*.cpp" \ -o -iname "*.h" \ -o -iname "*.cc" \ -o -iname "*.mm" \) | \ xargs -n 1 `dirname $0`/assert_replacer.py #sed -i -e "s/\b$1\b/$2/g" } convert MOZ_STATIC_ASSERT static_assert hg rev --no-backup mfbt/Assertions.h \ media/webrtc/signaling/src/sipcc/core/includes/ccapi.h \ modules/libmar/src/mar_private.h \ modules/libmar/src/mar.h # assert_replacer.py #!/usr/bin/python import sys import re pattern = re.compile(r"\bMOZ_STATIC_ASSERT\b") def replaceInPlace(fname): print fname f = open(fname, "rw+") lines = f.readlines() for i in range(0, len(lines)): while True: index = re.search(pattern, lines[i]) if index != None: index = index.start() lines[i] = lines[i][0:index] + "static_assert" + lines[i][index+len("MOZ_STATIC_ASSERT"):] for j in range(i + 1, len(lines)): if lines[j].find(" ", index) == index: lines[j] = lines[j][0:index] + lines[j][index+4:] else: break else: break f.seek(0, 0) f.truncate() f.write("".join(lines)) f.close() argc = len(sys.argv) for i in range(1, argc): replaceInPlace(sys.argv[i]) --HG-- extra : rebase_source : 4b4a4047d82f2c205b9fad8d56dfc3f1afc0b045
2013-07-18 21:59:53 +04:00
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:
MOZ_ASSERT_UNREACHABLE("unexpected paint-order component value");
}
aValue >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
}
}
/* 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 */
void nsStyleUtil::GetSerializedColorValue(nscolor aColor,
nsAString& aSerializedColor) {
MOZ_ASSERT(aSerializedColor.IsEmpty());
const bool hasAlpha = NS_GET_A(aColor) != 255;
if (hasAlpha) {
aSerializedColor.AppendLiteral("rgba(");
} else {
aSerializedColor.AppendLiteral("rgb(");
}
aSerializedColor.AppendInt(NS_GET_R(aColor));
aSerializedColor.AppendLiteral(", ");
aSerializedColor.AppendInt(NS_GET_G(aColor));
aSerializedColor.AppendLiteral(", ");
aSerializedColor.AppendInt(NS_GET_B(aColor));
if (hasAlpha) {
aSerializedColor.AppendLiteral(", ");
float alpha = nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor));
nsStyleUtil::AppendCSSNumber(alpha, aSerializedColor);
}
aSerializedColor.AppendLiteral(")");
}
/* static */
bool nsStyleUtil::IsSignificantChild(nsIContent* aChild,
bool aWhitespaceIsSignificant) {
bool isText = aChild->IsText();
if (!isText && !aChild->IsComment() && !aChild->IsProcessingInstruction()) {
return true;
}
return isText && aChild->TextLength() != 0 &&
(aWhitespaceIsSignificant || !aChild->TextIsOnlyWhitespace());
}
/* static */
bool nsStyleUtil::ThreadSafeIsSignificantChild(const nsIContent* aChild,
bool aWhitespaceIsSignificant) {
bool isText = aChild->IsText();
if (!isText && !aChild->IsComment() && !aChild->IsProcessingInstruction()) {
return true;
}
return isText && aChild->TextLength() != 0 &&
(aWhitespaceIsSignificant ||
!aChild->ThreadSafeTextIsOnlyWhitespace());
}
// 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 LengthPercentage& 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.LengthInCSSPixels() != 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.HasPercent() &&
(aCoord.Percentage() < 0.0f || aCoord.Percentage() > 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.horizontal) ||
ObjectPositionCoordMightCauseOverflow(objectPosistion.vertical)) {
return true;
}
return false;
}
/* static */
bool nsStyleUtil::CSPAllowsInlineStyle(
Element* aElement, dom::Document* aDocument,
nsIPrincipal* aTriggeringPrincipal, uint32_t aLineNumber,
uint32_t aColumnNumber, const nsAString& aStyleText, nsresult* aRv) {
nsresult rv;
if (aRv) {
*aRv = NS_OK;
}
nsCOMPtr<nsIContentSecurityPolicy> csp;
if (aTriggeringPrincipal && BasePrincipal::Cast(aTriggeringPrincipal)
->OverridesCSP(aDocument->NodePrincipal())) {
nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aTriggeringPrincipal);
if (ep) {
csp = ep->GetCsp();
}
} else {
csp = aDocument->GetCsp();
}
if (!csp) {
// No CSP --> the style is allowed
return true;
}
// query the nonce
nsAutoString nonce;
if (aElement && aElement->NodeInfo()->NameAtom() == nsGkAtoms::style) {
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce);
}
bool allowInlineStyle = true;
rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_STYLESHEET, nonce,
false, // aParserCreated only applies to scripts
aElement, nullptr, // nsICSPEventListener
aStyleText, aLineNumber, aColumnNumber,
&allowInlineStyle);
NS_ENSURE_SUCCESS(rv, false);
return allowInlineStyle;
}
void nsStyleUtil::AppendFontSlantStyle(const FontSlantStyle& aStyle,
nsAString& aOut) {
if (aStyle.IsNormal()) {
aOut.AppendLiteral("normal");
} else if (aStyle.IsItalic()) {
aOut.AppendLiteral("italic");
} else {
aOut.AppendLiteral("oblique");
auto angle = aStyle.ObliqueAngle();
if (angle != FontSlantStyle::kDefaultAngle) {
aOut.AppendLiteral(" ");
AppendCSSNumber(angle, aOut);
aOut.AppendLiteral("deg");
}
}
}