1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsStyleUtil.h"
7 #include "nsStyleConsts.h"
9 #include "nsIContent.h"
10 #include "nsReadableUtils.h"
11 #include "nsCSSProps.h"
12 #include "nsRuleNode.h"
14 using namespace mozilla
;
16 //------------------------------------------------------------------------------
17 // Font Algorithm Code
18 //------------------------------------------------------------------------------
20 // Compare two language strings
21 bool nsStyleUtil::DashMatchCompare(const nsAString
& aAttributeValue
,
22 const nsAString
& aSelectorValue
,
23 const nsStringComparator
& aComparator
)
26 uint32_t selectorLen
= aSelectorValue
.Length();
27 uint32_t attributeLen
= aAttributeValue
.Length();
28 if (selectorLen
> attributeLen
) {
32 nsAString::const_iterator iter
;
33 if (selectorLen
!= attributeLen
&&
34 *aAttributeValue
.BeginReading(iter
).advance(selectorLen
) !=
36 // to match, the aAttributeValue must have a dash after the end of
37 // the aSelectorValue's text (unless the aSelectorValue and the
38 // aAttributeValue have the same text)
42 result
= StringBeginsWith(aAttributeValue
, aSelectorValue
, aComparator
);
48 void nsStyleUtil::AppendEscapedCSSString(const nsAString
& aString
,
52 NS_PRECONDITION(quoteChar
== '\'' || quoteChar
== '"',
53 "CSS strings must be quoted with ' or \"");
54 aReturn
.Append(quoteChar
);
56 const PRUnichar
* in
= aString
.BeginReading();
57 const PRUnichar
* const end
= aString
.EndReading();
58 for (; in
!= end
; in
++) {
59 if (*in
< 0x20 || (*in
>= 0x7F && *in
< 0xA0)) {
60 // Escape U+0000 through U+001F and U+007F through U+009F numerically.
61 aReturn
.AppendPrintf("\\%hX ", *in
);
63 if (*in
== '"' || *in
== '\'' || *in
== '\\') {
64 // Escape backslash and quote characters symbolically.
65 // It's not technically necessary to escape the quote
66 // character that isn't being used to delimit the string,
67 // but we do it anyway because that makes testing simpler.
68 aReturn
.Append(PRUnichar('\\'));
74 aReturn
.Append(quoteChar
);
78 nsStyleUtil::AppendEscapedCSSIdent(const nsAString
& aIdent
, nsAString
& aReturn
)
80 // The relevant parts of the CSS grammar are:
81 // ident [-]?{nmstart}{nmchar}*
82 // nmstart [_a-z]|{nonascii}|{escape}
83 // nmchar [_a-z0-9-]|{nonascii}|{escape}
84 // nonascii [^\0-\177]
85 // escape {unicode}|\\[^\n\r\f0-9a-f]
86 // unicode \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
87 // from http://www.w3.org/TR/CSS21/syndata.html#tokenization
89 const PRUnichar
* in
= aIdent
.BeginReading();
90 const PRUnichar
* const end
= aIdent
.EndReading();
95 // A leading dash does not need to be escaped as long as it is not the
96 // *only* character in the identifier.
97 if (in
+ 1 != end
&& *in
== '-') {
98 aReturn
.Append(PRUnichar('-'));
102 // Escape a digit at the start (including after a dash),
103 // numerically. If we didn't escape it numerically, it would get
104 // interpreted as a numeric escape for the wrong character.
105 // A second dash immediately after a leading dash must also be
106 // escaped, but this may be done symbolically.
107 if (in
!= end
&& (*in
== '-' ||
108 ('0' <= *in
&& *in
<= '9'))) {
110 aReturn
.Append(PRUnichar('\\'));
111 aReturn
.Append(PRUnichar('-'));
113 aReturn
.AppendPrintf("\\%hX ", *in
);
118 for (; in
!= end
; ++in
) {
120 if (ch
< 0x20 || (0x7F <= ch
&& ch
< 0xA0)) {
121 // Escape U+0000 through U+001F and U+007F through U+009F numerically.
122 aReturn
.AppendPrintf("\\%hX ", *in
);
124 // Escape ASCII non-identifier printables as a backslash plus
127 ch
!= '_' && ch
!= '-' &&
128 (ch
< '0' || '9' < ch
) &&
129 (ch
< 'A' || 'Z' < ch
) &&
130 (ch
< 'a' || 'z' < ch
)) {
131 aReturn
.Append(PRUnichar('\\'));
139 nsStyleUtil::AppendBitmaskCSSValue(nsCSSProperty aProperty
,
140 int32_t aMaskedValue
,
145 for (int32_t mask
= aFirstMask
; mask
<= aLastMask
; mask
<<= 1) {
146 if (mask
& aMaskedValue
) {
147 AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty
, mask
),
149 aMaskedValue
&= ~mask
;
150 if (aMaskedValue
) { // more left
151 aResult
.Append(PRUnichar(' '));
155 NS_ABORT_IF_FALSE(aMaskedValue
== 0, "unexpected bit remaining in bitfield");
159 nsStyleUtil::AppendPaintOrderValue(uint8_t aValue
,
163 (NS_STYLE_PAINT_ORDER_BITWIDTH
* NS_STYLE_PAINT_ORDER_LAST_VALUE
<= 8,
164 "SVGStyleStruct::mPaintOrder and local variables not big enough");
166 if (aValue
== NS_STYLE_PAINT_ORDER_NORMAL
) {
167 aResult
.AppendLiteral("normal");
171 // Append the minimal value necessary for the given paint order.
172 MOZ_STATIC_ASSERT(NS_STYLE_PAINT_ORDER_LAST_VALUE
== 3,
173 "paint-order values added; check serialization");
175 // The following relies on the default order being the order of the
178 const uint8_t MASK
= (1 << NS_STYLE_PAINT_ORDER_BITWIDTH
) - 1;
180 uint32_t lastPositionToSerialize
= 0;
181 for (uint32_t position
= NS_STYLE_PAINT_ORDER_LAST_VALUE
- 1;
185 (aValue
>> (position
* NS_STYLE_PAINT_ORDER_BITWIDTH
)) & MASK
;
186 uint8_t earlierComponent
=
187 (aValue
>> ((position
- 1) * NS_STYLE_PAINT_ORDER_BITWIDTH
)) & MASK
;
188 if (component
< earlierComponent
) {
189 lastPositionToSerialize
= position
- 1;
194 for (uint32_t position
= 0; position
<= lastPositionToSerialize
; position
++) {
196 aResult
.AppendLiteral(" ");
198 uint8_t component
= aValue
& MASK
;
200 case NS_STYLE_PAINT_ORDER_FILL
:
201 aResult
.AppendLiteral("fill");
204 case NS_STYLE_PAINT_ORDER_STROKE
:
205 aResult
.AppendLiteral("stroke");
208 case NS_STYLE_PAINT_ORDER_MARKERS
:
209 aResult
.AppendLiteral("markers");
213 NS_NOTREACHED("unexpected paint-order component value");
215 aValue
>>= NS_STYLE_PAINT_ORDER_BITWIDTH
;
220 nsStyleUtil::AppendFontFeatureSettings(const nsTArray
<gfxFontFeature
>& aFeatures
,
223 for (uint32_t i
= 0, numFeat
= aFeatures
.Length(); i
< numFeat
; i
++) {
224 const gfxFontFeature
& feat
= aFeatures
[i
];
227 aResult
.AppendLiteral(", ");
233 tag
[1] = (feat
.mTag
>> 24) & 0xff;
234 tag
[2] = (feat
.mTag
>> 16) & 0xff;
235 tag
[3] = (feat
.mTag
>> 8) & 0xff;
236 tag
[4] = feat
.mTag
& 0xff;
239 aResult
.AppendASCII(tag
);
241 // output value, if necessary
242 if (feat
.mValue
== 0) {
244 aResult
.AppendLiteral(" off");
245 } else if (feat
.mValue
> 1) {
246 aResult
.AppendLiteral(" ");
247 aResult
.AppendInt(feat
.mValue
);
249 // else, omit value if 1, implied by default
254 nsStyleUtil::AppendFontFeatureSettings(const nsCSSValue
& aSrc
,
257 nsCSSUnit unit
= aSrc
.GetUnit();
259 if (unit
== eCSSUnit_Normal
) {
260 aResult
.AppendLiteral("normal");
264 NS_PRECONDITION(unit
== eCSSUnit_PairList
|| unit
== eCSSUnit_PairListDep
,
265 "improper value unit for font-feature-settings:");
267 nsTArray
<gfxFontFeature
> featureSettings
;
268 nsRuleNode::ComputeFontFeatures(aSrc
.GetPairListValue(), featureSettings
);
269 AppendFontFeatureSettings(featureSettings
, aResult
);
273 nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha
)
275 // Alpha values are expressed as decimals, so we should convert
276 // back, using as few decimal places as possible for
278 // First try two decimal places:
279 float rounded
= NS_roundf(float(aAlpha
) * 100.0f
/ 255.0f
) / 100.0f
;
280 if (FloatToColorComponent(rounded
) != aAlpha
) {
281 // Use three decimal places.
282 rounded
= NS_roundf(float(aAlpha
) * 1000.0f
/ 255.0f
) / 1000.0f
;
288 nsStyleUtil::IsSignificantChild(nsIContent
* aChild
, bool aTextIsSignificant
,
289 bool aWhitespaceIsSignificant
)
291 NS_ASSERTION(!aWhitespaceIsSignificant
|| aTextIsSignificant
,
292 "Nonsensical arguments");
294 bool isText
= aChild
->IsNodeOfType(nsINode::eTEXT
);
296 if (!isText
&& !aChild
->IsNodeOfType(nsINode::eCOMMENT
) &&
297 !aChild
->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION
)) {
301 return aTextIsSignificant
&& isText
&& aChild
->TextLength() != 0 &&
302 (aWhitespaceIsSignificant
||
303 !aChild
->TextIsOnlyWhitespace());