1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 #include "nsStyleUtil.h"
41 #include "nsStyleConsts.h"
43 #include "nsGkAtoms.h"
44 #include "nsIContent.h"
45 #include "nsIDocument.h"
46 #include "nsINameSpaceManager.h"
48 #include "nsNetUtil.h"
49 #include "nsReadableUtils.h"
50 #include "nsContentUtils.h"
51 #include "nsTextFormatter.h"
52 #include "nsCSSProps.h"
54 // XXX This is here because nsCachedStyleData is accessed outside of
55 // the content module; e.g., by nsCSSFrameConstructor.
56 #include "nsRuleNode.h"
58 nsCachedStyleData::StyleStructInfo
59 nsCachedStyleData::gInfo
[] = {
61 #define STYLE_STRUCT_INHERITED(name, checkdata_cb, ctor_args) \
62 { offsetof(nsCachedStyleData, mInheritedData), \
63 offsetof(nsInheritedStyleData, m##name##Data), \
65 #define STYLE_STRUCT_RESET(name, checkdata_cb, ctor_args) \
66 { offsetof(nsCachedStyleData, mResetData), \
67 offsetof(nsResetStyleData, m##name##Data), \
70 #include "nsStyleStructList.h"
72 #undef STYLE_STRUCT_INHERITED
73 #undef STYLE_STRUCT_RESET
78 #define POSITIVE_SCALE_FACTOR 1.10 /* 10% */
79 #define NEGATIVE_SCALE_FACTOR .90 /* 10% */
82 //------------------------------------------------------------------------------
84 //------------------------------------------------------------------------------
87 * Return the scaling percentage given a font scaler
88 * Lifted from layutil.c
90 float nsStyleUtil::GetScalingFactor(PRInt32 aScaler
)
98 mult
= NEGATIVE_SCALE_FACTOR
;
102 mult
= POSITIVE_SCALE_FACTOR
;
105 /* use the percentage scaling factor to the power of the pref */
114 //------------------------------------------------------------------------------
115 // Font Algorithm Code
116 //------------------------------------------------------------------------------
119 nsStyleUtil::CalcFontPointSize(PRInt32 aHTMLSize
, PRInt32 aBasePointSize
,
120 float aScalingFactor
, nsPresContext
* aPresContext
,
121 nsFontSizeType aFontSizeType
)
123 #define sFontSizeTableMin 9
124 #define sFontSizeTableMax 16
126 // This table seems to be the one used by MacIE5. We hope its adoption in Mozilla
127 // and eventually in WinIE5.5 will help to establish a standard rendering across
128 // platforms and browsers. For now, it is used only in Strict mode. More can be read
129 // in the document written by Todd Farhner at:
130 // http://style.verso.com/font_size_intervals/altintervals.html
132 static PRInt32 sStrictFontSizeTable
[sFontSizeTableMax
- sFontSizeTableMin
+ 1][8] =
134 { 9, 9, 9, 9, 11, 14, 18, 27},
135 { 9, 9, 9, 10, 12, 15, 20, 30},
136 { 9, 9, 10, 11, 13, 17, 22, 33},
137 { 9, 9, 10, 12, 14, 18, 24, 36},
138 { 9, 10, 12, 13, 16, 20, 26, 39},
139 { 9, 10, 12, 14, 17, 21, 28, 42},
140 { 9, 10, 13, 15, 18, 23, 30, 45},
141 { 9, 10, 13, 16, 18, 24, 32, 48}
143 // HTML 1 2 3 4 5 6 7
144 // CSS xxs xs s m l xl xxl
148 //------------------------------------------------------------
150 // This table gives us compatibility with WinNav4 for the default fonts only.
151 // In WinNav4, the default fonts were:
153 // Times/12pt == Times/16px at 96ppi
154 // Courier/10pt == Courier/13px at 96ppi
156 // The 2 lines below marked "anchored" have the exact pixel sizes used by
157 // WinNav4 for Times/12pt and Courier/10pt at 96ppi. As you can see, the
158 // HTML size 3 (user pref) for those 2 anchored lines is 13px and 16px.
160 // All values other than the anchored values were filled in by hand, never
161 // going below 9px, and maintaining a "diagonal" relationship. See for
162 // example the 13s -- they follow a diagonal line through the table.
164 static PRInt32 sQuirksFontSizeTable
[sFontSizeTableMax
- sFontSizeTableMin
+ 1][8] =
166 { 9, 9, 9, 9, 11, 14, 18, 28 },
167 { 9, 9, 9, 10, 12, 15, 20, 31 },
168 { 9, 9, 9, 11, 13, 17, 22, 34 },
169 { 9, 9, 10, 12, 14, 18, 24, 37 },
170 { 9, 9, 10, 13, 16, 20, 26, 40 }, // anchored (13)
171 { 9, 9, 11, 14, 17, 21, 28, 42 },
172 { 9, 10, 12, 15, 17, 23, 30, 45 },
173 { 9, 10, 13, 16, 18, 24, 32, 48 } // anchored (16)
175 // HTML 1 2 3 4 5 6 7
176 // CSS xxs xs s m l xl xxl
182 // These are the exact pixel values used by WinIE5 at 96ppi.
184 { ?, 8, 11, 12, 13, 16, 21, 32 }, // smallest
185 { ?, 9, 12, 13, 16, 21, 27, 40 }, // smaller
186 { ?, 10, 13, 16, 18, 24, 32, 48 }, // medium
187 { ?, 13, 16, 19, 21, 27, 37, ?? }, // larger
188 { ?, 16, 19, 21, 24, 32, 43, ?? } // largest
190 // HTML 1 2 3 4 5 6 7
191 // CSS ? ? ? ? ? ? ? ?
193 // (CSS not tested yet.)
197 static PRInt32 sFontSizeFactors
[8] = { 60,75,89,100,120,150,200,300 };
199 static PRInt32 sCSSColumns
[7] = {0, 1, 2, 3, 4, 5, 6}; // xxs...xxl
200 static PRInt32 sHTMLColumns
[7] = {1, 2, 3, 4, 5, 6, 7}; // 1...7
204 if (aFontSizeType
== eFontSize_HTML
) {
205 aHTMLSize
--; // input as 1-7
210 else if (aHTMLSize
> 6)
214 switch (aFontSizeType
)
216 case eFontSize_HTML
: column
= sHTMLColumns
; break;
217 case eFontSize_CSS
: column
= sCSSColumns
; break;
220 // Make special call specifically for fonts (needed PrintPreview)
221 PRInt32 fontSize
= nsPresContext::AppUnitsToIntCSSPixels(aBasePointSize
);
223 if ((fontSize
>= sFontSizeTableMin
) && (fontSize
<= sFontSizeTableMax
))
225 PRInt32 row
= fontSize
- sFontSizeTableMin
;
227 if (aPresContext
->CompatibilityMode() == eCompatibility_NavQuirks
) {
228 dFontSize
= nsPresContext::CSSPixelsToAppUnits(sQuirksFontSizeTable
[row
][column
[aHTMLSize
]]);
230 dFontSize
= nsPresContext::CSSPixelsToAppUnits(sStrictFontSizeTable
[row
][column
[aHTMLSize
]]);
235 PRInt32 factor
= sFontSizeFactors
[column
[aHTMLSize
]];
236 dFontSize
= (factor
* aBasePointSize
) / 100;
239 dFontSize
*= aScalingFactor
;
241 if (1.0 < dFontSize
) {
242 return (nscoord
)dFontSize
;
248 //------------------------------------------------------------------------------
250 //------------------------------------------------------------------------------
252 nscoord
nsStyleUtil::FindNextSmallerFontSize(nscoord aFontSize
, PRInt32 aBasePointSize
,
253 float aScalingFactor
, nsPresContext
* aPresContext
,
254 nsFontSizeType aFontSizeType
)
259 float relativePosition
;
261 nscoord indexFontSize
= aFontSize
; // XXX initialize to quell a spurious gcc3.2 warning
262 nscoord smallestIndexFontSize
;
263 nscoord largestIndexFontSize
;
264 nscoord smallerIndexFontSize
;
265 nscoord largerIndexFontSize
;
267 nscoord onePx
= nsPresContext::CSSPixelsToAppUnits(1);
269 if (aFontSizeType
== eFontSize_HTML
) {
277 smallestIndexFontSize
= CalcFontPointSize(indexMin
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
278 largestIndexFontSize
= CalcFontPointSize(indexMax
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
279 if (aFontSize
> smallestIndexFontSize
) {
280 if (aFontSize
< NSToCoordRound(float(largestIndexFontSize
) * 1.5)) { // smaller will be in HTML table
281 // find largest index smaller than current
282 for (index
= indexMax
; index
>= indexMin
; index
--) {
283 indexFontSize
= CalcFontPointSize(index
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
284 if (indexFontSize
< aFontSize
)
287 // set up points beyond table for interpolation purposes
288 if (indexFontSize
== smallestIndexFontSize
) {
289 smallerIndexFontSize
= indexFontSize
- onePx
;
290 largerIndexFontSize
= CalcFontPointSize(index
+1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
291 } else if (indexFontSize
== largestIndexFontSize
) {
292 smallerIndexFontSize
= CalcFontPointSize(index
-1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
293 largerIndexFontSize
= NSToCoordRound(float(largestIndexFontSize
) * 1.5);
295 smallerIndexFontSize
= CalcFontPointSize(index
-1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
296 largerIndexFontSize
= CalcFontPointSize(index
+1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
298 // compute the relative position of the parent size between the two closest indexed sizes
299 relativePosition
= float(aFontSize
- indexFontSize
) / float(largerIndexFontSize
- indexFontSize
);
300 // set the new size to have the same relative position between the next smallest two indexed sizes
301 smallerSize
= smallerIndexFontSize
+ NSToCoordRound(relativePosition
* (indexFontSize
- smallerIndexFontSize
));
303 else { // larger than HTML table, drop by 33%
304 smallerSize
= NSToCoordRound(float(aFontSize
) / 1.5);
307 else { // smaller than HTML table, drop by 1px
308 smallerSize
= NS_MAX(aFontSize
- onePx
, onePx
);
313 //------------------------------------------------------------------------------
315 //------------------------------------------------------------------------------
317 nscoord
nsStyleUtil::FindNextLargerFontSize(nscoord aFontSize
, PRInt32 aBasePointSize
,
318 float aScalingFactor
, nsPresContext
* aPresContext
,
319 nsFontSizeType aFontSizeType
)
324 float relativePosition
;
326 nscoord indexFontSize
= aFontSize
; // XXX initialize to quell a spurious gcc3.2 warning
327 nscoord smallestIndexFontSize
;
328 nscoord largestIndexFontSize
;
329 nscoord smallerIndexFontSize
;
330 nscoord largerIndexFontSize
;
332 nscoord onePx
= nsPresContext::CSSPixelsToAppUnits(1);
334 if (aFontSizeType
== eFontSize_HTML
) {
342 smallestIndexFontSize
= CalcFontPointSize(indexMin
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
343 largestIndexFontSize
= CalcFontPointSize(indexMax
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
344 if (aFontSize
> (smallestIndexFontSize
- onePx
)) {
345 if (aFontSize
< largestIndexFontSize
) { // larger will be in HTML table
346 // find smallest index larger than current
347 for (index
= indexMin
; index
<= indexMax
; index
++) {
348 indexFontSize
= CalcFontPointSize(index
, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
349 if (indexFontSize
> aFontSize
)
352 // set up points beyond table for interpolation purposes
353 if (indexFontSize
== smallestIndexFontSize
) {
354 smallerIndexFontSize
= indexFontSize
- onePx
;
355 largerIndexFontSize
= CalcFontPointSize(index
+1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
356 } else if (indexFontSize
== largestIndexFontSize
) {
357 smallerIndexFontSize
= CalcFontPointSize(index
-1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
358 largerIndexFontSize
= NSToCoordRound(float(largestIndexFontSize
) * 1.5);
360 smallerIndexFontSize
= CalcFontPointSize(index
-1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
361 largerIndexFontSize
= CalcFontPointSize(index
+1, aBasePointSize
, aScalingFactor
, aPresContext
, aFontSizeType
);
363 // compute the relative position of the parent size between the two closest indexed sizes
364 relativePosition
= float(aFontSize
- smallerIndexFontSize
) / float(indexFontSize
- smallerIndexFontSize
);
365 // set the new size to have the same relative position between the next largest two indexed sizes
366 largerSize
= indexFontSize
+ NSToCoordRound(relativePosition
* (largerIndexFontSize
- indexFontSize
));
368 else { // larger than HTML table, increase by 50%
369 largerSize
= NSToCoordRound(float(aFontSize
) * 1.5);
372 else { // smaller than HTML table, increase by 1px
373 largerSize
= aFontSize
+ onePx
;
378 //------------------------------------------------------------------------------
380 //------------------------------------------------------------------------------
383 nsStyleUtil::ConstrainFontWeight(PRInt32 aWeight
)
385 aWeight
= ((aWeight
< 100) ? 100 : ((aWeight
> 900) ? 900 : aWeight
));
386 PRInt32 base
= ((aWeight
/ 100) * 100);
387 PRInt32 step
= (aWeight
% 100);
388 PRBool negativeStep
= PRBool(50 < step
);
392 maxStep
= (base
/ 100);
396 maxStep
= ((900 - base
) / 100);
398 if (maxStep
< step
) {
401 return (base
+ ((negativeStep
) ? -step
: step
));
404 // Compare two language strings
405 PRBool
nsStyleUtil::DashMatchCompare(const nsAString
& aAttributeValue
,
406 const nsAString
& aSelectorValue
,
407 const nsStringComparator
& aComparator
)
410 PRUint32 selectorLen
= aSelectorValue
.Length();
411 PRUint32 attributeLen
= aAttributeValue
.Length();
412 if (selectorLen
> attributeLen
) {
416 nsAString::const_iterator iter
;
417 if (selectorLen
!= attributeLen
&&
418 *aAttributeValue
.BeginReading(iter
).advance(selectorLen
) !=
420 // to match, the aAttributeValue must have a dash after the end of
421 // the aSelectorValue's text (unless the aSelectorValue and the
422 // aAttributeValue have the same text)
426 result
= StringBeginsWith(aAttributeValue
, aSelectorValue
, aComparator
);
432 void nsStyleUtil::AppendEscapedCSSString(const nsString
& aString
,
435 aReturn
.Append(PRUnichar('"'));
437 const nsString::char_type
* in
= aString
.get();
438 const nsString::char_type
* const end
= in
+ aString
.Length();
439 for (; in
!= end
; in
++)
443 // Escape all characters below 0x20 numerically.
446 This is the buffer into which snprintf should write. As the hex. value is,
447 for numbers below 0x20, max. 2 characters long, we don't need more than 5
448 characters ("\XX "+NUL).
451 nsTextFormatter::snprintf(buf
, NS_ARRAY_LENGTH(buf
), NS_LITERAL_STRING("\\%hX ").get(), *in
);
454 } else switch (*in
) {
455 // Special characters which should be escaped: Quotes and backslash
459 aReturn
.Append(PRUnichar('\\'));
460 // And now, after the eventual escaping character, the actual one.
462 aReturn
.Append(PRUnichar(*in
));
466 aReturn
.Append(PRUnichar('"'));
470 nsStyleUtil::AppendEscapedCSSIdent(const nsString
& aIdent
, nsAString
& aReturn
)
472 // The relevant parts of the CSS grammar are:
473 // ident [-]?{nmstart}{nmchar}*
474 // nmstart [_a-z]|{nonascii}|{escape}
475 // nmchar [_a-z0-9-]|{nonascii}|{escape}
476 // nonascii [^\0-\177]
477 // escape {unicode}|\\[^\n\r\f0-9a-f]
478 // unicode \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
479 // from http://www.w3.org/TR/CSS21/syndata.html#tokenization
481 const nsString::char_type
* in
= aIdent
.get();
482 const nsString::char_type
* const end
= in
+ aIdent
.Length();
484 // Deal with the leading dash separately so we don't need to
485 // unnecessarily escape digits.
486 if (in
!= end
&& *in
== '-') {
487 aReturn
.Append(PRUnichar('-'));
491 PRBool first
= PR_TRUE
;
492 for (; in
!= end
; ++in
, first
= PR_FALSE
)
494 if (*in
< 0x20 || (first
&& '0' <= *in
&& *in
<= '9'))
496 // Escape all characters below 0x20, and digits at the start
497 // (including after a dash), numerically. If we didn't escape
498 // digits numerically, they'd get interpreted as a numeric escape
499 // for the wrong character.
502 This is the buffer into which snprintf should write. As the hex.
503 value is, for numbers below 0x7F, max. 2 characters long, we
504 don't need more than 5 characters ("\XX "+NUL).
507 nsTextFormatter::snprintf(buf
, NS_ARRAY_LENGTH(buf
),
508 NS_LITERAL_STRING("\\%hX ").get(), *in
);
512 if (!((ch
== PRUnichar('_')) ||
513 (PRUnichar('A') <= ch
&& ch
<= PRUnichar('Z')) ||
514 (PRUnichar('a') <= ch
&& ch
<= PRUnichar('z')) ||
515 PRUnichar(0x80) <= ch
||
516 (!first
&& ch
== PRUnichar('-')) ||
517 (PRUnichar('0') <= ch
&& ch
<= PRUnichar('9')))) {
518 // Character needs to be escaped
519 aReturn
.Append(PRUnichar('\\'));
527 nsStyleUtil::AppendBitmaskCSSValue(nsCSSProperty aProperty
,
528 PRInt32 aMaskedValue
,
533 for (PRInt32 mask
= aFirstMask
; mask
<= aLastMask
; mask
<<= 1) {
534 if (mask
& aMaskedValue
) {
535 AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty
, mask
),
537 aMaskedValue
&= ~mask
;
538 if (aMaskedValue
) { // more left
539 aResult
.Append(PRUnichar(' '));
543 NS_ABORT_IF_FALSE(aMaskedValue
== 0, "unexpected bit remaining in bitfield");
547 nsStyleUtil::ColorComponentToFloat(PRUint8 aAlpha
)
549 // Alpha values are expressed as decimals, so we should convert
550 // back, using as few decimal places as possible for
552 // First try two decimal places:
553 float rounded
= NS_roundf(float(aAlpha
) * 100.0f
/ 255.0f
) / 100.0f
;
554 if (FloatToColorComponent(rounded
) != aAlpha
) {
555 // Use three decimal places.
556 rounded
= NS_roundf(float(aAlpha
) * 1000.0f
/ 255.0f
) / 1000.0f
;
562 nsStyleUtil::IsSignificantChild(nsIContent
* aChild
, PRBool aTextIsSignificant
,
563 PRBool aWhitespaceIsSignificant
)
565 NS_ASSERTION(!aWhitespaceIsSignificant
|| aTextIsSignificant
,
566 "Nonsensical arguments");
568 PRBool isText
= aChild
->IsNodeOfType(nsINode::eTEXT
);
570 if (!isText
&& !aChild
->IsNodeOfType(nsINode::eCOMMENT
) &&
571 !aChild
->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION
)) {
575 return aTextIsSignificant
&& isText
&& aChild
->TextLength() != 0 &&
576 (aWhitespaceIsSignificant
||
577 !aChild
->TextIsOnlyWhitespace());